web-dev-qa-db-ja.com

MySQL-グループによって返される行を制御する

次のようなデータベーステーブルがあります。

id    version_id    field1    field2
1     1             texta      text1
1     2             textb      text2
2     1             textc      text3
2     2             textd      text4
2     3             texte      text5

うまくいかなかった場合は、行の多くのバージョンが含まれており、テキストデータが含まれています。

クエリを実行して、各IDの最大番号を持つバージョンを返します。 (つまり、上記の2番目と最後の行のみ)。

Version_id DESCで並べ替えながらgroupを使用してみましたが、グループ化後に並べ替えるように見えるため、これは機能しません。

誰でもアイデアはありますか?できないとは信じられない!

更新:

これはうまくいきますが、サブクエリを使用しています:

SELECT *
FROM (SELECT * FROM table ORDER BY version_id DESC) t1
GROUP BY t1.id
56
benlumley

列のグループごとの最大値の選択と呼ばれます。 mysqlのいくつかの異なるアプローチがあります。

私がそれをする方法は次のとおりです。

SELECT *
FROM (SELECT id, max(version_id) as version_id FROM table GROUP BY id) t1
INNER JOIN table t2 on t2.id=t1.id and t1.version_id=t2.version_id

これは比較的効率的ですが、mysqlはメモリ内にサブクエリ用の一時テーブルを作成します。このテーブルのインデックス(id、version_id)が既にあると思います。

この種の問題に対して多少なりともサブクエリを使用する必要があるのはSQLの欠陥です( semi-joins も別の例です)。

Mysqlではサブクエリは最適化されていませんが、メモリではなくディスクに書き込まれるほど大きくない限り、非相関サブクエリはそれほど悪くはありません。このクエリのintが2つしかないため、サブクエリはそれが発生するずっと前に数百万行になる可能性がありますが、最初のクエリのselect *サブクエリはこの問題にすぐに悩まされる可能性があります。

49
ʞɔıu

これでうまくいくと思いますが、それが最良か最速かはわかりません。

SELECT * FROM table 
WHERE (id, version_id) IN 
  (SELECT id, MAX(version_id) FROM table GROUP BY id)
3
Chris J
_SELECT id, version_id, field1, field2
FROM (
    SELECT @prev = id AS st, (@prev := id), m.*
    FROM (
           (SELECT @prev := NULL) p,
           (
            SELECT *
            FROM   mytable
            ORDER BY
                   id DESC, version_id DESC
           ) m
     ) m2
WHERE NOT IFNULL(st, FALSE);
_

サブクエリはありません。1つあれば、UNIQUE INDEX ON MYTABLE (id, version_id)をパスします(これが必要だと思います)

2
Quassnoi

このクエリは、次の方法でグループなしでジョブを実行します。

SELECT * FROM table AS t
LEFT JOIN table AS t2 
    ON t.id=t2.id 
    AND t.version_id < t2.version_id
WHERE t2.id IS NULL

一時テーブルは必要ありません。

0
Patrick Savalle

分析機能もいつでも使用できます。これにより、より多くの制御が可能になります

select tmp.* from ( select id,version_id,field1,field2, rank() over(partition by id order by version_id desc ) as rnk from table) tmp where tmp.rnk=1

データのタイプに応じてrank()関数で問題に直面した場合、row_number()またはdense_rank()から選択することもできます。

0
sumit kumar

これは擬似コードですが、このようなものはうまく機能するはずです

select *
from table
inner join
(
    select id , max(version_id) maxVersion
    from table 
) dvtbl ON id = dvtbl.id && versionid = dvtbl.maxVersion
0
Chris Meek

私は通常、サブクエリでこれを行います:

データテーブルからid、version_id、field1、field2を選択します。id=(datatableからidを選択します。id= dt.id version_id desc limit 1の順序で選択します)

0
mcassano