Как выбрать первый ряд для каждой группы в MySQL? - PullRequest
55 голосов
/ 29 апреля 2010

В C # это будет выглядеть так:

table
   .GroupBy(row => row.SomeColumn)
   .Select(group => group
       .OrderBy(row => row.AnotherColumn)
       .First()
   )

Linq-To-Sql переводит его в следующий код T-SQL:

SELECT [t3].[AnotherColumn], [t3].[SomeColumn]
FROM (
    SELECT [t0].[SomeColumn]
    FROM [Table] AS [t0]
    GROUP BY [t0].[SomeColumn]
    ) AS [t1]
OUTER APPLY (
    SELECT TOP (1) [t2].[AnotherColumn], [t2].[SomeColumn]
    FROM [Table] AS [t2]
    WHERE (([t1].[SomeColumn] IS NULL) AND ([t2].[SomeColumn] IS NULL))
      OR (([t1].[SomeColumn] IS NOT NULL) AND ([t2].[SomeColumn] IS NOT NULL)
        AND ([t1].[SomeColumn] = [t2].[SomeColumn]))
    ORDER BY [t2].[AnotherColumn]
    ) AS [t3]
ORDER BY [t3].[AnotherColumn]

Но это несовместимо с MySQL.

Ответы [ 11 ]

69 голосов
/ 29 апреля 2010

Я основал свой ответ только на заголовке вашего поста, так как я не знаю C # и не понял данный запрос. Но в MySQL я предлагаю вам попробовать подвыбрать. Сначала получите набор первичных ключей интересных столбцов, затем выберите данные из этих строк:

SELECT somecolumn, anothercolumn 
  FROM sometable 
 WHERE id IN (
               SELECT min(id) 
                 FROM sometable 
                GROUP BY somecolumn
             );
20 голосов
/ 29 апреля 2010

Когда я пишу

SELECT AnotherColumn
FROM Table
GROUP BY SomeColumn
;

Это работает. IIRC в других СУБД такое утверждение невозможно, поскольку на столбец, который не принадлежит ключу группировки, ссылаются без какого-либо объединения.

Эта «причуда» очень тесно связана с тем, что я хочу. Поэтому я использовал его, чтобы получить желаемый результат:

SELECT * FROM 
(
 SELECT * FROM `table`
 ORDER BY AnotherColumn
) t1
GROUP BY SomeColumn
;
15 голосов
/ 29 апреля 2010

Вот еще один способ, которым вы можете попробовать, для которого не нужно это поле ID.

select some_column, min(another_column)
  from i_have_a_table
 group by some_column

Тем не менее я согласен с lfagundes, что вы должны добавить какой-нибудь первичный ключ ..

Также помните, что, делая это, вы не можете (легко) получить другие значения в той же строке, что и результирующая пара some_colum, another_column! Для этого вам понадобится приложение lfagundes и ПК!

6 голосов
/ 29 апреля 2010

Вы должны использовать некоторую статистическую функцию, чтобы получить значение AnotherColumn, которое вы хотите. То есть, если вы хотите наименьшее значение AnotherColumn для каждого значения SomeColumn (численно или лексикографически), вы можете использовать:

SELECT SomeColumn, MIN(AnotherColumn)
FROM YourTable
GROUP BY SomeColumn

Некоторые, надеюсь, полезные ссылки:

http://dev.mysql.com/doc/refman/5.1/en/group-by-functions.html

http://www.oreillynet.com/databases/blog/2007/05/debunking_group_by_myths.html

4 голосов
/ 05 января 2018

С Документация по MySQL 5.7

MySQL 5.7.5 и выше реализует обнаружение функциональной зависимости. Если включен режим SQL ONLY_FULL_GROUP_BY (который по умолчанию), MySQL отклоняет запросы, для которых список выбора, условие HAVING или список ORDER BY ссылаются на неагрегированные столбцы, которые не названы в предложении GROUP BY и функционально не зависят от них. .

Это означает, что решение @Jader Dias не будет работать везде.

Вот решение, которое будет работать при включении ONLY_FULL_GROUP_BY:

SET @row := NULL;
SELECT
    SomeColumn,
    AnotherColumn
FROM (
    SELECT
        CASE @id <=> SomeColumn AND @row IS NOT NULL 
            WHEN TRUE THEN @row := @row+1 
            ELSE @row := 0 
        END AS rownum,
        @id := SomeColumn AS SomeColumn,
        AnotherColumn
    FROM
        SomeTable
    ORDER BY
        SomeColumn, -AnotherColumn DESC
) _values
WHERE rownum = 0
ORDER BY SomeColumn;
2 голосов
/ 19 апреля 2018

Я не видел следующее решение среди ответов, поэтому я подумал, что выложу его там.

Проблема состоит в том, чтобы выбрать строки, которые являются первыми строками при упорядочении по AnotherColumn во всех группах, сгруппированных по SomeColumn.

Следующее решение сделает это в MySQL. id должен быть уникальным столбцом, который не должен содержать значения, содержащие - (который я использую в качестве разделителя).

select t1.*
from mytable t1
inner join (
  select SUBSTRING_INDEX(
    GROUP_CONCAT(t3.id ORDER BY t3.AnotherColumn DESC SEPARATOR '-'),
    '-', 
    1
  ) as id
  from mytable t3
  group by t3.SomeColumn
) t2 on t2.id = t1.id


-- Where 
SUBSTRING_INDEX(GROUP_CONCAT(id order by AnotherColumn desc separator '-'), '-', 1)
-- can be seen as:
FIRST(id order by AnotherColumn desc)

-- For completeness sake:
SUBSTRING_INDEX(GROUP_CONCAT(id order by AnotherColumn desc separator '-'), '-', -1)
-- would then be seen as:
LAST(id order by AnotherColumn desc)

В трекере ошибок MySQL есть запрос функции для FIRST() и LAST(), но он был закрыт много лет назад.

1 голос
/ 01 ноября 2018
SELECT
    t1.*

FROM
    table_name AS t1

    LEFT JOIN table_name AS t2 ON (
        t2.group_by_column = t1.group_by_column
        -- group_by_column is the column you would use in the GROUP BY statement
        AND
        t2.order_by_column < t1.order_by_column
        -- order_by_column is column you would use in the ORDER BY statement
        -- usually is the autoincremented key column
    )

WHERE
    t2.group_by_column IS NULL;

С MySQL v8 + вы можете использовать оконные функции

1 голос
/ 18 июля 2018

Еще один способ сделать это (без первичного ключа) - использовать функции JSON:

select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") )
  from sometable group by somecolumn

или предварительно 5.7.22

select somecolumn, 
  json_unquote( 
    json_extract( 
      concat('["', group_concat(othercolumn separator '","') ,'"]') 
    ,"$[0]" ) 
  ) 
  from sometable group by somecolumn

Заказ (или фильтрацию) можно выполнить перед группировкой:

select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") ) 
  from (select * from sometable order by othercolumn) as t group by somecolumn

... или после группировки (конечно):

select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") ) as other 
  from sometable group by somecolumn order by other

По общему признанию, это довольно запутанно, и производительность, вероятно, не велика (не проверял это на больших данных, хорошо работает на моих ограниченных наборах данных).

0 голосов
/ 12 октября 2017

Как насчет этого:

SELECT SUBSTRING_INDEX(
      MIN(CONCAT(OrderColumn, '|', IFNULL(TargetColumn, ''))
    ), '|', -1) as TargetColumn
FROM table
GROUP BY GroupColumn
0 голосов
/ 08 сентября 2017

Почему бы не использовать ключевое слово MySQL LIMIT?

SELECT [t2].[AnotherColumn], [t2].[SomeColumn]
FROM [Table] AS [t2]
WHERE (([t1].[SomeColumn] IS NULL) AND ([t2].[SomeColumn] IS NULL))
  OR (([t1].[SomeColumn] IS NOT NULL) AND ([t2].[SomeColumn] IS NOT NULL)
    AND ([t1].[SomeColumn] = [t2].[SomeColumn]))
ORDER BY [t2].[AnotherColumn]
LIMIT 1
...