質問
既存のアプリケーションに機能を追加しようとしていますが、次のような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をグループ化するため、選択できます。ただし、集計関数内になく、GROUP BY句の一部ではない列も選択しています。これはどのように可能ですか?値はグループごとに一意ではないため、ランダムな値を選択するだけですか?
(MSSQL Server)の出身地、それはエラーです。誰かが私にこの動作を説明できますか、なぜMySQLで許可されていますか?
解決
この機能はいくつかのあいまいなクエリを許可し、その列から選択された任意の値で結果セットをサイレントに返すことは事実です。実際には、最初に物理的に保存されるのはグループ内の行の値になる傾向があります。
これらのクエリは、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句も許可しますが、グループの last 行から値を選択します。†
† 少なくともテストしたバージョンでは。 任意とは、MySQLまたはSQLiteのいずれかが将来実装を変更し、いくつかの異なる動作をする可能性があることを意味します。したがって、現在このようなあいまいな場合にあるように振る舞いに依存することはできません。クエリを、確定的で曖昧ではないように書き換えることをお勧めします。これが、MySQL 5.7がデフォルトでONLY_FULL_GROUP_BYを有効にする理由です。
他のヒント
Googleをもう少し長く使用する必要があります... 私の回答。
MySQLはGROUP BYの使用を拡張します。 非集計列を使用できること またはSELECTリスト内の計算 GROUP BYに表示されない 句。この機能を使用して、 回避することでパフォーマンスを向上させる 不要な列のソートと グルーピング。たとえば、必要はありません のcustomer.nameでグループ化する 次のクエリ
標準SQLでは、追加する必要があります customer.nameをGROUP BY句に追加します。 MySQLでは、名前は冗長です。
それでも、それは...間違っているようです。
select * from personel where p_id IN(select
min(dbo.personel.p_id)
FROM
personel
GROUP BY dbo.personel.p_adi)
次のようなクエリがあるとします:
SELECT g, v
FROM t
GROUP BY g;
この場合、 g
の可能な値ごとに、mysqlは v
の対応する値の1つを選択します。
ただし、どちらを選択するかは、状況によって異なります。
gの各グループについて、レコードがテーブル t
に挿入された順序で v
の最初の値が保持されることをどこかで読みました。
テーブルのレコードは要素の順序が重要ではない set として扱われるため、これは非常にいです。これはとても「mysql-ish」です...
v
のどの値を保持するかを決定する場合、次のように t
の副選択を適用する必要があります。
SELECT g, v
FROM (
SELECT *
FROM t
ORDER BY g, v DESC
) q
GROUP BY g;
この方法で、サブクエリのレコードが外部クエリによって処理される順序を定義します。したがって、 g の個々の値に対して選択する
v
の値を信頼できます。コード>。
ただし、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');
mysql 5.6.41で。
新しいバージョンで修正または修正されたバグの可能性があります。新しいバージョンの経験がある場合は、フィードバックをお寄せください。