2016-04-15 33 views
3

我在查詢中遇到問題,其中一個CTE沒有返回任何行。但是這很難被注意到,並且在很短的時間內調試了一段時間。如何在PostgreSQL中調試公用表表達式?

是否有可能在Postgres中輸出所有CTE,而不會註釋掉主要查詢

create or replace function generate_grid(
    poly geometry, step double precision) 
    returns setof geometry as 
$$ */ 

with 
    initial as (select st_xmin(poly) x0, st_ymin(poly) y0), 
    ...(another 3 CTE skipped here)... 
    grid as (select point from points where st_intersects(point, poly)), 
    centroid as (select st_centroid(poly) point from grid where (select count(*)=0 from grid)) 
select * from grid 
union all 
select * from centroid; 
$$ language sql; 

在該示例中,CTE centroid要逐步增量添加到之前運作良好的功能。它應該返回行,以防grid爲空。該錯誤(,我已經修復)是它沒有,因爲它從空CTE grid中選擇。現在,當我描述問題時,很明顯它爲什麼會失敗,但是當您編寫它並進行調試時,可能會發生各種各樣的事情,例如混合幾何體SRID,錯誤的SRID等。

+0

你爲什麼會寫這樣的事情擺在首位,如果你不知道是什麼它呢? – joop

+0

@joop當你不小心發出錯誤時,你總是知道你的程序在做什麼。這是一個小的細節,你失蹤,使事情無法正常工作。我添加了與之合作的真實查詢。 –

+1

'...從網格哪裏(選擇計數(*)= 0從網格))'只是不好的風格,這是可以避免的,恕我直言。 [我甚至不知道它是否是有效的語法;我會在這裏使用'NOT EXISTS()'構造IIUC這個片段] – joop

回答

3

EXPLAIN ANALYZE看起來要單獨報告CTE。

當我運行(PostgreSQL系統9.4)它顯示的CTE分開,並在結果部分。它只表示從返回「CTE掃描於x」的實際的行數爲0

explain analyze 
with x as (select 1 where false), 
    y as (select 2 where true) 
select * from x, y; 

返回:

Nested Loop (cost=0.02..0.07 rows=1 width=8) (actual time=0.002..0.002 rows=0 loops=1) 
    Output: x."?column?", y."?column?" 
    CTE x 
    -> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.000..0.000 rows=0 loops=1) 
      Output: 1 
      One-Time Filter: false 
    CTE y 
    -> Result (cost=0.00..0.01 rows=1 width=0) (never executed) 
      Output: 2 
    -> CTE Scan on x (cost=0.00..0.02 rows=1 width=4) (actual time=0.002..0.002 rows=0 loops=1) 
     Output: x."?column?" 
    -> CTE Scan on y (cost=0.00..0.02 rows=1 width=4) (never executed) 
     Output: y."?column?" 
Planning time: 0.034 ms 
Execution time: 0.018 ms 

我不知道解釋總是會顯示這樣的數據,我懷疑這取決於PostgreSQL的決定如何優化查詢,但它應該是一個很好的起點。

http://www.postgresql.org/docs/current/static/sql-explain.html

1

說明文檔與CROSS JOIN的問題是,它會產生任何輸出時,派生表的一個是空的:

with x as (select 1 where false), 
    y as (select 2 where true) 
select * from x, y; 

你需要像OUTER CROSS JOIN

SQL Server有很大OUTER APPLY

with x(c) as (select 1 where 1=0), 
    y(d) as (select 2 where 1=1) 
select * 
FROM (values ('Base')) AS s(val) -- always one row 
OUTER APPLY x 
OUTER APPLY y; 

LiveDemo

你可以模擬使用LEFT JOIN LATERAL這種行爲,但它看起來有點 「醜」:

;WITH x(c) AS (SELECT 1 WHERE false), 
     y(d) AS (SELECT 2 WHERE true) 
SELECT * 
FROM (VALUES ('Base row')) AS s(val) 
LEFT JOIN LATERAL (SELECT * FROM x) AS x(c) ON true 
LEFT JOIN LATERAL (SELECT * FROM y) AS y(d) ON true; 

SqlFiddleDemo

輸出:

╔═══════════╦═════════╦═══╗ 
║ val  ║ c  ║ d ║ 
╠═══════════╬═════════╬═══╣ 
║ Base row ║ (null) ║ 2 ║ 
╚═══════════╩═════════╩═══╝ 

或在這種情況下,簡單的LEFT JOIN

;WITH x(c) AS (SELECT 1 WHERE false), 
    y(d) AS (SELECT 2 WHERE true) 
SELECT * 
FROM (VALUES ('Base row')) AS s(val) 
LEFT JOIN x ON true 
LEFT JOIN y ON true; 
+1

我剛做了一個示例查詢,當然我知道它使0行。但該工具非常有趣,謝謝! –

+0

提示:'select * from x full join y on true' – Abelisto