web-dev-qa-db-ja.com

group byで非集計列を使用できますか?

GROUP BYクエリのSELECT行に非集計を配置することはできません(すべきではありません)。

ただし、最大値に関連付けられている非集計の1つにアクセスしたいと思います。平易な英語で、私はそれぞれの種類の最も古いIDを持つテーブルが欲しいです。

CREATE TABLE stuff (
   id int,
   kind int,
   age int
);

このクエリは、私が求めている情報を提供します。

SELECT kind, MAX(age)
FROM stuff
GROUP BY kind;

しかし、それは最も有用な形ではありません。後のクエリで使用できるように、各行にidを関連付けたいと思っています。

私はこのようなものを探しています:

SELECT id, kind, MAX(age)
FROM stuff
GROUP BY kind;

これを出力します:

SELECT stuff.*
FROM
   stuff,
   ( SELECT kind, MAX(age)
     FROM stuff
     GROUP BY kind) maxes
WHERE
   stuff.kind = maxes.kind AND
   stuff.age = maxes.age

参加せずにこの情報を入手する方法があるはずだと本当に思えます。最大値を計算するときに他の列を記憶するSQLエンジンが必要です。

16
deft_code

最大経過時間を持つIDが1つだけではない可能性があるため、MAXが検出した行のIDを取得できません。

12

GROUP BYクエリのSELECT行に非集計を配置することはできません(すべきではありません)。

集計関数が正しい結果を返すために、グループ化する対象を定義することができ、定義する必要があります。

MySQL(およびSQLite)は、仕様に反し、SELECTで引用された列が欠落しているGROUP BY句をクエリが受け入れることを許可するという無限の知恵で決定しました。これにより、これらのクエリは事実上移植できなくなります。

参加せずにこの情報を入手する方法があるはずだと本当に思えます。

MySQLがサポートしていない分析/ランキング/ウィンドウ関数にアクセスできない場合、派生テーブル/インラインビューへの自己結合は、希望する結果を得る最も移植性の高い手段です。

6
OMG Ponies

最近のデータベースでは、sum()over(partition by ...)を使用してこの問題を解決できます。

select id, kind, age as max_age from (
  select id, kind, age, max(age) over (partition by kind) as mage
    from table)
where age = mage

これはシングルパスになります

2
Grimaldi

2回ジョブを実行するのではなく、1回のパスで問題を解決するようにシステムに依頼するのは確かに魅力的だと思います(最大値を見つけ、対応するIDを見つけます)。 CONCATを使用して(Naktibaldaの参照記事で提案されているように)実行できますが、それがより効率的かどうかはわかりません

SELECT MAX( CONCAT( LPAD(age, 10, '0'), '-', id)
FROM STUFF1
GROUP BY kind;

うまくいくはずですが、年齢とIDを取得するために答えを分割する必要があります。 (それは本当に醜いですが)

2
mb14

集計関数maxは多くの行を取得し、最大値を選択するため、結合が必要です。したがって、集約関数が検出したものを選択するには、結合が必要です。

別の言い方をすれば、maxをsumに置き換えた場合、クエリはどのように動作すると思いますか?

ただし、内部結合はサブクエリよりも効率的かもしれません。

1
developer

ここでは、PostgesSQLのDISTINCTONが役立ちます。

SELECT DISTINCT ON (kind) kind, id, age 
FROM stuff
ORDER BY kind, age DESC;

これは種類ごとにグループ化され、最初の行を順序付けられた形式で返します。年齢の降順で並べ替えているので、種類の最大年齢の行を取得します。

P.S. DISTINCT ONの列は、次の順序で最初に表示されます。

0
Aneesh Dash