web-dev-qa-db-ja.com

gROUP BY句に含まれているか、集約関数で使用されている必要があります。

この呼び出し元の '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です。

202
RandomGuy

はい、これは一般的な集計の問題です。 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)を手動で有効にする必要があります...

172
Sebas

Postgresでは、特別なDISTINCT ON (expression)という構文を使うこともできます。

SELECT DISTINCT ON (cname) 
    cname, wmname, avg
FROM 
    makerar 
ORDER BY 
    cname, avg DESC ;
98
ypercubeᵀᴹ

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

ただし、回避策があります。必須フィールドも同様に集約します。 posgresでは、これはうまくいくはずです。

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

これはすべてのwnameの配列をavg順に作成し、最初の要素を返すことに注意してください(postgresの配列は1ベースです)。

16
e-neko
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;

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

14
zero323

私にとっては、これは「一般的な集計の問題」ではなく、誤ったSQLクエリに関するものです。 「各cnameの最大avgを選択してください...」に対する唯一の正しい答えは、

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

結果は次のようになります。

 cname  |      MAX(avg)
--------+---------------------
 canada | 2.0000000000000000
 spain  | 5.0000000000000000

この結果は一般に質問 「各グループにとって最良の結果は何ですか?」に答えます。 。スペインの最良の結果は5であり、カナダの最良の結果は2であることがわかります。それは事実であり、エラーはありません。 wmnameも表示する必要がある場合は、「結果セットからwmnameを選択するためのRULEとは何ですか?」という質問に答える必要があります。間違いを明確にするために入力データを少し変更しましょう。

  cname | wmname |        avg           
--------+--------+-----------------------
 spain  | zoro   |  1.0000000000000000
 spain  | luffy  |  5.0000000000000000
 spain  | usopp  |  5.0000000000000000

このクエリの実行にどの結果が期待されますか:SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;spain+luffyまたはspain+usoppのどちらであるべきですか?どうして?いくつかが適切であれば、どのように「より良い」wmnameを選択するかは、クエリでは確定ではないため、結果も確定しません。 SQLインタプリタがエラーを返すのはそのためです。クエリは正しくありません。

言い換えれば、質問への正しい答えはありません "誰がspainグループの中で最高ですか?" 。 usoppは同じ「スコア」を持っているので、ルフィはusoppより優れていません。

3
ox160d05d

これもうまくいくようです

SELECT *
FROM makerar m1
WHERE m1.avg = (SELECT MAX(avg)
                FROM makerar m2
                WHERE m1.cname = m2.cname
               )
0
daintym0sh

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
0