衍生表通常無法參照 (相依於) 同一個 FROM
子句中先前資料表的欄位。可將衍生表定義為橫向衍生表,以指定允許此類參照。
非橫向衍生表使用第 15.2.15.8 節,「衍生表」中討論的語法來指定。橫向衍生表的語法與非橫向衍生表相同,唯一的差別是在衍生表規格之前指定關鍵字 LATERAL
。LATERAL
關鍵字必須位於每個要用作橫向衍生表的資料表之前。
橫向衍生表受限於下列限制
橫向衍生表只能出現在
FROM
子句中,可以是逗號分隔的表清單,或是 join 規範(JOIN
、INNER JOIN
、CROSS JOIN
、LEFT [OUTER] JOIN
或RIGHT [OUTER] JOIN
)。如果橫向衍生表位於 join 子句的右運算元,且包含對左運算元的參考,則 join 操作必須是
INNER JOIN
、CROSS JOIN
或LEFT [OUTER] JOIN
。如果該表位於左運算元,且包含對右運算元的參考,則 join 操作必須是
INNER JOIN
、CROSS JOIN
或RIGHT [OUTER] JOIN
。如果橫向衍生表參考了聚合函數,則該函數的聚合查詢不能是擁有橫向衍生表所在
FROM
子句的查詢。依照 SQL 標準,MySQL 始終將與表函數(例如
JSON_TABLE()
)的 join 操作視為已使用LATERAL
。由於LATERAL
關鍵字是隱含的,因此在JSON_TABLE()
之前不允許使用;這也符合 SQL 標準。
以下討論說明了橫向衍生表如何實現某些非橫向衍生表無法實現,或需要效率較低的解決方案才能完成的 SQL 操作。
假設我們要解決這個問題:給定一個銷售人員表(其中每行描述一個銷售人員)和一個所有銷售額的表(其中每行描述一筆銷售:銷售人員、客戶、金額、日期),確定每位銷售人員最大銷售額的大小和客戶。這個問題可以透過兩種方式解決。
解決問題的第一種方法:對於每位銷售人員,計算最大銷售額,並找出提供此最大銷售額的客戶。在 MySQL 中,可以這樣做
SELECT
salesperson.name,
-- find maximum sale size for this salesperson
(SELECT MAX(amount) AS amount
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id)
AS amount,
-- find customer for this maximum size
(SELECT customer_name
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id
AND all_sales.amount =
-- find maximum size, again
(SELECT MAX(amount) AS amount
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id))
AS customer_name
FROM
salesperson;
該查詢效率不高,因為它會為每位銷售人員計算兩次最大銷售額(第一次在第一個子查詢中,第二次在第二個子查詢中)。
我們可以嘗試透過為每位銷售人員計算一次最大值,並將其「快取」在衍生表中,來提高效率,如下面的修改後的查詢所示
SELECT
salesperson.name,
max_sale.amount,
max_sale_customer.customer_name
FROM
salesperson,
-- calculate maximum size, cache it in transient derived table max_sale
(SELECT MAX(amount) AS amount
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id)
AS max_sale,
-- find customer, reusing cached maximum size
(SELECT customer_name
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id
AND all_sales.amount =
-- the cached maximum size
max_sale.amount)
AS max_sale_customer;
但是,該查詢在 SQL-92 中是非法的,因為衍生表不能依賴同一個 FROM
子句中的其他表。衍生表必須在查詢的持續時間內保持不變,不能包含對其他 FROM
子句表的欄位的參考。如下所示,該查詢會產生此錯誤
ERROR 1054 (42S22): Unknown column 'salesperson.id' in 'where clause'
在 SQL:1999 中,如果衍生表前面加上 LATERAL
關鍵字(表示「此衍生表依賴於其左側的前一個表」),則查詢變為合法
SELECT
salesperson.name,
max_sale.amount,
max_sale_customer.customer_name
FROM
salesperson,
-- calculate maximum size, cache it in transient derived table max_sale
LATERAL
(SELECT MAX(amount) AS amount
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id)
AS max_sale,
-- find customer, reusing cached maximum size
LATERAL
(SELECT customer_name
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id
AND all_sales.amount =
-- the cached maximum size
max_sale.amount)
AS max_sale_customer;
橫向衍生表不必是常數,並且每次頂層查詢處理來自其依賴的前一個表的新行時,都會更新為最新狀態。
解決問題的第二種方法:如果在 SELECT
清單中的子查詢可以返回多個欄位,則可以使用不同的解決方案
SELECT
salesperson.name,
-- find maximum size and customer at same time
(SELECT amount, customer_name
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id
ORDER BY amount DESC LIMIT 1)
FROM
salesperson;
這是有效率的,但是非法的。它不起作用,因為此類子查詢只能返回單一欄位
ERROR 1241 (21000): Operand should contain 1 column(s)
一種重寫查詢的嘗試是從衍生表中選擇多個欄位
SELECT
salesperson.name,
max_sale.amount,
max_sale.customer_name
FROM
salesperson,
-- find maximum size and customer at same time
(SELECT amount, customer_name
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id
ORDER BY amount DESC LIMIT 1)
AS max_sale;
但是,這也行不通。衍生表依賴於 salesperson
表,因此在沒有 LATERAL
的情況下會失敗
ERROR 1054 (42S22): Unknown column 'salesperson.id' in 'where clause'
加入 LATERAL
關鍵字使查詢合法
SELECT
salesperson.name,
max_sale.amount,
max_sale.customer_name
FROM
salesperson,
-- find maximum size and customer at same time
LATERAL
(SELECT amount, customer_name
FROM all_sales
WHERE all_sales.salesperson_id = salesperson.id
ORDER BY amount DESC LIMIT 1)
AS max_sale;
簡而言之,LATERAL
是解決剛才討論的兩種方法中所有缺點的有效解決方案。