各GROUP BYグループの最初の行を選択しますか?


1326

タイトルが示すように、でグループ化された各行セットの最初の行を選択したいと思いますGROUP BY

具体的には、purchases次のようなテーブルがある場合:

SELECT * FROM purchases;

私の出力:

id | お客様| 合計
--- + ---------- + ------
 1 | ジョー| 5
 2 | サリー| 3
 3 | ジョー| 2
 4 | サリー| 1

それぞれが行っidた最大の購入(total)について問い合わせたいのcustomerですが。このようなもの:

SELECT FIRST(id), customer, FIRST(total)
FROM  purchases
GROUP BY customer
ORDER BY total DESC;

期待される出力:

FIRST(id)| お客様| 最初(合計)
---------- + ---------- + -------------
        1 | ジョー| 5
        2 | サリー| 3

最大の1つだけを探しているので、クエリを実行してみませんMAX(total)か?
phil294

4
@ phil294でmax(total)をクエリしても、その合計は、その合計が発生した行の「id」値に関連付けられません。
gwideman

回答:


1117

Oracle 9.2+(最初に述べた8i +ではない)、SQL Server 2005 +、PostgreSQL 8.4 +、DB2、Firebird 3.0 +、Teradata、Sybase、Vertica:

WITH summary AS (
    SELECT p.id, 
           p.customer, 
           p.total, 
           ROW_NUMBER() OVER(PARTITION BY p.customer 
                                 ORDER BY p.total DESC) AS rk
      FROM PURCHASES p)
SELECT s.*
  FROM summary s
 WHERE s.rk = 1

すべてのデータベースでサポートされています:

しかし、あなたは関係を壊すためにロジックを追加する必要があります:

  SELECT MIN(x.id),  -- change to MAX if you want the highest
         x.customer, 
         x.total
    FROM PURCHASES x
    JOIN (SELECT p.customer,
                 MAX(total) AS max_total
            FROM PURCHASES p
        GROUP BY p.customer) y ON y.customer = x.customer
                              AND y.max_total = x.total
GROUP BY x.customer, x.total

2
Informix 12.xはウィンドウ関数もサポートしています(ただし、CTEは派生テーブルに変換する必要があります)。また、Firebird 3.0はウィンドウ関数もサポートします
a_horse_with_no_name

37
ROW_NUMBER() OVER(PARTITION BY [...])他のいくつかの最適化とともに、クエリを30秒から数ミリ秒に短縮するのに役立ちました。ありがとう!(PostgreSQL 9.2)
サム

8
total1人の顧客にとって等しく最高の購入が複数ある場合、最初のクエリは任意の勝者を返します(実装の詳細に応じてid、実行ごとに変わる可能性があります!)。通常(常にではありません)、「最小の1」のような追加の基準によって定義された顧客ごとに1行が必要ですid。修正するには、のリストに追加idORDER BYますrow_number()。次に、2番目のクエリと同じ結果が得られますが、この場合は非常に非効率的です。また、追加の列ごとに別のサブクエリが必要になります。
Erwin Brandstetter 2014年

2
GoogleのBigQueryは、最初のクエリのROW_NUMBER()コマンドもサポートしています。私たちの魅力のように働きました
Praxiteles

2
window関数を含む最初のバージョンは、SQLiteバージョン3.25.0以降で機能することに注意してください。 windowfunctions.html#history
brianz

1150

ではPostgreSQLのこれは通常、簡単で高速(以下、よりパフォーマンスの最適化):

SELECT DISTINCT ON (customer)
       id, customer, total
FROM   purchases
ORDER  BY customer, total DESC, id;

または、出力列の序数で(それほど明確でない場合は)短くします。

SELECT DISTINCT ON (2)
       id, customer, total
FROM   purchases
ORDER  BY 2, 3 DESC, 1;

totalNULLの可能性がある場合(どちらの方法でも害はありませんが、既存のインデックス一致させる必要があります):

...
ORDER  BY customer, total DESC NULLS LAST, id;

