SQL-92 和更早版本不允許查詢的選取清單、HAVING
條件或 ORDER BY
清單參照未在 GROUP BY
子句中命名的非聚合欄位。例如,此查詢在標準 SQL-92 中是非法的,因為選取清單中未聚合的 name
欄位未出現在 GROUP BY
中
SELECT o.custid, c.name, MAX(o.payment)
FROM orders AS o, customers AS c
WHERE o.custid = c.custid
GROUP BY o.custid;
為了使查詢在 SQL-92 中合法,必須從選取清單中省略 name
欄位,或在 GROUP BY
子句中命名它。
如果非聚合欄位在功能上相依於 GROUP BY
欄位,則 SQL:1999 及更高版本允許每個選用功能 T301 使用此類非聚合欄位:如果 name
和 custid
之間存在這種關係,則查詢是合法的。例如,如果 custid
是 customers
的主索引鍵,情況就會如此。
MySQL 實作功能相依性的偵測。如果啟用 ONLY_FULL_GROUP_BY
SQL 模式 (預設為啟用),MySQL 會拒絕選取清單、HAVING
條件或 ORDER BY
清單參照未在 GROUP BY
子句中命名,且在功能上不相依於它們的非聚合欄位的查詢。
當啟用 SQL ONLY_FULL_GROUP_BY
模式時,如果該欄位僅限於單一值,MySQL 也允許在 GROUP BY
子句中未命名的非聚合欄位,如下列範例所示
mysql> CREATE TABLE mytable (
-> id INT UNSIGNED NOT NULL PRIMARY KEY,
-> a VARCHAR(10),
-> b INT
-> );
mysql> INSERT INTO mytable
-> VALUES (1, 'abc', 1000),
-> (2, 'abc', 2000),
-> (3, 'def', 4000);
mysql> SET SESSION sql_mode = sys.list_add(@@session.sql_mode, 'ONLY_FULL_GROUP_BY');
mysql> SELECT a, SUM(b) FROM mytable WHERE a = 'abc';
+------+--------+
| a | SUM(b) |
+------+--------+
| abc | 3000 |
+------+--------+
使用 ONLY_FULL_GROUP_BY
時,也可以在 SELECT
清單中擁有多個非聚合欄位。在這種情況下,每個此類欄位都必須在 WHERE
子句中限制為單一值,且所有此類限制條件都必須由邏輯 AND
連接,如下所示
mysql> DROP TABLE IF EXISTS mytable;
mysql> CREATE TABLE mytable (
-> id INT UNSIGNED NOT NULL PRIMARY KEY,
-> a VARCHAR(10),
-> b VARCHAR(10),
-> c INT
-> );
mysql> INSERT INTO mytable
-> VALUES (1, 'abc', 'qrs', 1000),
-> (2, 'abc', 'tuv', 2000),
-> (3, 'def', 'qrs', 4000),
-> (4, 'def', 'tuv', 8000),
-> (5, 'abc', 'qrs', 16000),
-> (6, 'def', 'tuv', 32000);
mysql> SELECT @@session.sql_mode;
+---------------------------------------------------------------+
| @@session.sql_mode |
+---------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION |
+---------------------------------------------------------------+
mysql> SELECT a, b, SUM(c) FROM mytable
-> WHERE a = 'abc' AND b = 'qrs';
+------+------+--------+
| a | b | SUM(c) |
+------+------+--------+
| abc | qrs | 17000 |
+------+------+--------+
如果停用 ONLY_FULL_GROUP_BY
,MySQL 對標準 SQL 的 GROUP BY
用法的擴充功能允許選取清單、HAVING
條件或 ORDER BY
清單參照非聚合欄位,即使這些欄位在功能上不相依於 GROUP BY
欄位也是如此。這會導致 MySQL 接受前面的查詢。在這種情況下,伺服器可以自由地從每個群組中選擇任何值,因此,除非它們相同,否則所選的值是不確定的,這可能不是您想要的。此外,從每個群組選擇的值不會受到新增 ORDER BY
子句的影響。結果集排序會在選擇值後發生,而 ORDER BY
不會影響伺服器選擇每個群組內的哪個值。當您知道由於資料的某些屬性,未在 GROUP BY
中命名的每個非聚合欄位中的所有值對於每個群組都相同時,停用 ONLY_FULL_GROUP_BY
主要很有用。
您可以使用 ANY_VALUE()
來參照非聚合欄位,而無需停用 ONLY_FULL_GROUP_BY
來達到相同的效果。
下列討論示範功能相依性、MySQL 在缺少功能相依性時產生的錯誤訊息,以及導致 MySQL 在缺少功能相依性的情況下接受查詢的方法。
由於選取清單中未聚合的 address
欄位未在 GROUP BY
子句中命名,因此啟用 ONLY_FULL_GROUP_BY
時,此查詢可能無效
SELECT name, address, MAX(age) FROM t GROUP BY name;
如果 name
是 t
的主索引鍵,或是唯一的 NOT NULL
欄位,則查詢有效。在這種情況下,MySQL 會識別選取的欄位在功能上相依於群組欄位。例如,如果 name
是主索引鍵,則其值會決定 address
的值,因為每個群組只有一個主索引鍵的值,因此只有一個資料列。因此,群組中 address
值的選擇沒有隨機性,因此不需要拒絕查詢。
如果 name
不是 t
的主索引鍵或唯一的 NOT NULL
欄位,則查詢無效。在這種情況下,無法推斷出功能相依性,並且會發生錯誤
mysql> SELECT name, address, MAX(age) FROM t GROUP BY name;
ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP
BY clause and contains nonaggregated column 'mydb.t.address' which
is not functionally dependent on columns in GROUP BY clause; this
is incompatible with sql_mode=only_full_group_by
如果您知道 對於給定的資料集,每個 name
值實際上都唯一決定 address
值,則 address
在功能上有效地相依於 name
。為了告知 MySQL 接受查詢,您可以使用 ANY_VALUE()
函式
SELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name;
或者,停用 ONLY_FULL_GROUP_BY
。
然而,前面的範例非常簡單。特別是,您不太可能在單一主索引鍵欄位上群組,因為每個群組都只包含一個資料列。如需在更複雜的查詢中示範功能相依性的其他範例,請參閱第 14.19.4 節「功能相依性的偵測」。
如果查詢具有聚合函式,且沒有 GROUP BY
子句,則在啟用 ONLY_FULL_GROUP_BY
的情況下,選取清單、HAVING
條件或 ORDER BY
清單中不能有非聚合欄位
mysql> SELECT name, MAX(age) FROM t;
ERROR 1140 (42000): In aggregated query without GROUP BY, expression
#1 of SELECT list contains nonaggregated column 'mydb.t.name'; this
is incompatible with sql_mode=only_full_group_by
如果沒有 GROUP BY
,則只有單一分組,且對於該分組要選擇哪個 name
值是不確定的。這裡,如果 MySQL 選擇哪個 name
值不重要,也可以使用 ANY_VALUE()
。
SELECT ANY_VALUE(name), MAX(age) FROM t;
ONLY_FULL_GROUP_BY
也會影響使用 DISTINCT
和 ORDER BY
的查詢處理。考慮一個表格 t
,它有三個欄位 c1
、c2
和 c3
,並且包含以下這些列
c1 c2 c3
1 2 A
3 4 B
1 2 C
假設我們執行以下查詢,預期結果會依 c3
排序
SELECT DISTINCT c1, c2 FROM t ORDER BY c3;
為了對結果排序,必須先消除重複項目。但要這麼做,我們應該保留第一列還是第三列?這個任意的選擇會影響保留的 c3
值,進而影響排序,使其也變得任意。為了避免這個問題,如果任何 ORDER BY
表達式不符合至少以下一個條件,則具有 DISTINCT
和 ORDER BY
的查詢會被視為無效而拒絕。
該表達式等於選取列表中的一個表達式
該表達式所引用且屬於查詢所選取表格的所有欄位,都是選取列表的元素
MySQL 對於標準 SQL 的另一個擴充,允許在 HAVING
子句中引用選取列表中的別名表達式。例如,以下查詢會傳回在 orders
表格中只出現一次的 name
值
SELECT name, COUNT(name) FROM orders
GROUP BY name
HAVING COUNT(name) = 1;
MySQL 擴充功能允許在 HAVING
子句中使用聚合欄位的別名
SELECT name, COUNT(name) AS c FROM orders
GROUP BY name
HAVING c = 1;
標準 SQL 只允許在 GROUP BY
子句中使用欄位表達式,因此像這樣的語句是無效的,因為 FLOOR(value/100)
是一個非欄位表達式
SELECT id, FLOOR(value/100)
FROM tbl_name
GROUP BY id, FLOOR(value/100);
MySQL 擴充標準 SQL 以允許在 GROUP BY
子句中使用非欄位表達式,並認為先前的語句有效。
標準 SQL 也不允許在 GROUP BY
子句中使用別名。MySQL 擴充標準 SQL 以允許別名,因此另一種寫入查詢的方式如下
SELECT id, FLOOR(value/100) AS val
FROM tbl_name
GROUP BY id, val;
別名 val
在 GROUP BY
子句中被視為欄位表達式。
在 GROUP BY
子句中存在非欄位表達式的情況下,MySQL 會識別該表達式與選取列表中的表達式之間的相等性。這表示在啟用 ONLY_FULL_GROUP_BY
SQL 模式時,包含 GROUP BY id, FLOOR(value/100)
的查詢是有效的,因為相同的 FLOOR()
表達式也出現在選取列表中。但是,MySQL 不會嘗試識別對 GROUP BY
非欄位表達式的函數相依性,因此,即使第三個選取的表達式是 id
欄位和 GROUP BY
子句中的 FLOOR()
表達式的簡單公式,在啟用 ONLY_FULL_GROUP_BY
的情況下,以下查詢仍會無效
SELECT id, FLOOR(value/100), id+FLOOR(value/100)
FROM tbl_name
GROUP BY id, FLOOR(value/100);
一種解決方法是使用衍生表格
SELECT id, F, id+F
FROM
(SELECT id, FLOOR(value/100) AS F
FROM tbl_name
GROUP BY id, FLOOR(value/100)) AS dt;