web-dev-qa-db-ja.com

複数行の最大値に基づいて単一行を選択する方法

重複の可能性:
SQL:グループごとの最大レコードを検索

私はそのように4つの列を持つテーブルを持っています:

name   major    minor  revision
p1     0        4      3
p1     1        0      0
p1     1        1      4
p2     1        1      1
p2     2        5      0
p3     3        4      4

これは基本的に、プログラムの各バージョンのレコードを含むcaテーブルです。すべてのプログラムとその最新バージョンを取得するために選択を実行して、結果が次のようになるようにします。

name   major    minor  revision
p1     1        1      4
p2     2        5      0
p3     3        4      4

名前でグループ化して各列の最大値を取得することはできません。これは、各列の最大数で終わるだけで、バージョンが最も高い特定の行ではないためです。どうすればこれを設定できますか?

15
Brian

私がSQLの問題を解決しようとする方法は、物事を段階的に進めることです。

  • 各製品の最大メジャーバージョンに対応する最大マイナーバージョンの最大リビジョンが必要です。

各製品の最大メジャー番号は次の式で与えられます。

SELECT Name, MAX(major) AS Major FROM CA GROUP BY Name;

したがって、各製品の最大メジャー番号に対応する最大マイナー番号は、次の式で与えられます。

SELECT CA.Name, CA.Major, MAX(CA.Minor) AS Minor
  FROM CA
  JOIN (SELECT Name, MAX(Major) AS Major
          FROM CA
         GROUP BY Name
       ) AS CB
    ON CA.Name = CB.Name AND CA.Major = CB.Major
 GROUP BY CA.Name, CA.Major;

したがって、最大リビジョン(各製品の最大メジャー番号に対応する最大マイナーバージョン番号)は、次の式で与えられます。

SELECT CA.Name, CA.Major, CA.Minor, MAX(CA.Revision) AS Revision
  FROM CA
  JOIN (SELECT CA.Name, CA.Major, MAX(CA.Minor) AS Minor
          FROM CA
          JOIN (SELECT Name, MAX(Major) AS Major
                  FROM CA
                 GROUP BY Name
               ) AS CB
            ON CA.Name = CB.Name AND CA.Major = CB.Major
         GROUP BY CA.Name, CA.Major
       ) AS CC
    ON CA.Name = CC.Name AND CA.Major = CC.Major AND CA.Minor = CC.Minor
 GROUP BY CA.Name, CA.Major, CA.Minor;

テスト済み-動作し、 Andomarquery と同じ答えを生成します。


パフォーマンス

大量のデータ(11616行のデータ)を作成し、Andomarのクエリのベンチマークタイミングを実行しました。ターゲットDBMSは、MacOS X10.7.2で実行されているIBMInformix Dynamic Server(IDS)バージョン11.70.FC2でした。 IDSは2番目のクエリの比較表記をサポートしていないため、Andomarの2つのクエリのうち最初のクエリを使用しました。私はデータをロードし、統計を更新し、私の後にAndomarを使用して、およびAndomarの後に私のものを使用してクエリを実行しました。また、IDSオプティマイザーによって報告された基本コストも記録しました。両方のクエリの結果データは同じでした(したがって、クエリは両方とも正確であるか、等しく不正確です)。

インデックスなしのテーブル:

Andomar's query                           Jonathan's query
Time: 22.074129                           Time: 0.085803
Estimated Cost: 2468070                   Estimated Cost: 22673
Estimated # of Rows Returned: 5808        Estimated # of Rows Returned: 132
Temporary Files Required For: Order By    Temporary Files Required For: Group By

(名前、メジャー、マイナー、リビジョン)に一意のインデックスがあるテーブル:

Andomar's query                           Jonathan's query
Time: 0.768309                            Time: 0.060380
Estimated Cost: 31754                     Estimated Cost: 2329
Estimated # of Rows Returned: 5808        Estimated # of Rows Returned: 139
                                          Temporary Files Required For: Group By