主なポイント

  • DISTINCT ON標準のPostgreSQL拡張機能です(リストDISTINCT全体でのみSELECT定義されています)。

  • DISTINCT ON句に任意の数の式をリストします。結合された行の値は重複を定義します。マニュアル:

    明らかに、2つの行が少なくとも1つの列の値が異なる場合、それらは異なると見なされます。この比較では、ヌル値は等しいと見なされます。

    大胆な強調鉱山。

  • DISTINCT ONと組み合わせることができますORDER BY。の先頭の式はORDER BYの式のセットに含まれている必要がありますが、DISTINCT ONそれらの間の順序を自由に再配置できます。例。あなたは追加することができます追加式をしORDER BYて、ピアの各グループから特定の行を選択ます。または、マニュアルにあるように

    DISTINCT ON式(複数可)、左端一致しなければならないORDER BY 表現(複数可)。ORDER BY句は、通常、それぞれの中の行の所望の優先順位を決定する追加の式(S)含有するであろうDISTINCT ON基。

    idは最後の項目として追加しました:
    id各グループから最小の行を選択して、最大の行を共有しtotalます」。

    グループごとの最初のクエリを決定するソート順と一致しない方法で結果を並べ替えるには、上のクエリを別のの外部クエリにネストできますORDER BY例。

  • totalNULLの可能性がある場合は、おそらく最大の非NULL値を持つ行が必要です。追加NULLS LASTデモンストレーションのようにします。見る:

  • SELECTリストはの式によって制約されていませんDISTINCT ONか、ORDER BYどのような方法で。(上記の単純なケースでは必要ありません):

    • に式含める必要はありませんDISTINCT ONORDER BY

    • に他の式を含めることができますSELECTリスト。これは、はるかに複雑なクエリをサブクエリおよび集約/ウィンドウ関数に置き換えるための手段です。

  • 私はPostgresバージョン8.3〜12でテストしましたが、この機能は少なくともバージョン7.1以降に存在しているため、基本的には常にあります。

索引

完璧上記のクエリのためのインデックスは次のようになり、マルチカラムインデックスマッチングシーケンスで、一致するソート順ですべての3つの列にまたがります。

CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);

専門的すぎるかもしれません。ただし、特定のクエリの読み取りパフォーマンスが重要な場合に使用します。あなたが持っている場合はDESC NULLS LAST、クエリでは、そのソート順が一致するように、インデックスに同じを使用して、インデックスが適用されます。

有効性/パフォーマンスの最適化

クエリごとに調整されたインデックスを作成する前に、コストとメリットを比較検討します。上記のインデックスの可能性は、データの分布に大きく依存します

インデックスは、事前にソートされたデータを配信するために使用されます。Postgres 9.2以降では、インデックスが基礎となるテーブルよりも小さい場合、クエリはインデックスのみのスキャンからも恩恵を受けることができます。ただし、インデックス全体をスキャンする必要があります。

基準

ここには、今では時代遅れの簡単なベンチマークがありました。この別の回答では、詳細なベンチマークに置き換えました。


28
これは、ほとんどのデータベースサイズに最適な答えですが、100万行に近づくDISTINCT ONと、非常に遅くなることを指摘しておきます。実装は常にテーブル全体をソートし、重複をスキャンします(必要な複数列インデックスを作成した場合でも)すべてのインデックスを無視します。可能な解決策については、explainextended.com / 2009/05/03 / postgresql-optimizing-distinctを参照てください。
Meekohi 2014年

14
序数を使用して「コードを短くする」ことはひどい考えです。読みやすくするために列名を残しておいてはどうでしょうか?
KOTJMF、2015

13
@KOTJMF:個人的な好みで行くことをお勧めします。私は教育するための両方のオプションを示します。構文の省略表現は、SELECTリスト内の長い式に役立ちます。
Erwin Brandstetter、2015年

1
@jangorecki:元のベンチマークは2011年のもので、セットアップはもうありません。とにかく、pg 9.4とpg 9.5でテストを実行するときが来ました。追加された回答で詳細を参照してください。。以下のインストール結果のコメントを追加できますか?
Erwin Brandstetter 2016年

2
@PirateApp:私の頭の上からではありません。ピアのグループごとDISTINCT ON1行を取得する場合にのみ適しています。
Erwin Brandstetter 2018年

