13

問題

このテーブルがあるとしますtabフィドルが利用可能です)。

| g | a | b |     v |
---------------------
| 1 | 3 | 5 |   foo |
| 1 | 4 | 7 |   bar |
| 1 | 2 | 9 |   baz |
| 2 | 1 | 1 |   dog |
| 2 | 5 | 2 |   cat |
| 2 | 5 | 3 | horse |
| 2 | 3 | 8 |   pig |

で行をグループ化してgおり、グループごとに column から 1 つの値が必要ですv。ただし、値は必要ありませんa、 maximal を含む行と、それらすべてからmaximal を含む行の値が必要ですb。言い換えれば、私の結果は

| 1 |   bar |
| 2 | horse |

現在のソリューション

これを達成するためのクエリを知っています:

SELECT grps.g,
(SELECT v FROM tab
 WHERE g = grps.g
 ORDER BY a DESC, b DESC
 LIMIT 1) AS r
FROM (SELECT DISTINCT g FROM tab) grps

質問

しかし、私はこのクエリはかなり醜いと考えています。主に依存サブクエリを使用しているため、実際のパフォーマンスキラーのように感じます. したがって、この問題に対するより簡単な解決策があるかどうか疑問に思っています。

予想される答え

この質問に対する最も可能性の高い答えは、このための機能を提供する MySQL (または MariaDB) のアドオンまたはパッチのようなものです。しかし、他の有用なインスピレーションも歓迎します。依存サブクエリなしで機能するものはすべて、回答として適格です。

catあなたの解決策が単一の順序付け列でのみ機能する場合、つまりとを区別できなかった場合はhorse、その回答を自由に提案してください。大多数のユース ケースでも役立つと思います。たとえば100*a+b、単一の式のみを使用しながら、両方の列で上記のデータを並べ替える可能性が高い方法です。

私はかなりハックな解決策をいくつか考えていて、しばらくしたら追加するかもしれませんが、最初にいくつかの素晴らしい新しい解決策が最初に流れ込むかどうかを確認します.


ベンチマーク結果

さまざまな回答を見て比較するのはかなり難しいため、いくつかのベンチマークを実行しました。これは、MySQL 5.1 を使用して、自分のデスクトップで実行しました。数値は他のシステムと比較することはできません。相互に比較するだけです。アプリケーションにとってパフォーマンスが重要な場合は、実際のデータを使用して独自のテストを行う必要があります。新しい回答が得られたら、それらをスクリプトに追加して、すべてのテストを再実行します。

したがって、従属サブクエリを使用しても、これまでのところ私自身のソリューションはそれほど悪くはないようです。驚くべきことに、従属サブクエリも使用する acatt によるソリューションのパフォーマンスははるかに悪いものです。おそらく、MySQL オプティマイザが対処できないものです。RichardTheKiwi が提案したソリューションは、全体的なパフォーマンスも優れているようです。他の 2 つのソリューションは、データの構造に大きく依存します。多くのグループと小さなグループでは、xdazz のアプローチは他のすべてのアプローチよりも優れていますが、Dems によるソリューションは少数の大規模なグループでは最高のパフォーマンスを発揮します (それでも例外的に良くはありません)。

4

4 に答える 4

5

この方法では、サブクエリは使用されません。

SELECT t1.g, t1.v
FROM tab t1
LEFT JOIN tab t2 ON t1.g = t2.g AND (t1.a < t2.a OR (t1.a = t2.a AND t1.b < t2.b))
WHERE t2.g IS NULL

説明:

LEFT JOINは、t1.aが最大値のときに、それより大きい値のs2.aがなく、s2行の値がNULLになることに基づいて機能します。

于 2012-10-04T11:52:14.127 に答える
5
SELECT g, a, b, v
  FROM (
            SELECT *, 
                   @rn := IF(g = @g, @rn + 1, 1) rn, 
                   @g := g
              FROM (select @g := null, @rn := 0) x, 
                   tab
          ORDER BY g, a desc, b desc, v
       ) X
 WHERE rn = 1;

シングルパス。他のすべてのソリューションは、私には O(n^2) に見えます。

于 2012-10-04T13:19:32.193 に答える
1

多くの RDBMS には、この問題に特に適した構造があります。MySQLはその 1 つではありません

これは、3 つの基本的なアプローチにつながります。

  • EXISTS 句で EXISTS および相関サブクエリを使用して、各レコードをチェックして、必要なレコードであるかどうかを確認します。 (@acatt の回答ですが、MySQL が常にこれをうまく最適化するとは限らないことを理解しています。MySQL が(g,a,b)これをうまく行わないと仮定する前に、複合インデックスがオンになっていることを確認してください。)

  • 同じチェックを完全に埋めるために半デカルト積を実行します。結合しないレコードはすべて対象レコードです。各グループ ('g') が大きい場合、パフォーマンスが急速に低下する可能性があります( の一意の値ごとに 10 個のレコードがある場合g、最大 50 個のレコードが生成され、49 個が破棄されます。グループ サイズが 100 の場合、最大 5000 個のレコードが生成され、破棄 4999)ですが、少人数のグループには最適です。(@xdazzの回答。)

  • または、複数のサブクエリを使用して MAX(a) を決定し、次に MAX(b) を決定します...

複数の順次サブクエリ...

SELECT
  yourTable.*
FROM
  (SELECT g,    MAX(a) AS a FROM yourTable GROUP BY g   ) AS searchA
INNER JOIN
  (SELECT g, a, MAX(b) AS b FROM yourTable GROUP BY g, a) AS searchB
    ON  searchA.g = searchB.g
    AND searchA.a = searchB.a
INNER JOIN
  yourTable
    ON  yourTable.g = searchB.g
    AND yourTable.a = searchB.a
    AND yourTable.b = searchB.b

MySQL が 2 番目のサブクエリを最適化する方法に応じて、これは他のオプションよりもパフォーマンスが高い場合とそうでない場合があります。ただし、これは、特定のタスクのコードの中で最も長い(そして保守性が最も低い可能性がある)コードです。

3 つの検索フィールドすべてに複合インデックスがあると仮定すると(g, a, b)g. しかし、それはテストされるべきです。

の小さなグループサイズのg場合、@xdazzの回答を使用します。

編集

強引なアプローチもあります。

  • 同じテーブルを作成しますが、ID として AUTO_INCREMENT カラムを使用します。
  • g、a、b の順にテーブルをこのクローンに挿入します。
  • ID は で見つけることができますSELECT g, MAX(id)
  • この結果を使用して、v必要な値を検索できます。

これが最善の方法である可能性は低いです。そうである場合、事実上、この種の問題に対処する MySQL のオプティマイザーの能力を非難することになります。

とはいえ、どのエンジンにも弱点があります。したがって、個人的には、RDBMS がどのように動作しているかを理解し、選択できるようになるまで、すべてを試してみます:)

編集

を使用した例ROW_NUMBER()(Oracle、SQL Server、PostGreSQL など)

SELECT
  *
FROM
(
  SELECT
    ROW_NUMBER() OVER (PARTITION BY g ORDER BY a DESC, b DESC) AS sequence_id,
    *
  FROM
    yourTable
)
  AS data
WHERE
  sequence_id = 1
于 2012-10-04T12:35:37.303 に答える
0

これは、相関クエリを使用して解決できます。

SELECT g, v
FROM tab t
WHERE NOT EXISTS (
    SELECT 1
    FROM tab
    WHERE g = t.g
        AND a > t.a
        OR (a = t.a AND b > t.b)
    )
于 2012-10-04T12:01:42.760 に答える