2016-04-15 3 views
3

Ich hatte ein Problem in einer Abfrage, wo einer der CTEs keine Zeilen zurückgegeben. Aber das war schwer zu bemerken, und das Debugging dauerte eine ganze Weile.Wie Debuggen allgemeiner Tabellenausdrücke in PostgreSQL?

Ist es möglich, alle CTEs in Postgres auszugeben, ohne die Hauptabfrage auskommentieren zu müssen?

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; 

Im Beispiel wurde centroid CTE auf die Funktion schrittweise hinzugefügt, die vor gut funktioniert. Es sollte Zeilen zurückgeben, falls grid leer ist. Der Fehler (den ich behoben habe) war, dass es nicht, weil es aus dem leeren CTE grid ausgewählt wurde. Jetzt, als ich das Problem beschrieben habe, ist es offensichtlich, warum es fehlgeschlagen ist, aber wenn Sie es schreiben und debuggen, können alle möglichen Dinge passieren, wie gemischte SRID-Geometrien, falsche SRID, usw.

+0

Warum würden Sie schreiben, so etwas in erster Linie, wenn Sie nicht wissen, was es tut? – joop

+0

@joop Wenn Sie versehentlich einen Fehler machen, wissen Sie immer, was Ihr Programm macht. Es ist ein kleines Detail, das Sie vermissen, dass die Dinge nicht funktionieren. Ich habe die echte Abfrage hinzugefügt, mit der ich gearbeitet habe. –

+1

'... aus dem Raster wo (Anzahl (*) = 0 aus dem Raster wählen))' ist nur schlechte Art, die vermieden werden kann, IMHO. [Ich weiß nicht einmal, ob es eine gültige Syntax ist; Ich würde ein 'NOT EXISTS()' Konstrukt hier IIUC das Fragment verwenden] – joop

Antwort

3

EXPLAIN ANALYSE versucht, die CTEs separat zu melden.

Wenn ich laufe, dass (Postgresql 9.4) wird die CTEs separat zeigt, und im Ergebnisteil es zeigt, dass die tatsächliche Anzahl der Zeilen von „CTE Scan auf x“ zurückkehrte, war 0.

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

Returns:

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 

ich weiß nicht, die erklären würde immer Daten wie diese angezeigt werden, ich vermute, es hängt davon ab, wie Postgresql entscheidet die Abfrage zu optimieren, aber es sollte ein guter Startpunkt sein.

Erklären Dokumentation http://www.postgresql.org/docs/current/static/sql-explain.html

1

Das Problem mit CROSS JOIN ist, dass es keine Ausgabe erzeugen würde, wenn ein abgeleiteter Tabellen leer ist:

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

Sie brauchen so etwas wie OUTER CROSS JOIN.

In SQL Server gibt es große 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

Sie dieses Verhalten LEFT JOIN LATERAL mit simulieren könnte, aber es sieht ein wenig "hässlich":

;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

Ausgang:

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

oder einfach LEFT JOIN in diesem Fall:

;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

Ich habe gerade eine Beispielabfrage gemacht, natürlich weiß ich, dass es 0 Zeilen macht. Aber das Tool ist sehr interessant, danke! –

+0

Hinweis: 'wähle * aus x full join y on true' – Abelisto