134

基準

Postgresので最も興味深い候補者のテスト9.49.5の途中で現実的なテーブルと200K行purchasesおよび10K明確なcustomer_id顧客あたり平均20行)。

Postgres 9.5の場合、86446人の異なる顧客を対象に2番目のテストを実行しました。以下を参照してください(顧客ごとの平均2.3行)。

セットアップ

メインテーブル

CREATE TABLE purchases (
  id          serial
, customer_id int  -- REFERENCES customer
, total       int  -- could be amount of money in Cent
, some_column text -- to make the row bigger, more realistic
);

serial(以下に追加されたPK制約)と整数を使用しcustomer_idます。これは、より一般的な設定だからです。また追加some_column通常より多くの列を補うためにれます。

ダミーデータ、PK、インデックス-一般的なテーブルにもいくつかの無効なタプルがあります。

INSERT INTO purchases (customer_id, total, some_column)    -- insert 200k rows
SELECT (random() * 10000)::int             AS customer_id  -- 10k customers
     , (random() * random() * 100000)::int AS total     
     , 'note: ' || repeat('x', (random()^2 * random() * random() * 500)::int)
FROM   generate_series(1,200000) g;

ALTER TABLE purchases ADD CONSTRAINT purchases_id_pkey PRIMARY KEY (id);

DELETE FROM purchases WHERE random() > 0.9; -- some dead rows

INSERT INTO purchases (customer_id, total, some_column)
SELECT (random() * 10000)::int             AS customer_id  -- 10k customers
     , (random() * random() * 100000)::int AS total     
     , 'note: ' || repeat('x', (random()^2 * random() * random() * 500)::int)
FROM   generate_series(1,20000) g;  -- add 20k to make it ~ 200k

CREATE INDEX purchases_3c_idx ON purchases (customer_id, total DESC, id);

VACUUM ANALYZE purchases;

customer テーブル-上位クエリ用

CREATE TABLE customer AS
SELECT customer_id, 'customer_' || customer_id AS customer
FROM   purchases
GROUP  BY 1
ORDER  BY 1;

ALTER TABLE customer ADD CONSTRAINT customer_customer_id_pkey PRIMARY KEY (customer_id);

VACUUM ANALYZE customer;

私の中で第二のテスト 9.5のための私は、同じ設定を使用しますが、でrandom() * 100000生成するcustomer_idあたりわずか数行を取得しますcustomer_id

テーブルのオブジェクトサイズ purchases

このクエリで生成されます

               what                | bytes/ct | bytes_pretty | bytes_per_row
-----------------------------------+----------+--------------+---------------
 core_relation_size                | 20496384 | 20 MB        |           102
 visibility_map                    |        0 | 0 bytes      |             0
 free_space_map                    |    24576 | 24 kB        |             0
 table_size_incl_toast             | 20529152 | 20 MB        |           102
 indexes_size                      | 10977280 | 10 MB        |            54
 total_size_incl_toast_and_indexes | 31506432 | 30 MB        |           157
 live_rows_in_text_representation  | 13729802 | 13 MB        |            68
 ------------------------------    |          |              |
 row_count                         |   200045 |              |
 live_tuples                       |   200045 |              |
 dead_tuples                       |    19955 |              |

クエリ

