2013-02-18 8 views
5

Wenn ich habe folgendes Spielzeug AbfrageUnterstützt Postgresql plpgsql/sql in der Where-Klausel einen Kurzschluss?

SELECT * 
FROM my_tables 
WHERE my_id in (
    SELECT my_other_id 
    FROM my_other_tables 
) AND some_slow_func(arg) BETWEEN 1 AND 2; 

Würde die erste Bedingung in dem Kurzschluss WHERE-Klausel der zweite Bedingung, die eine komplexe Laufzeit haben würde?

Ich arbeite an einigen SQL, die tatsächlich Teil eines FOR LOOP in Plpgsql ist, und ich könnte Iterationen über alle Datensätze, die in den my_other_tables existieren, und dann im Rahmen der FOR LOOP mit der some_slow_func (). Aber ich bin gespannt, ob SQL unterstützt, oder plpgsql unterstützt Kurzschlüsse.

Einige Forschung: sah ich in den Postgres Mailinglisten und fand dies im allgemeinen sagen, SQL nicht Kurzschlüsse unterstützt:

http://www.postgresql.org/message-id/[email protected]

Aber eine der Antworten sagt, dass, um durch durchgesetzt werden kann Subselects. Ich bin mir nicht sicher, wovon er spricht. Ich weiß, was ein Subselect ist, aber ich bin mir nicht sicher, wie die Order durchgesetzt wird. Könnte jemand das für mich klären?

+0

Ich denke nicht, dass ein Kurzschluss relevant ist; SQL soll satzorientiert sein und das Ergebnis sollte nicht von der Reihenfolge der Auswertung abhängen. Eine Ausnahme hiervon könnte * eine UNION von zwei Unterabfragen sein, sowohl mit einem LIMIT als auch einem zusätzlichen LIMIT für die Abfrage als Ganzes. Aber LIMIT ist sowieso grenzwertig ... Nebenwirkungen einer Evaluation sollten in einem wirklich relationalen RSBMS nicht möglich sein (vielleicht außer LATERAL).Kurz gesagt: Die Reihenfolge der Auswertung beeinflusst nur die Leistung und nicht (die Richtigkeit der) Ergebnisse, IMHO. Deshalb sollten wir die Reihenfolge der Auswertung dem Planer überlassen. – wildplasser

Antwort

6

Wie dokumentiert, ist die Bewertungsreihenfolge in einer WHERE-Klausel nicht vorhersehbar.

Bei Unterabfragen ist das anders. Bei den aktuellen Versionen besteht die einfachste und gebräuchlichste Methode zur Steuerung der Evaluierungsreihenfolge darin, eine Unterabfrage in einem CTE zu schreiben. Um sicherzustellen, dass die IN(...) zuerst ausgewertet wird, könnte den Code geschrieben werden als:

WITH subquery AS 
(select * from my_tables 
    WHERE my_id in (SELECT my_other_id FROM my_other_tables) 
) 
SELECT * FROM subquery 
    WHERE some_slow_func(arg) BETWEEN 1 AND 2; 

etwas anderes, das Sie optimieren können, ist die Kosten Ihrer Funktion des Optimierer zu signalisieren, dass es langsam ist. Die Standardkosten für eine Funktion sind 100, und es kann wie mit einer Aussage geändert werden:

ALTER FUNCTION funcname(argument types) cost N; 

wo N ist die geschätzten per-Call-Kosten, in einer willkürlichen Einheit ausgedrückt, die die Planner Cost Constants verglichen werden soll.

+0

Ja, mit WITH wäre ein guter schneller Weg, um den Satz zu begrenzen. Ich bin mir nicht sicher, warum ich nicht darüber nachgedacht habe. Ich liebe es, WITH zu benutzen. Auch nicht daran gedacht, die Kosten für den Planer zu ändern. Ich wusste davon, aber ich habe immer Postgreten auf die Optimierung achten lassen. Ich habe es vorher noch nie benutzt, werde aber mehr dazu lesen. Danke, dass Sie mich darauf aufmerksam gemacht haben! – enigmasck

+0

Sie können natürlich auch 'COST N' angeben, wenn Sie die Funktion definieren. – jpmc26

0

Nach the Postgresql docs und this answer by Tom Lane, die Reihenfolge der Ausführung der WHERE Einschränkungen ist nicht zuverlässig.

Ich denke, Ihre beste Wette hier könnte sein, diesen anderen Teil Ihrer WHERE-Klausel an den Anfang Ihrer Funktion hinzuzufügen und "fail fast"; dh, führen Sie in Ihrer Funktion my_id in ( SELECT my_other_id FROM my_other_tables) aus, und wenn es nicht besteht, kehren Sie direkt dorthin zurück, bevor Sie intensiv arbeiten. Das sollte dir ungefähr den gleichen Effekt bringen.

+0

Danke für die Referenzen. Ich habe den ganzen Tag die Postgresql-Dokumentation angeschaut und bin auf diese Erklärung nicht gestoßen. Ich schätze Ihre schnelle Antwort. – enigmasck

+0

Kein Problem. Ein Tipp - wenn Sie sich diese Foren ansehen, gibt es unten gewöhnlich Abschnitte für "In Response To" und "Responses". Wenn Sie Links unter Antworten folgen, erhalten Sie wahrscheinlich weitere Informationen. In diesem Fall folgte ich lediglich der Antwort von Tom Lane auf den Posten, den Sie im OP verknüpft hatten. –

2

Ich weiß, dass dies eine alte Frage ist, aber vor kurzem in ähnliches Problem lief, und die Verwendung eines CASE-Prädikat in der WHERE-Klausel funktionierte besser für mich. Im Zusammenhang mit der obigen Antwort:

SELECT * 
    FROM my_tables 
WHERE CASE WHEN my_id in (SELECT my_other_id 
          FROM my_other_tables) 
      AND some_slow_func(arg) BETWEEN 1 AND 2 
      THEN 1 
      ELSE 0 
     END = 1; 

Dies macht für SQL, die etwas mehr DB-agnostisch ist. Natürlich kann es keine Indizes verwenden, wenn Sie einige auf my_id haben, aber abhängig von dem Kontext, in dem Sie sich befinden, könnte dies eine gute Option sein.

Verwandte Themen