web-dev-qa-db-ja.com

単一のSELECTで集計値と非集計値を混在させることができないのはなぜですか?

SELECTステートメントに1つの集約関数がある場合、ステートメントの他のすべての値は集約関数であるか、GROUP BY句にリストされている必要があります。わかりませんなぜそれが事実です。

私が行った場合:

SELECT Name, 'Jones' AS Surname FROM People

私は得ます:

NAME    SURNAME
Dave    Jones
Susan   Jones
Amy     Jones

そのため、DBMSは各行から値を取得し、結果セットでそれに単一の値を追加しました。それはいいです。しかし、それがうまくいくなら、なぜ私はできないのですか?

SELECT Name, COUNT(Name) AS Surname FROM People

同じように見えますが、各行から値を取得し、単一の値を追加します。しかし、代わりに:

NAME    SURNAME
Dave    3
Susan   3
Amy     3    

私は得ます:

集計関数の一部として指定された式 'ContactName'を含まないクエリを実行しようとしました。

許可されていないことはわかっていますが、2つの状況が似ているため、理由がわかりません。 DBMSを実装しやすくするためですか?なぜだと思うように動かないのか、誰かに説明してもらえたら、ありがたいです。

20
TarkaDaal

集計は完全な結果では機能せず、結果のグループでのみ機能します。

以下を含むテーブルを考えてみましょう:

_Person   Pet
-------- --------
Amy      Cat
Amy      Dog
Amy      Canary
Dave     Dog
Susan    Snake
Susan    Spider
_

Personでグループ化するクエリを使用すると、データが次のグループに分割されます。

_Amy:
  Amy    Cat
  Amy    Dog
  Amy    Canary
Dave:
  Dave   Dog
Susan:
  Susan  Snake
  Susan  Spider
_

集計を使用する場合、たとえばcount集計では、グループごとに1つの結果が生成されます。

_Amy:
  Amy    Cat
  Amy    Dog
  Amy    Canary    count(*) = 3
Dave:
  Dave   Dog       count(*) = 1
Susan:
  Susan  Snake
  Susan  Spider    count(*) = 2
_

したがって、クエリselect Person, count(*) from People group by Personは、グループごとに1つのレコードを提供します。

_Amy    3
Dave   1
Susan  2
_

結果でペットフィールドも取得しようとすると、各グループのそのフィールドに複数の値が存在する可能性があるため、機能しません。

(MySQLのような一部のデータベースは、とにかくそれを許可し、グループ内からランダムな値を返すだけであり、結果が適切かどうかを知るのはあなたの責任です。)

集計を使用してもグループ化を指定しない場合でも、クエリはグループ化され、結果全体が単一のグループになります。したがって、クエリselect count(*) from Personはすべてのレコードを含む単一のグループを作成し、集計はそのグループのレコードをカウントできます。結果には各グループの1行が含まれます。グループが1つしかないため、結果には1行が含まれます。

17
Guffa

グループ化せずにCOUNTを呼び出すと、テーブルが1つのグループに「折りたたまれ」、select句のグループ内の個々の項目にアクセスできなくなります。

それでも、サブクエリまたはクロス結合を使用して結果を取得できます。

    SELECT p1.Name, COUNT(p2.Name) AS Surname FROM People p1 CROSS JOIN People p2 GROUP BY p1.Name

    SELECT Name, (SELECT COUNT(Name) FROM People) AS Surname FROM People
8
Dmitry

他の人が説明したように、_GROUP BY_がある場合、またはSELECTリストでCOUNT()などの集計関数を使用している場合は、行のグループ化を行っているため、一致する行が折りたたまれていますグループごとに1つに。

_GROUP BY_なしでSELECTリストで集計関数のみを使用する場合、_GROUP BY 1_があると考えて、すべての行がグループ化され、1つに折りたたまれます。したがって、100行ある場合、100行あるため、データベースは実際に名前を表示できません。

ただし、「ウィンドウ処理」機能を持つRDBMSの場合、必要なことは実行可能です。例えば。 _GROUP BY_なしで集約関数を使用します。

テーブル内のすべての行(名前)がカウントされるSQL-Serverの例:

_SELECT Name
     , COUNT(*) OVER() AS cnt
FROM People
_

上記はどのように機能しますか?

  • COUNT(*) OVER() AS cntが存在しなかったようなNameが表示され、

  • テーブルを完全にグループ化しているようにCOUNT(*)を表示します。


もう一つの例。テーブルにSurnameフィールドがある場合は、次のようにして、姓でグループ化されたすべての行を表示し、同じ姓を持つ人の数を数えることができます。

_SELECT Name
     , Surname
     , COUNT(*) OVER(PARTITION BY Surname) AS cnt
FROM People
_
6
ypercubeᵀᴹ

クエリは暗黙的に結果セットにさまざまなタイプの行を要求しますが、これは許可されていません。返されるすべての行は同じタイプで、同じ種類の列を持つ必要があります。

'SELECT name、surname'は、テーブルのすべての行の行を返したいと考えています。

'SELECT COUNT(*)'は、テーブル内のすべての行の結果を組み合わせた単一の行を返したいと考えています。

この場合、データベースはおそらく両方のクエリを実行してから、 'SELECT COUNT(*)'の結果をすべての結果にコピーすることができます。これを行わない理由の1つは、ステルスパフォーマンスヒットになることです。どこにも宣言せずに、追加の自己結合を効果的に実行することになります。

他の回答では、このクエリの実際のバージョンを作成する方法を説明しているため、ここでは説明しません。

2
GlennS

集計関数とgroup by句は別個のものではなく、クエリの別の場所に表示される同じものの一部です。列で集計する場合は、集計に使用する関数を指定する必要があります。集計関数が必要な場合は、いくつかの列に適用する必要があります。

1
entonio

集約関数は、特定の条件を持つ複数の行から値を取得し、それらを1つの値に結合します。この条件は、ステートメントのGROUP BYによって定義されます。したがって、GROUP BYなしで集約関数を使用することはできません

SELECT Name, 'Jones' AS Surname FROM People  

固定値を持つ追加の列を選択するだけです...

SELECT Name, COUNT(Name) AS Surname FROM People GROUP BY Name

名前を選択するようにDBMSに指示し、すべての名前がテーブルで発生する頻度を覚えて、それらを1行にまとめます。したがって、GROUP BYを省略した場合、DBMSはレコードを折りたたむ方法を伝えることができません。

1
Syjin