382

この呼び出し元「makerar」のようなテーブルがあります

 cname  | wmname |          avg           
--------+-------------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  | 1.00000000000000000000
 spain  | usopp  |     5.0000000000000000

そして、各cnameの最大平均を選択したいと思います。

SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname;

エラーが出ますが、

ERROR:  column "makerar.wmname" must appear in the GROUP BY clause or be used in an   aggregate function 
LINE 1: SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname;

だから私はこれをします

SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname, wmname;

ただし、これでは意図した結果が得られず、以下の誤った出力が表示されます。

 cname  | wmname |          max           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  | 1.00000000000000000000
 spain  | usopp  |     5.0000000000000000

実際の結果は

 cname  | wmname |          max           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

この問題を解決するにはどうすればよいですか?

注: このテーブルは、以前の操作で作成された VIEW です。

4

7 に答える 7

311

はい、これは一般的な集約の問題です。SQL3 (1999)より前では、選択されたフィールドはGROUP BY句 [*] に表示される必要があります。

この問題を回避するには、サブクエリで集計を計算し、それ自体と結合して、表示する必要がある追加の列を取得する必要があります。

SELECT m.cname, m.wmname, t.mx
FROM (
    SELECT cname, MAX(avg) AS mx
    FROM makerar
    GROUP BY cname
    ) t JOIN makerar m ON m.cname = t.cname AND t.mx = m.avg
;

 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

しかし、より単純に見えるウィンドウ関数を使用することもできます。

SELECT cname, wmname, MAX(avg) OVER (PARTITION BY cname) AS mx
FROM makerar
;

このメソッドの唯一の点は、すべてのレコードを表示することです (ウィンドウ関数はグループ化されません)。ただし、各行に国の正しい (つまり、cnameレベルで最大)が表示されるので、それはあなた次第です:MAX

 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  |     5.0000000000000000
 spain  | usopp  |     5.0000000000000000

(cname, wmname)最大値に一致するタプルのみを表示するソリューションは、間違いなくエレガントではありませんが、次のとおりです。

SELECT DISTINCT /* distinct here matters, because maybe there are various tuples for the same max value */
    m.cname, m.wmname, t.avg AS mx
FROM (
    SELECT cname, wmname, avg, ROW_NUMBER() OVER (PARTITION BY avg DESC) AS rn 
    FROM makerar
) t JOIN makerar m ON m.cname = t.cname AND m.wmname = t.wmname AND t.rn = 1
;


 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

[*]: 興味深いことに、仕様ではグループ化されていないフィールドを選択できるようになっていますが、主要なエンジンはそれをあまり好まないようです。Oracle と SQLServer はこれをまったく許可していません。Mysql はデフォルトで許可していましたが、5.7 以降、ONLY_FULL_GROUP_BYこの機能をサポートするには、管理者がサーバー構成でこのオプション ( ) を手動で有効にする必要があります...

于 2013-10-26T02:02:55.813 に答える
161

DISTINCT ON (expression)Postgres では、特別な構文も使用できます。

SELECT DISTINCT ON (cname) 
    cname, wmname, avg
FROM 
    makerar 
ORDER BY 
    cname, avg DESC ;
于 2013-11-01T09:05:35.107 に答える
53

選択でグループ化されていないフィールドと集計されていないフィールドを指定する際の問題group byは、エンジンがこの場合に返す必要があるレコードのフィールドを知る方法がないことです。最初ですか?最後ですか?通常、集計結果に自然に対応するレコードはありません (minおよびmaxは例外です)。

ただし、回避策があります。必須フィールドも集約してください。postgres では、これでうまくいくはずです:

SELECT cname, (array_agg(wmname ORDER BY avg DESC))[1], MAX(avg)
FROM makerar GROUP BY cname;

これにより、平均順に並べられたすべての wname の配列が作成され、最初の要素が返されることに注意してください (postgres の配列は 1 ベースです)。

于 2015-11-20T15:51:38.897 に答える
20
SELECT t1.cname, t1.wmname, t2.max
FROM makerar t1 JOIN (
    SELECT cname, MAX(avg) max
    FROM makerar
    GROUP BY cname ) t2
ON t1.cname = t2.cname AND t1.avg = t2.max;

rank() ウィンドウ関数の使用:

SELECT cname, wmname, avg
FROM (
    SELECT cname, wmname, avg, rank() 
    OVER (PARTITION BY cname ORDER BY avg DESC)
    FROM makerar) t
WHERE rank = 1;

ノート

どちらも、グループごとに複数の最大値を保持します。avg が max に等しいレコードが複数ある場合でも、グループごとに 1 つのレコードのみが必要な場合は、@ypercube の回答を確認する必要があります。

于 2013-10-26T02:06:58.393 に答える
2

これも効果ありそう

SELECT *
FROM makerar m1
WHERE m1.avg = (SELECT MAX(avg)
                FROM makerar m2
                WHERE m1.cname = m2.cname
               )
于 2017-01-30T21:52:21.023 に答える
-1

最近、 を使用してカウントしようとしたときにこの問題に遭遇し、 andステートメントcase whenの順序を変更すると問題が解決することがわかりました。whichcount

SELECT date(dateday) as pick_day,
COUNT(CASE WHEN (apples = 'TRUE' OR oranges 'TRUE') THEN fruit END)  AS fruit_counter

FROM pickings

GROUP BY 1

- を使用する代わりに、後者で、リンゴとオレンジが集約関数に表示されるというエラーが発生しました

CASE WHEN ((apples = 'TRUE' OR oranges 'TRUE') THEN COUNT(*) END) END AS fruit_counter
于 2016-11-09T14:07:26.307 に答える