1. row_number()CTEで(他の回答を参照

WITH cte AS (
   SELECT id, customer_id, total
        , row_number() OVER(PARTITION BY customer_id ORDER BY total DESC) AS rn
   FROM   purchases
   )
SELECT id, customer_id, total
FROM   cte
WHERE  rn = 1;

2. row_number()サブクエリで(私の最適化)

SELECT id, customer_id, total
FROM   (
   SELECT id, customer_id, total
        , row_number() OVER(PARTITION BY customer_id ORDER BY total DESC) AS rn
   FROM   purchases
   ) sub
WHERE  rn = 1;

3. DISTINCT ON他の回答を参照

SELECT DISTINCT ON (customer_id)
       id, customer_id, total
FROM   purchases
ORDER  BY customer_id, total DESC, id;

4. LATERALサブクエリを使用したrCTE (ここを参照

WITH RECURSIVE cte AS (
   (  -- parentheses required
   SELECT id, customer_id, total
   FROM   purchases
   ORDER  BY customer_id, total DESC
   LIMIT  1
   )
   UNION ALL
   SELECT u.*
   FROM   cte c
   ,      LATERAL (
      SELECT id, customer_id, total
      FROM   purchases
      WHERE  customer_id > c.customer_id  -- lateral reference
      ORDER  BY customer_id, total DESC
      LIMIT  1
      ) u
   )
SELECT id, customer_id, total
FROM   cte
ORDER  BY customer_id;

5.付きのcustomerテーブルLATERALここを参照

SELECT l.*
FROM   customer c
,      LATERAL (
   SELECT id, customer_id, total
   FROM   purchases
   WHERE  customer_id = c.customer_id  -- lateral reference
   ORDER  BY total DESC
   LIMIT  1
   ) l;

6. array_agg()ORDER BY他の回答を参照

SELECT (array_agg(id ORDER BY total DESC))[1] AS id
     , customer_id
     , max(total) AS total
FROM   purchases
GROUP  BY customer_id;

結果

上記のクエリの実行時間EXPLAIN ANALYZE(およびすべてのオプションをオフ)、最高5回の実行

すべてのクエリで(他の手順の中で)インデックスのみのスキャンが使用されましたpurchases2_3c_idx。インデックスのサイズが小さいだけのものもあれば、より効果的なものもあります。

A. Postgres 9.4、20万行、最大20 customer_id

1. 273.274 ms  
2. 194.572 ms  
3. 111.067 ms  
4.  92.922 ms  
5.  37.679 ms  -- winner
6. 189.495 ms

B. Postgres 9.5と同じ

1. 288.006 ms
2. 223.032 ms  
3. 107.074 ms  
4.  78.032 ms  
5.  33.944 ms  -- winner
6. 211.540 ms  

C. Bと同じですが、1行あたり約2.3行 customer_id

1. 381.573 ms
2. 311.976 ms
3. 124.074 ms  -- winner
4. 710.631 ms
5. 311.976 ms
6. 421.679 ms

関連するベンチマーク

Postgres 11.5(2019年9月現在)での1,000万行と60,000の一意の「顧客」による「ogr」テストによる新しいものを次に示します。結果は、これまでに見てきたことと一致しています。

2011年の元の(古い)ベンチマーク

私は、PostgreSQL 9.1を使用して、65579行の実際のテーブルと、関連する3つの列それぞれに単一列のbtreeインデックスで3つのテストを実行し、5回の実行で最高の実行時間を費やしました。@OMGPoniesの最初のクエリ()を上記のソリューション()
と比較します。ADISTINCT ONB

  1. テーブル全体を選択すると、この場合は5958行になります。

    A: 567.218 ms
    B: 386.673 ms
  2. WHERE customer BETWEEN x AND y1000行になる条件を使用します。

    A: 249.136 ms
    B:  55.111 ms
  3. で単一の顧客を選択しWHERE customer = xます。

    A:   0.143 ms
    B:   0.072 ms

同じテストが他の回答で説明されているインデックスで繰り返されました

CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);

1A: 277.953 ms  
1B: 193.547 ms

2A: 249.796 ms -- special index not used  
2B:  28.679 ms

3A:   0.120 ms  
3B:   0.048 ms

5
素晴らしいベンチマークをありがとう。合計ではなくタイムスタンプがあるイベントデータのクエリを実行すると、新しいBRINインデックスのメリットが得られるかと思いました。これにより、一時的なクエリの速度が向上する可能性があります。
jangorecki 2016年

3
@jangorecki:物理的にソートされたデータを含む巨大なテーブルは、BRINインデックスから利益を得ることができます。
Erwin Brandstetter 2016年

@ErwinBrandstetter 2. row_number()およびの5. customer table with LATERAL例では、IDが最小になることを保証するものは何ですか?
Artem Novikov 2016年

@ArtemNovikov:何も。目的はcustomer_id 、が最も高い行ごとに取得することですtotalid選択した行のがでも最小になるのは、質問のテストデータでの誤解を招く偶然customer_idです。
Erwin Brandstetter 2016年

1
@ArtemNovikov:インデックスのみのスキャンを許可します。
Erwin Brandstetter 2016年

55

これは一般的です 問題はすでに十分にテストされ、高度に最適化されたソリューションを持っています。個人的には、Bill Karwinによる左結合ソリューション他の多くのソリューションを含む元の投稿)を好みます

この一般的な問題に対する一連の解決策は、驚くべきことに、最も公式なソースの1つであるMySQLマニュアルにあります一般的なクエリの例::特定の列のグループごとの最大値を保持する行を参照してください。


22
MySQLマニュアルは、Postgres / SQLite(SQLは言うまでもない)の質問に対する「公式」な方法ですか また、明確にするために、DISTINCT ONバージョンははるかに短く、単純で、一般的にPostgresでは、での自己LEFT JOIN結合または半反結合の代替よりもパフォーマンスが優れていNOT EXISTSます。また、「十分にテスト」されています。
Erwin Brandstetter 2013

3
Erwinが書いたものに加えて、ウィンドウ関数(最近の一般的なSQL機能です)を使用すると、ほとんどの場合、派生テーブルとの結合を使用するよりも高速になります
a_horse_with_no_name

6
素晴らしい参照。これが「グループごとの最大の問題」と呼ばれることを知りませんでした。ありがとうございました。
David Mann

問題は、グループあたりの最大の n ではなく最初の nです。
reinierpost

1
私が試した2つの注文フィールドの場合、「Bill Karwinによる左結合ソリューション」ではパフォーマンスが低下します。以下の私のコメントを参照してください。stackoverflow.com/ a / 8749095/684229
Johnny Wong

30

Postgresでは、次のarray_aggように使用できます。

SELECT  customer,
        (array_agg(id ORDER BY total DESC))[1],
        max(total)
FROM purchases
GROUP BY customer

これはあなたに idにより、各顧客の最大の購入。

注意すべき点:

  • array_aggは集約関数なので、で動作しGROUP BYます。
  • array_agg自分自身だけをスコープとする順序を指定できるので、クエリ全体の構造を制約しません。デフォルトとは異なる何かをする必要がある場合、NULLをソートする方法の構文もあります。
  • 配列を作成したら、最初の要素を取得します。(Postgres配列は0インデックスではなく1インデックスです)。
  • array_agg3番目の出力列にも同様の方法で使用できますが、max(total)より簡単です。
  • とは異なりDISTINCT ON、を使用array_aggすると、GROUP BY他の理由で必要な場合に備えてを保持できます。

14

このソリューションは、サブQが存在するため、Erwinが指摘したように効率的ではありません。

select * from purchases p1 where total in
(select max(total) from purchases where p1.customer=customer) order by total desc;

ありがとう、はい、同意します。subqと外部クエリの結合には実際に時間がかかります。"in"はここでは問題になりません。subqは1行のみになるためです。ところで、あなたはどの構文エラーを指していますか?
user2407394 2013年

おお.. ..それは各顧客のために最高の合計を見つける必要があるような関係を壊すnow..however編集したが、ここで必要とされていない「Teradataの」..に使用
user2407394

引き分けの場合、単一の顧客に対して複数の行を取得することを知っていますか?それが望ましいかどうかは、正確な要件によって異なります。通常はそうではありません。手元の質問については、タイトルはかなり明確です。
Erwin Brandstetter 2013年

これは質問から明らかではありません。同じ顧客が2つの異なるIDの購入=最大である場合、両方を表示する必要があると思います。
user2407394 2013年

10

私はこの方法を使用しています(postgresqlのみ):https : //wiki.postgresql.org/wiki/First/last_%28aggregate%29

-- Create a function that always returns the first non-NULL item
CREATE OR REPLACE FUNCTION public.first_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE sql IMMUTABLE STRICT AS $$
        SELECT $1;
$$;

-- And then wrap an aggregate around it
CREATE AGGREGATE public.first (
        sfunc    = public.first_agg,
        basetype = anyelement,
        stype    = anyelement
);

-- Create a function that always returns the last non-NULL item
CREATE OR REPLACE FUNCTION public.last_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE sql IMMUTABLE STRICT AS $$
        SELECT $2;
$$;

-- And then wrap an aggregate around it
CREATE AGGREGATE public.last (
        sfunc    = public.last_agg,
        basetype = anyelement,
        stype    = anyelement
);

その後、あなたの例はほとんどそのまま動作します:

SELECT FIRST(id), customer, FIRST(total)
FROM  purchases
GROUP BY customer
ORDER BY FIRST(total) DESC;

警告:NULLの行は無視されます


編集1-代わりにpostgres拡張機能を使用

今私はこのように使用します: http //pgxn.org/dist/first_last_agg/

ubuntu 14.04にインストールするには:

apt-get install postgresql-server-dev-9.3 git build-essential -y
git clone git://github.com/wulczer/first_last_agg.git
cd first_last_app
make && sudo make install
psql -c 'create extension first_last_agg'

これは、最初と最後の機能を提供するpostgresの拡張機能です。上記の方法より明らかに高速です。


編集2-順序付けとフィルタリング

(これらのような)集約関数を使用する場合、データを既に順序付けする必要なく、結果を順序付けできます。

http://www.postgresql.org/docs/current/static/sql-expressions.html#SYNTAX-AGGREGATES

したがって、順序付けを使用した同等の例は次のようになります。

SELECT first(id order by id), customer, first(total order by id)
  FROM purchases
 GROUP BY customer
 ORDER BY first(total);

もちろん、アグリゲート内に収まると見なしたときに、順序付けとフィルタリングを行うことができます。これは非常に強力な構文です。


このカスタム関数アプローチも使用します。十分に普遍的でシンプルです。なぜ物事を複雑にするのですか?これは他のソリューションよりもパフォーマンスが著しく低いですか?
Sergey Shcherbakov

9

クエリ:

SELECT purchases.*
FROM purchases
LEFT JOIN purchases as p 
ON 
  p.customer = purchases.customer 
  AND 
  purchases.total < p.total
WHERE p.total IS NULL

それはどのように機能しますか?(そこに行ったことがある)

各購入の合計が最高になるようにしたいと思います。


いくつかの理論的なもの(クエリを理解したいだけの場合は、この部分をスキップしてください)

Totalを関数T(customer、id)とし、名前とIDを指定して値を返す所定の合計(T(customer、id))が最高であることを証明するには、次のいずれかを証明する必要があることを証明する必要があります。

  • ∀xT(customer、id)> T(customer、x)(この合計は、その顧客の他のすべての合計よりも高い)

または

  • ¬∃xT(customer、id)<T(customer、x)(その顧客にはそれ以上の合計は存在しません)

最初の方法では、その名前のすべてのレコードを取得する必要があります。

2つ目は、これよりも高い記録はあり得ないと言うためのスマートな方法が必要になります。


SQLに戻る

名前を付けてテーブルを結合したままにし、合計が結合したテーブルよりも小さい場合:

      LEFT JOIN purchases as p 
      ON 
      p.customer = purchases.customer 
      AND 
      purchases.total < p.total

同じユーザーの合計が高い別のレコードがあるすべてのレコードが結合されることを確認します。

purchases.id, purchases.customer, purchases.total, p.id, p.customer, p.total
1           , Tom           , 200             , 2   , Tom   , 300
2           , Tom           , 300
3           , Bob           , 400             , 4   , Bob   , 500
4           , Bob           , 500
5           , Alice         , 600             , 6   , Alice   , 700
6           , Alice         , 700

これにより、グループ化を必要とせずに、各購入の合計の最大値をフィルタリングできます。

WHERE p.total IS NULL

purchases.id, purchases.name, purchases.total, p.id, p.name, p.total
2           , Tom           , 300
4           , Bob           , 500
6           , Alice         , 700

そして、それが私たちが必要とする答えです。


8

非常に高速なソリューション

SELECT a.* 
FROM
    purchases a 
    JOIN ( 
        SELECT customer, min( id ) as id 
        FROM purchases 
        GROUP BY customer 
    ) b USING ( id );

