Как я могу получить первое непустое значение группы в MySQL?

У меня есть следующая таблица:

id | item_id | name         | description        | locale
----------------------------------------------------------
1  | 1       |     (null)   |       (null)       | en-GB
2  | 1       | Foo          |       (null)       | en-US
3  | 1       | Bar          | Lorem ipsum dolor. | en-AU

Я пытаюсь вернуть такой результат ?:

item_id | name         | description
-------------------------------------------------------------------
1       | Foo          | Lorem ipsum dolor.

Мне сказали, что coalsece был подходящим вариантом, но, похоже, он не работает с группами. Хотя выстрел не выстрелил. Это должно дать хорошее представление о том, что я пытаюсь сделать.

select item_id, coalesce(name) as name, coalesce(description) as description from test
where item_id = 1
group by item_id
order by field(locale, 'en-GB', 'en-US', 'en-AU');

Другие попытки были сделаны с использованием first (), first_value (), min (), max () ... Я лучше выполняю это в прямом запросе MySQL, чем смешиваю его с логикой PHP.

1
tim 4 Сен 2016 в 04:55

4 ответа

Лучший ответ

Вы можете попробовать что-то вроде этого:

select 
    item_id,

    substring_index(
        coalesce(group_concat(name separator ','), ''), 
        ',', 
        1
    ) as first_name,

    substring_index(
        coalesce(group_concat(description separator ','), ''), 
        ',', 
        1
   ) as first_description

from test
group by item_id

См. Примечание внизу относительно order by.

Результат

item_id first_name  first_description
1       Foo         Lorem ipsum dolor.
2       

Объяснение

Используя функцию group_concat, мы объединяем все имена и описания в CSV для каждого item_id.

Результат может быть нулевым. Поэтому мы используем coalesce для предоставления пустой строки в качестве значения, если group_concat привел к нулю.

Затем мы разделяем запятыми и получаем первую часть информации.

Предостережение: ваше имя или описание могут содержать запятую. Вам нужно будет как-то учесть это, чего в запросе сейчас нет.

Живой пример или данные, которые можно попробовать, если кто-то захочет

http://rextester.com/RFI82565

create table test (
  id int,
  item_id int, 
  name varchar(100),
  description varchar(200),
  locale varchar(10)
);

insert into test values 
(1, 1, NULL, NULL, 'en-GB'),
(2, 1, 'Foo', NULL, 'en-US'),
(3, 1, 'Bar', 'Lorem ipsum dolor.', 'en-AU'),
(4, 2, NULL, NULL, 'en-GB');

Примечание

Пользователь Straberry мудро заметил, что в моем запросе отсутствует предложение order by, которое OP может счесть ценным.

Заказ можно сделать в предложении group_concat и после group by. Ниже приведен пример того, как можно сделать заказ на обоих уровнях. Заказ на уровне заказов group_concat гарантирует, что first_name соответствует указанному вами заказу. Упорядочивание после group by помогает упорядочить итоговый результат.

select 
    item_id,

    substring_index(
        coalesce(group_concat(name order by field(locale, 'en-GB', 'en-US', 'en-AU') separator ','), ''), 
        ',', 
        1
    ) as first_name,

    substring_index(
        coalesce(group_concat(description order by field(locale, 'en-GB', 'en-US', 'en-AU') separator ','), ''), 
        ',', 
        1
   ) as first_description

from test
group by item_id
order by field(locale, 'en-GB', 'en-US', 'en-AU');
3
zedfoxus 13 Сен 2016 в 13:47

Аналогично решению @zedfoxus, но с использованием решения Standard SQL, работающего в каждой СУБД:

select 
    item_id,

    coalesce(max(case when locale = 'en-GB' then name end)
            ,max(case when locale = 'en-US' then name end)
            ,max(case when locale = 'en-AU' then name end)) as first_name,

    coalesce(max(case when locale = 'en-GB' then description end)
            ,max(case when locale = 'en-US' then description end)
            ,max(case when locale = 'en-AU' then description end)) as first_description

from test
group by item_id
2
dnoeth 15 Сен 2016 в 21:07
select TT2.Id, TT2.Item_Id, TT2.MyName, TT2.MyDesc, TT2.Locale from #TempTest TT2 
where convert(Varchar(12),TT2.Id) + '#' + convert(Varchar(12),TT2.Item_Id) 
in 
(
select X = convert(Varchar(12),Min(TT1.Id)) + '#' + convert(Varchar(12),Min(TT1.Item_Id))
from #TempTest TT1
where TT1.MyName is not null
group by TT1.Locale
)
0
M T Head 16 Сен 2016 в 16:33

Я не уверен, но я думаю, что следующий запрос получит только поле, в котором нет нулевых значений

 SELECT item_id,name,description,locale FROM test WHERE (name != '' AND description != '')

Надеюсь, что это работает

-1
ocnet 13 Сен 2016 в 10:40