59

既存のアプリケーションに機能を追加しようとしていますが、次のようなMySQLビューに出くわしました。

SELECT
     AVG(table_name.col1),
     AVG(table_name.col2),
     AVG(table_name.col3),
     table_name.personID,
     table_name.col4
FROM table_name
GROUP BY table_name.personID;

OK、いくつかの集計関数があります。グループ化するため、personIDを選択できます。ただし、集計関数に含まれておらず、GROUPBY句の一部でもない列も選択しています。これはどのように可能ですか?値はグループごとに確実に一意ではないため、ランダムな値を選択するだけですか?

私の出身地(MSSQL Server)では、それはエラーです。誰かがこの動作を私に説明できますか、そしてなぜそれがMySQLで許可されているのですか?

4

4 に答える 4

59

この機能がいくつかのあいまいなクエリを許可し、その列から選択された任意の値を含む結果セットをサイレントに返すことは事実です。実際には、最初に物理的に保存されるのは、グループ内の行の値になる傾向があります。

GROUP BY基準の列に機能的に依存する列のみを選択する場合、これらのクエリはあいまいではありません。つまり、グループを定義する値ごとに「あいまいな」列の個別の値が1つしかない場合は、問題はありません。このクエリは、論理的にあいまいさをもたらすことはできませんが、Microsoft SQL Server(およびANSI SQL)では違法です。

SELECT AVG(table1.col1), table1.personID, persons.col4
FROM table1 JOIN persons ON (table1.personID = persons.id)
GROUP BY table1.personID;

また、MySQLにはSQLモードがあり、標準に従って動作します。 ONLY_FULL_GROUP_BY

FWIW、SQLiteはこれらのあいまいなGROUP BY句も許可しますが、グループの最後の行から値を選択します。†</sup>


†</sup>少なくとも私がテストしたバージョンでは。任意であるとは、MySQLまたはSQLiteのいずれかが将来実装を変更し、いくつかの異なる動作をする可能性があることを意味します。したがって、このようなあいまいなケースで現在のように動作を維持することに依存するべきではありません。クエリを書き直して、決定論的で曖昧にならないようにすることをお勧めします。これが、MySQL5.7がデフォルトでONLY_FULL_GROUP_BYを有効にする理由です。

于 2009-06-21T08:22:26.543 に答える
16

もう少しグーグルするべきだった…答えが見つかったようだ。

MySQLは、GROUP BYの使用を拡張して、GROUPBY句に表示されないSELECTリスト内の非集計列または計算を使用できるようにします。この機能を使用すると、不要な列の並べ替えやグループ化を回避して、パフォーマンスを向上させることができます。たとえば、次のクエリでcustomer.nameをグループ化する必要はありません。

標準SQLでは、customer.nameをGROUPBY句に追加する必要があります。MySQLでは、名前は冗長です。

それでも、それはただ...間違っているようです。

于 2009-06-21T07:09:40.807 に答える
2

次のようなクエリがあるとします。

SELECT g, v 
FROM t
GROUP BY g;

この場合、の可能な値ごとにg、mysqlはの対応する値の1つを選択しますv

ただし、どちらを選択するかは、状況によって異なります。

vgの各グループについて、レコードがテーブルに挿入された順序で、の最初の値が保持されていることをどこかで読みましたt

テーブル内のレコードは、要素の順序が重要ではないセットとして扱われる必要があるため、これは非常に醜いです。これはとても「mysqlっぽい」です...

保持する値を決定する場合は、次のようvに副選択を適用する必要があります。t

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        ORDER BY g, v DESC
) q
GROUP BY g;

このようにして、サブクエリのレコードが外部クエリによって処理される順序を定義します。したがってv、の個々の値に対してどの値が選択されるかを信頼できますg

ただし、WHERE条件が必要な場合は、十分に注意してください。WHERE条件をサブクエリに追加すると、動作が維持され、常に期待する値が返されます。

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
GROUP BY g;

これはあなたが期待することであり、副選択はテーブルをフィルタリングして順序付けます。g指定された値を持つレコードを保持し、外部クエリはそれgとの最初の値を返しますv

ただし、同じWHERE条件を外部クエリに追加すると、非決定論的な結果が得られます。

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        -- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
GROUP BY g;

驚いたことに、同じクエリを何度も実行すると、異なる値が表示されるv場合があります。これは奇妙なことです。予想される動作は、サブクエリから適切な順序ですべてのレコードを取得し、外部クエリでそれらをフィルタリングしてから、前の例で選択したものと同じものを選択することです。しかし、そうではありません。

v一見ランダムに値を選択します。v同じクエリが、より多く(〜20)回実行した場合に異なる値を返しましたが、分布は均一ではありませんでした。

外部WHEREを追加する代わりに、次のようにHAVING条件を指定する場合:

SELECT g, v 
FROM (
    SELECT * 
        FROM t1 
        -- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
-- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
GROUP BY g
HAVING g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9';

その後、再び一貫した動作が得られます。

結論:この手法にまったく依存しないことをお勧めします。本当に必要な場合は、外部クエリでWHERE条件を回避してください。可能であれば内部クエリで使用するか、外部クエリでHAVING句を使用します。

私はこのデータでそれをテストしました:

CREATE TABLE t1 (
    v INT,
    g VARCHAR(36)
);

INSERT INTO t1 VALUES (1, '737a8783-110c-447e-b4c2-1cbb7c6b72c9');
INSERT INTO t1 VALUES (2, '737a8783-110c-447e-b4c2-1cbb7c6b72c9');

mysql5.6.41で。

新しいバージョンで修正された/修正されたバグかもしれません。新しいバージョンの経験がある場合は、フィードバックをお寄せください。

于 2018-12-05T05:33:28.643 に答える
0
select * from personel where p_id IN(select
min(dbo.personel.p_id)
FROM
personel
GROUP BY dbo.personel.p_adi)
于 2013-04-15T09:21:45.020 に答える