pgsql中的lateral使用小結


pgsql中的lateral

  • 什么是LATERAL
  • 帶有LATERAL的SQL的計算步驟
  • LATERAL在OUTER JOIN中的使用限制(或定義限制)
  • LATERAL的幾個簡單的例子
  • 總結

舉幾個我經常使用的栗子

首先說下場景:
有個一個商品表goods,還有一個評價表evaluations。商品表和評價表是一對多的。
1、在一個后台,我想查詢商品的信息,同時查詢這個商品的評價的數量。
我們可以通過這樣來實現

SELECT 
    g.*,
    COUNT(e.*) as num
FROM goods as g
LEFT JOIN evaluation as e on e.goods_id=g.id
WHERE 1=1  GROUP BY g.id

通過左連接,加上分組就能實現了
那么也可以使用lateral來實現

SELECT 
    g.*,
    e.num
FROM goods as g
LEFT JOIN LATERAL(
    SELECT COUNT(ev.id) as num FROM  evaluation AS ev 
    WHERE   ev.goods_id=g.id
) AS e ON TRUE
WHERE 1=1 

就這樣好像lateral的優勢不是那么明顯。
2、我們查詢評論數目大於3的商品的信息

SELECT 
    g.*,
    COUNT(e.*) as num
FROM goods as g
LEFT JOIN evaluation as e on e.goods_id=g.id
HAVING COUNT(e.*)>3   GROUP BY g.id 

這樣就不行了,查詢不到了。
這時候就需要使用LATERAL

SELECT 
    g.*,
    e.num
FROM goods as g
LEFT JOIN LATERAL(
    SELECT COUNT(ev.id) as num FROM  evaluation AS ev 
    WHERE   ev.goods_id=g.id
) AS e ON TRUE
WHERE 1=1 AND num>3

3、然后我們再次查詢這些商品的信息,希望找到黃金會員評論的商品信息
這時候LATERAL的優勢就更加明顯了

SELECT 
    g.*,
    e.num
FROM goods as g
LEFT JOIN LATERAL(
     SELECT COUNT(ev.id) as num
     FROM  evaluation AS ev 
     LEFT JOIN  users u  on u.id=ev.user_id
     WHERE   ev.goods_id=g.id AND u.grade=9
) AS e ON TRUE
WHERE 1=1 AND num>0

什么是LATERAL

我們先來看官方對lateral的定義

可以在出現於FROM中的子查詢前放置關鍵詞LATERAL。這允許它們引用前面的FROM項提供的列(如果沒有 LATERAL,每一個子查詢將被獨立計算,並且因此不能被其他FROM項交叉引用)。
出現在FROM中的表函數的前面也可以被放上關鍵詞LATERAL,但對於關鍵詞的不可選的,在任何情況下函 數的參數都可以包含前面FROM項提供額列的引用。
一個LATERAL項可以出現在FROM列表項層,或者出現在一個JOIN樹中。在后者如果出現在JOIN的右部, 那么可以引用在JOIN左部分的任何項。
如果一個FROM項包含LATERAL交叉引用,計算過程中:對於提供交叉引用列的FROM項的每一行,或者 多個提供這些列的多個FROM項進行集合,LATERAL項將被使用該行或者行集中的列值進行計算。得到結 果行將和它們被計算出來的行進行正常的連接。對於來自這些列的源表的每一行或行集,該過程將重復。

手冊上提到:

SELECT * FROM foo, LATERAL (SELECT * FROM bar WHERE bar.id = foo.bar_id) ss;    
  
在 LATERAL (這里可以關聯(引用)lateral左邊的表或子句)  
  
所以允許:   
  
LATERAL (SELECT * FROM bar WHERE bar.id = foo.bar_id) 

帶有LATERAL的SQL的計算步驟

1、逐行提取被lateral子句關聯(引用)的FROM或JOIN的ITEM(也叫source table)的記錄(s) 中的column(s)

for each row of the FROM item providing the cross-referenced column(s),
or set of rows of multiple FROM items providing the columns,


2、使用以上提取的columns(s),關聯計算lateral子句中的ITEM
the LATERAL item is evaluated using that row or row set'us values of the columns.