ご覧のとおり、インデックスはAndomarのクエリのパフォーマンスを劇的に向上させますが、それでもこのシステムでは私のクエリよりもコストがかかるようです。インデックスを使用すると、クエリの時間を25%節約できます。インデックスがある場合とない場合の、同等の量のデータに対するAndomarのクエリの2つのバージョンの同等の数値を確認したいと思います。 (私のテストデータは必要に応じて提供できます。132の製品がありました。質問にリストされている3つと129の新しい製品です。各新製品には(同じ)90のバージョンエントリがありました。)

不一致の理由は、Andomarのクエリのサブクエリが相関サブクエリであり、比較的コストのかかるプロセスであるためです(インデックスが欠落している場合は劇的にそうなります)。

9

not existsサブクエリを使用して、古いレコードを除外できます。

select  *
from    YourTable yt
where   not exists
        (
        select  *
        from    YourTable older
        where   yt.name = older.name and 
                (
                    yt.major < older.major or
                    yt.major = older.major and yt.minor < older.minor or
                    yt.major = older.major and yt.minor = older.minor and
                        yt.revision < older.revision
                )
        )

これはMySQLで次のように書くこともできます:

select  *
from    YourTable yt
where   not exists
        (
        select  *
        from    YourTable older
        where   yt.name = older.name and 
                  (yt.major,    yt.minor,    yt.revision) 
                < (older.major, older.major, older.revision)
        )
11
Andomar
SELECT cam.*
FROM 
      ( SELECT DISTINCT name
        FROM ca 
      ) AS cadistinct
  JOIN 
      ca AS cam
    ON ( cam.name, cam.major, cam.minor, cam.revision )
     = ( SELECT name, major, minor, revision
         FROM ca
         WHERE name = cadistinct.name
         ORDER BY major DESC
                , minor DESC
                , revision DESC
         LIMIT 1
       )

これはMySQL(現在のバージョン)で機能しますが、お勧めしません:

SELECT *
FROM 
    ( SELECT name, major, minor, revision
      FROM ca
      ORDER BY name
             , major DESC
             , minor DESC
             , revision DESC
    ) AS tmp
GROUP BY name
2
ypercubeᵀᴹ

Update3変数group_concat_max_lenの最小値は4であるため、使用できません。でも君ならできる:

select 
  name, 
  SUBSTRING_INDEX(group_concat(major order by major desc),',', 1) as major, 
  SUBSTRING_INDEX(group_concat(minor order by major desc, minor desc),',', 1)as minor, 
  SUBSTRING_INDEX(group_concat(revision order by major desc, minor desc, revision desc),',', 1) as revision
from your_table
group by name;

これはテスト済みです ここ いいえ、以前のバージョンでは間違った結果は得られず、連結された値の数にのみ問題がありました。

2
Florin Ghita

最高のバージョンは最高のリビジョンのものだと思っているのは私だけですか?

そう、

select a.name, a.major, a.minor, a.revision
from table a
where a.revision = (select max(b.revision) from table b where b.name = a.name)
1
aF.

これらの列に数値がある場合は、メジャー、マイナー、リビジョン値に対して一意で適切に順序付けられた、ある種の数式を思い付くことができます。例えば。数値が10未満の場合は、次のように文字列として追加して比較できます。

select name, major, minor, revision, 
       concat(major, minor, revision) as version
from versions

それらが100を超えない数値である場合は、次のようにすることができます。

select name, major, minor, revision, 
       (major * 10000 + minor * 100 + revision) as version
from versions

次のように、maxversionを名前でグループ化するだけでは不十分です。

select name, major, minor, revision 
from (
    select name, major, minor, revision, 
           (major * 10000 + minor * 100 + revision) as version
    from versions) v1
where version = (select max (major * 10000 + minor * 100 + revision) 
                 from versions v2 
                 where v1.name = v2.name)
1
SWeko

バージョン番号の一部ごとに最大3桁が許可されます。より多くの桁を使用したい場合は、メジャー乗算に2つのゼロを追加し、各桁に1つのゼロからマイナー乗算を追加します(明確であることを願っています)。

select  t.* 
from yourTable t
join (
    select name, max(major * 1000000 + minor * 1000  + revision) as ver
    from yourTable 
    group by name
) t1 on t1.ver = (t.major * 1000000 + t.minor * 1000  + t.revision)

結果:

name    major   minor   revision
p1      1       1       4
p2      2       5       0
p3      3       4       4
1
Michał Powaga