web-dev-qa-db-ja.com

MySQLの各グループの最初の行を選択する方法は?

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とは互換性がありません。

55
Jader Dias

書くとき

SELECT AnotherColumn
FROM Table
GROUP BY SomeColumn
;

できます。グループ化キーに属さない列は、なんらかの集計なしで参照されているため、他のRDBMSのこのようなステートメントのIIRCは不可能です。

この「癖」は、私が望むものに非常に近い振る舞いをします。だから私はそれを使って、私が望んでいた結果を得ました:

SELECT * FROM 
(
 SELECT * FROM `table`
 ORDER BY AnotherColumn
) t1
GROUP BY SomeColumn
;
20
Jader Dias

C#がわからず、指定されたクエリを理解できなかったため、回答は投稿のタイトルのみに基づいていました。しかし、MySQLでは、サブセレクトを試すことをお勧めします。最初に興味深い列の主キーのセットを取得してから、それらの行からデータを選択します。

SELECT somecolumn, anothercolumn 
  FROM sometable 
 WHERE id IN (
               SELECT min(id) 
                 FROM sometable 
                GROUP BY somecolumn
             );
69
lfagundes

もう1つ試すことができますが、そのIDフィールドは必要ありません。

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

それでも、主キーを追加する必要があるというlfagundesに同意します。

また、これを行うと、他の値が結果のsome_colum、another_columnのペアと同じ行になることは(簡単に)できないことに注意してください!そのためにはlfagundesのアプローチとPKが必要です!

15
lexu

何らかの集計関数を使用して、必要なAnotherColumnの値を取得する必要があります。つまり、SomeColumnの各値に対してAnotherColumnの最小値が必要な場合(数値または辞書順)、次を使用できます。

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

6
David M

MySQL 5.7ドキュメント から

MySQL 5.7.5以降では、機能依存の検出が実装されています。 ONLY_FULL_GROUP_BY SQLモードが有効になっている場合(デフォルト)、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;
4
Nicolai

私は答えの中に次の解決策を見たことがないので、それをそこに置くと思った。

問題は、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()機能要求 がありますが、何年も前に閉じられました。

2
Lars Nyström

さらに別の方法(主キーなし)では、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

確かに、かなり複雑であり、パフォーマンスはおそらく大きくありません(大きなデータでテストしなかったため、私の限られたデータセットでうまく機能します)。

1
Iikka
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

さらに別の方法

ビューで機能するグループから最大を選択します

SELECT * FROM action a 
WHERE NOT EXISTS (
   SELECT 1 FROM action a2 
   WHERE a2.user_id = a.user_id 
   AND a2.action_date > a.action_date 
   AND a2.action_type = a.action_type
)
AND a.action_type = "CF"
0
Timo Huovinen

これはどう:

SELECT SUBSTRING_INDEX(
      MIN(CONCAT(OrderColumn, '|', IFNULL(TargetColumn, ''))
    ), '|', -1) as TargetColumn
FROM table
GROUP BY GroupColumn
0
Yura Fedoriv