3、lateral的計算結果row(s),與所有from,join ITEM(S)正常的進行join計算 The resulting row(s) are joined as usual with the rows they were computed from.


4、從1到3開始循環,直到所有的source table的行取盡。
This is repeated for each row or set of rows from the column source table(s).

 

LATERAL在OUTER JOIN中的使用限制(或定義限制)

由於lateral的計算步驟是從source table逐條展開的,所以OUTER JOIN時只能使用source table 作為whole端,LATERAL內的ITEM不能作為WHOLE端。
因此lateral只能在left join的右邊。或者right join的左邊。因此不能是WHOLE端。

The column source table(s) must be INNER or LEFT joined to the LATERAL item,   
  
else there would not be a well-defined set of rows from which to compute each set of rows for the LATERAL item.   
  
Thus, although a construct such as X RIGHT JOIN LATERAL Y is syntactically valid,   
  
it is not actually allowed for Y to reference X.  

 LATERAL的幾個簡單的例子

1、
A trivial example of LATERAL is

SELECT * FROM foo, LATERAL (SELECT * FROM bar WHERE bar.id = foo.bar_id) ss;

This is not especially useful since it has exactly the same result as the more conventional

SELECT * FROM foo, bar WHERE bar.id = foo.bar_id;

一個LATERAL項可以出現在FROM列表項層
2、
LATERAL is primarily useful when the cross-referenced column is necessary for computing the row(s) to be joined. A common application is providing an argument value for a set-returning function. For example, supposing that vertices(polygon) returns the set of vertices of a polygon, we could identify close-together vertices of polygons stored in a table with:

SELECT p1.id, p2.id, v1, v2
FROM polygons p1, polygons p2,
     LATERAL vertices(p1.poly) v1,
     LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

This query could also be written

SELECT p1.id, p2.id, v1, v2
FROM polygons p1 CROSS JOIN LATERAL vertices(p1.poly) v1,
     polygons p2 CROSS JOIN LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

函數調用,支持應用函數左邊的ITEM(S)。所以可以看消除LATERAL,語義是一樣的。
(As already mentioned, the LATERAL key word is unnecessary in this example, but we use it for clarity.)

3、
It is often particularly handy to LEFT JOIN to a LATERAL subquery, so that source rows will appear in the result even if the LATERAL subquery produces no rows for them. For example, if get_product_names() returns the names of products made by a manufacturer, but some manufacturers in our table currently produce no products, we could find out which ones those are like this:

SELECT m.name
FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true
WHERE pname IS NULL;

lateral的查詢結果也是可以作為整個語句的查詢條件的

總結

1、lateral 可以出現在FROM的列表項層,也可以出現在JOIN數樹中,如果出現在JOIN的右部分,那么 可以引用在JOIN左部分的任何項。
2、由於lateral的計算步驟是從source table逐條展開的,所以OUTER JOIN時只能使用source table 作為whole端,LATERAL內的ITEM不能作為WHOLE端。
3、LATERAL 關鍵詞可以在前綴一個 SELECT FROM 子項. 這能讓 SELECT 子項在FROM項出現之前就引 用到FROM項中的列. (沒有 LATERAL 的話, 每一個 SELECT 子項彼此都是獨立的,因此不能夠對其 它的 FROM 項進行交叉引用.)
4、當一個 FROM 項包含 LATERAL 交叉引用的時候,查詢的計算過程如下: 對於FROM項提供給交叉引 用列的每一行,或者多個FROM像提供給引用列的行的集合, LATERAL 項都會使用行或者行的集合的列 值來進行計算. 計算出來的結果集像往常一樣被加入到聯合查詢之中. 這一過程會在列的來源表的行或 者行的集合上重復進行.

參考

【PostgreSQL 9.3 add LATERAL support - LATERAL的語法和用法介紹】https://github.com/digoal/blog/blob/master/201210/20121008_01.md?spm=a2c4e.10696291.0.0.408619a4cXorB6&file=20121008_01.md
【LATERAL】https://www.postgresql.org/docs/devel/queries-table-expressions.html#QUERIES-LATERAL


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM