Получите лучшие строки на основе нескольких столбцов в MySQL

У меня есть таблица, которая выглядит так:

id | title | value | language
---+-------+-------+---------
1  | a     | 1800  |    NULL
2  | a     | 1900  |    NULL
3  | b     | 1700  |    NULL
4  | b     | 1750  |    NULL 
5  | b     | 1790  |    1
6  | c     | 1892  |    NULL
7  | c     | 1900  |    1
8  | c     | 1910  |    2
9  | d     | 3020  |    NULL

Хотелось бы получить следующий результат:

id | title | value | language
---+-------+-------+---------
2  | a     | 1900  |    NULL
4  | b     | 1750  |    NULL 
5  | b     | 1790  |    1
6  | c     | 1892  |    NULL
7  | c     | 1900  |    1
8  | c     | 1910  |    2
9  | d     | 3020  |    NULL

Суть в том, чтобы выбрать наибольшее значение в столбце значений каждого языка каждого заголовка, причем наибольшее значение будет самым последним. Во-вторых, хотелось бы избежать агрегатных функций, таких как MAX, DISTINCT или GROUP-BY, поскольку я создаю представление MySQL с использованием алгоритма MERGE и не хочу в конечном итоге создавать временную таблицу (см. нижний раздел https://dev.mysql.com/doc/refman/5.6/en/view-algorithms.html).

Пока это работает, но возвращает только самую большую строку для заголовка:

SELECT t1.title
FROM table t1
LEFT OUTER JOIN table t2
  ON t1.title = t2.title
        AND t1.value < t2.value
WHERE t2.title IS NULL

Как я могу создать тот, который учитывает язык, как результаты выше? Спасибо.

Почему вы строите ВИД?

Strawberry 30.05.2019 12:43

@Strawberry Чтобы упростить поиск.

John Miller 30.05.2019 12:44
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
0
2
69
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Этот ответ предполагает, что вы используете MySQL 8+, в котором ваш запрос становится очень простым. MySQL 8 и более поздние версии поддерживают аналитические функции, которые были добавлены с целью решения подобных проблем.

Мы можем попробовать использовать ROW_NUMBER здесь:

WITH cte AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY title, language ORDER BY value DESC) rn
    FROM yourTable
)

SELECT id, title, value, language
FROM cte
WHERE rn = 1;

Демо

Есть способ справиться с этим с более ранними версиями MySQL, но он требует пользовательских переменных и имеет тенденцию быть очень уродливым. Так что, возможно, подумайте об обновлении, если вы ожидаете много запросов, подобных этому.

Это должно дать вам то, что вы хотите.

SELECT t1.title, t1.value, t1.language
FROM [Table] t1
LEFT OUTER JOIN [Table] t2 ON 
     t1.title = t2.title AND
     (IFNULL(t1.language, '') = IFNULL(t2.language, ''))
WHERE 
     t1.value > t2.value;

Вы не будете возражать, если я поправлю ваш запрос, чтобы я мог поднять его?

John Miller 30.05.2019 13:36

@ДжонМиллер, конечно!

nunoq 30.05.2019 13:52

@JohnMiller На самом деле, я считаю, что этот мой ответ вернет повторяющиеся строки, если у вас есть, например, больше записей в заголовке «a» с языком «NULL». Не знаю, подходит ли вам этот сценарий, но имейте в виду.

nunoq 30.05.2019 15:19
Ответ принят как подходящий

Вы можете сделать это с помощью NOT EXISTS:

select t.*
from tablename t
where not exists (
  select 1 from tablename
  where 
    title = t.title and 
    coalesce(language, 0) = coalesce(t.language, 0) and 
    value > t.value
)

Смотрите демо.
Результаты:

| id  | title | value | language |
| --- | ----- | ----- | -------- |
| 2   | a     | 1900  | NULL     |
| 4   | b     | 1750  | NULL     |
| 5   | b     | 1790  | 1        |
| 6   | c     | 1892  | NULL     |
| 7   | c     | 1900  | 1        |
| 8   | c     | 1910  | 2        |
| 9   | d     | 3020  | NULL     |

Еще одна вещь: я предполагаю, что 0 никогда не будет допустимым значением в столбце language. Если это не так, измените 0 на -1 или что-то, чего никогда не было бы в этом столбце.

forpas 30.05.2019 13:46

Спасибо. Поле является внешним ключом к таблице, содержащей «дополнительные» языки. Таким образом, NULL заменяет язык по умолчанию. Но еще раз спасибо за подсказку.

John Miller 30.05.2019 13:53

Другие вопросы по теме