2016-04-15 43 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