web-dev-qa-db-ja.com

SELECT DISTINCT ON、別の列で並べ替え

次の表testを検討してください。

CREATE TABLE test(col1 int, col2 varchar, col3 date);
INSERT INTO test VALUES
  (1,'abc','2015-09-10')
, (1,'abc','2015-09-11')
, (2,'xyz','2015-09-12')
, (2,'xyz','2015-09-13')
, (3,'tcs','2015-01-15')
, (3,'tcs','2015-01-18');
postgres=# select * from test;
  col1 | col2 |    col3    
 ------+------+------------
     1 | abc  | 2015-09-10
     1 | abc  | 2015-09-11
     2 | xyz  | 2015-09-12
     2 | xyz  | 2015-09-13
     3 | tcs  | 2015-01-15
     3 | tcs  | 2015-01-18

返されたセットを日付descで並べ替えたい:

 col1 | col2 |    col3    
------+------+------------
    2 | xyz  | 2015-09-13
    1 | abc  | 2015-09-11
    3 | tcs  | 2015-01-18

私がdistinct onで何とか達成したこと:

select distinct on (col1) col1, col2, col3 from test order by col1, col3 desc;
 col1 | col2 |    col3    
------+------+------------
    1 | abc  | 2015-09-11
    2 | xyz  | 2015-09-13
    3 | tcs  | 2015-01-18

havingで必要なものではありません:

select distinct on (col1) col1, col2, col3 from test group by col1, col2, col3 having col3 = max(col3)
 col1 | col2 |    col3    
------+------+------------
    1 | abc  | 2015-09-10
    2 | xyz  | 2015-09-13
    3 | tcs  | 2015-01-18
7
Luis

_DISTINCT ON_は引き続き使用できます。それを外部クエリにラップして、ニーズに合わせて並べ替えます。見る:

_SELECT *
FROM  (
   SELECT DISTINCT ON (col1)
          col1, col2, col3
   FROM   test
   ORDER  BY col1, col3 DESC
   ) sub
ORDER  BY col3 DESC, col2;
_

_col2_が機能的に_col1_に依存すると仮定すると、内部クエリの_DISTINCT ON_および_ORDER BY_では無視できます。しかし、意味のあるタイブレーカーとして、外側の_ORDER BY_に追加しました。 _col2_なしで_col1_が一意でない場合は、_col1_を追加することができます。

_col3_が_NOT NULL_として定義されていると仮定します。そうでない場合、_NULLS LAST_を追加します。

_(col1)_あたりfew行のみの場合、これは通常は最速のソリューション。見る:

db <> fiddle ここ

ウィンドウ関数row_number()を持つサブクエリ(Véraceが提案したような)は有効な代替手段ですが、通常は低速です。私は 多くのテスト を実行しましたが、自分で試してください。 _DISTINCT ON_(高速であると予想される場合は内部でハッシュアルゴリズムに切り替わる可能性があります)のように2回ソートする必要がありますが、内部クエリの後にすべての行を保持し、不要なコストを追加します。どちらの方法でも、内部クエリに_ORDER BY_は必要ありません。

_SELECT col1, col2, col3
FROM  (
   SELECT col1, col2, col3
       ,  row_number() OVER (PARTITION BY col1 ORDER BY col3 DESC) AS rn
   FROM   test
   ) sub
WHERE  rn = 1
ORDER  BY col3 DESC, col2;
_

また、CTEが必要ない場合は使用しないでください。通常はかなり高価です(ほとんどの場合これが修正されたPostgres 12まで)。

_col1_あたりのmany行の場合、インデックス付けがはるかに重要になり、通常ははるかに高速な代替手段。見る:

余談ですが、OracleやSQL Serverとは異なり、PostgreSQLは ウィンドウ関数 に対して「分析関数」という用語を使用しません。 (これらの関数の「分析」とは何ですか?)

6

これは定番です greatest-n-per-group 問題があります。それらは頻繁に領域のホスト全体で発生し、 Analytic functions (以下を参照)は学ぶ価値があります。

現在、これは通常、Analytic(別名Window)関数を使用することで解決されます。フィドル here を参照してください。

このクエリを使用できます-

WITH cte AS
(
  SELECT 
    ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS rn,
    col1, col2, col3 
  FROM test
  ORDER BY col3 DESC
)
SELECT * FROM cte 
WHERE rn = 1

結果-

rn  col1    col2    col3
1   2   xyz     2015-09-13
1   1   abc     2015-09-11
1   3   tcs     2015-01-18

分析関数は知っておく価値があります-それらは非常に強力であり、学習に費やしたあらゆる努力に対して何度も返済することがわかります。内部クエリを単独で実行します-実験、それが私が学んだ方法です。ところで、使用しているPostgreSQLのバージョンで質問にタグを付けることは常に価値があります!

これを行うより伝統的な方法は

SELECT x, y, mc FROM
(
  SELECT col1 AS x, col2 AS y, MAX(col3) AS mc
  FROM test
  GROUP BY col1, col2
) AS tab
ORDER BY mc

同じ結果-フィドルでも。

6
Vérace