テーブルがidでインデックス付けされている場合、非常に高速です。

create index purchases_id on purchases (id);

USING句は非常に標準的です。一部のマイナーデータベースシステムにはないのです。
Holger Jakobs 2016

2
これは、合計が最も大きい顧客の購入を検出しない
Johnny Wong

7

SQL Serverでは、これを行うことができます。

SELECT *
FROM (
SELECT ROW_NUMBER()
OVER(PARTITION BY customer
ORDER BY total DESC) AS StRank, *
FROM Purchases) n
WHERE StRank = 1

説明:ここで、 Group byは顧客に基づいて行われ、それを合計で注文します。次に、そのような各グループにStRankとしてシリアル番号が与えられ、StRankが1である最初の1人の顧客を取り出します。


ありがとうございました!これは完全に機能し、理解と実装が非常に簡単でした。
ruohola


4

PostgreSQLでは、first_valueウィンドウ関数を以下と組み合わせて使用することもできますSELECT DISTINCT

select distinct customer_id,
                first_value(row(id, total)) over(partition by customer_id order by total desc, id)
from            purchases;

私は複合を作成した(id, total)ので、両方の値が同じ集計から返されます。もちろん、常にfirst_value()2回申請することができます。


3

承認されたOMGポニーの「任意のデータベースでサポートされている」ソリューションは、私のテストからは速度が優れています。

ここでは、同じアプローチを提供しますが、より完全でクリーンな任意のデータベースソリューションを提供します。ネクタイが考慮され(顧客ごとに1行のみ、顧客ごとの最大合計の複数のレコードでさえ取得したい場合)、他の購入フィールド(たとえば、purchase_payment_id)が購入テーブルの実際に一致する行に選択されます。

すべてのデータベースでサポートされています:

select * from purchase
join (
    select min(id) as id from purchase
    join (
        select customer, max(total) as total from purchase
        group by customer
    ) t1 using (customer, total)
    group by customer
) t2 using (id)
order by customer

このクエリは、特にPurchaseテーブルに(customer、total)のような複合インデックスがある場合は、かなり高速です。

リマーク:

  1. t1、t2は、データベースによっては削除できるサブクエリエイリアスです。

  2. 警告:このusing (...)節は、2017年1月のこの編集の時点では、MS-SQLとOracle dbでは現在サポートされていませんon t2.id = purchase.id。たとえば、自分で拡張する必要があります。USING構文は、SQLite、MySQL、PostgreSQLで機能します。


2

Snowflake / Teradataは、ウィンドウ関数のQUALIFYようHAVINGに機能する句をサポートしています。

SELECT id, customer, total
FROM PURCHASES
QUALIFY ROW_NUMBER() OVER(PARTITION BY p.customer ORDER BY p.total DESC) = 1

1
  • 集計行のセットから(特定の条件によって)任意の行を選択する場合。

  • sum/avgに加えて別の()集計関数を使用する場合max/min。したがって、あなたは手掛かりを使うことができませんDISTINCT ON

次のサブクエリを使用できます。

SELECT  
    (  
       SELECT **id** FROM t2   
       WHERE id = ANY ( ARRAY_AGG( tf.id ) ) AND amount = MAX( tf.amount )   
    ) id,  
    name,   
    MAX(amount) ma,  
    SUM( ratio )  
FROM t2  tf  
GROUP BY name

交換できます amount = MAX( tf.amount )1つの制限で任意の条件にます。このサブクエリは複数の行を返してはいけません

しかし、そのようなことをしたい場合は、おそらくウィンドウ関数を探します


1

SQl Serverの場合、最も効率的な方法は次のとおりです。

with
ids as ( --condition for split table into groups
    select i from (values (9),(12),(17),(18),(19),(20),(22),(21),(23),(10)) as v(i) 
) 
,src as ( 
    select * from yourTable where  <condition> --use this as filter for other conditions
)
,joined as (
    select tops.* from ids 
    cross apply --it`s like for each rows
    (
        select top(1) * 
        from src
        where CommodityId = ids.i 
    ) as tops
)
select * from joined

使用済みの列のクラスター化インデックスを作成することを忘れないでください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.