Warum tritt bei dieser Abfrage, bei der eine FROM-Klausel fehlt, kein Fehler auf?

9

Wir haben also eine Abfrage mit einer Unterabfrage, die einen Tippfehler enthält. Es fehlt die FROM-Klausel. Aber wenn Sie es ausführen, tritt kein Fehler auf! Warum!?


SELECT

    1
   ,r.id
   ,'0D4133BE-C1B5-4141-AFAD-B171A2CCCE56'
   ,GETDATE()
   ,1
   ,'Y'
   ,'N'
   ,oldItem.can_view
   ,oldItem.can_update

FROM Role r

JOIN RoleObject oldReport
    ON r.customer_id = oldReport.customer_id

JOIN RoleItem oldItem
    ON oldReport.id = oldItem.role_object_id
        AND r.id = oldItem.role_id

WHERE r.id NOT IN (SELECT
        role_id
    WHERE role_object_id = '0D4133BE-C1B5-4141-AFAD-B171A2CCCE56')

AND oldReport.id = '169BA22F-1614-4EBA-AF45-18E333C54C6C'
Wjdavis5
quelle

Antworten:

21

Diese Aussage ist legal (mit anderen Worten, nein FROMist erforderlich):

SELECT x = 1;
SELECT x = 1 WHERE 1 = 1; -- also try WHERE 1 = 0;

Der Trick ist, wenn Sie einen Spaltennamen einführen, der eindeutig nicht existieren kann. Also scheitern diese:

SELECT name WHERE 1 = 1;

SELECT x = 1 WHERE id > 0;

Meldung 207, Ebene 16, Status 1
Ungültiger Spaltenname 'Name'.
Meldung 207, Ebene 16, Status 1
Ungültiger Spaltenname 'id'.

Wenn die ungültige Spalte jedoch in eine Unterabfrage eingefügt wird, wird SQL Server, wenn es diese Spalte im inneren Bereich der Unterabfrage nicht findet, zu einem äußeren Bereich durchlaufen und die Unterabfrage mit diesem äußeren Bereich korrelieren lassen. Dies gibt alle Zeilen zurück, zum Beispiel:

SELECT * FROM sys.columns WHERE name IN (SELECT name WHERE 1 = 1);

Weil es im Wesentlichen heißt:

SELECT * FROM sys.columns WHERE name IN (SELECT sys.columns.name WHERE 1 = 1); /*
              ^^^^^^^^^^^                       -----------
                   |                                 |
                   -----------------------------------    */

Sie brauchen nicht einmal eine WHEREKlausel in der Unterabfrage:

SELECT * FROM sys.columns WHERE name IN (SELECT name);

Sie können sehen, dass es wirklich auf den äußeren Bereichstisch schaut, weil dies:

SELECT * FROM sys.columns WHERE name IN (SELECT name WHERE name > N'x');

Gibt weit weniger Zeilen zurück (11 auf meinem System).

Dies beinhaltet die Einhaltung des Standards für das Scoping. Sie können ähnliche Dinge sehen, wenn Sie zwei # temp-Tabellen haben:

CREATE TABLE #foo(foo int);
CREATE TABLE #bar(bar int);

SELECT foo FROM #foo WHERE foo IN (SELECT foo FROM #bar);

Offensichtlich sollte dies ein Fehler sein, oder, da es kein fooIn gibt #bar? Nee. Was passiert ist, dass SQL Server sagt: "Oh, ich habe hier keine foogefunden, Sie müssen die andere gemeint haben."

Auch im Allgemeinen würde ich vermeiden NOT IN. NOT EXISTShat das Potenzial, in einigen Szenarien effizienter zu sein, aber was noch wichtiger ist, sein Verhalten ändert sich nicht, wenn es möglich ist, dass die Zielspalte sein könnte NULL. Weitere Informationen finden Sie in diesem Beitrag .

Aaron Bertrand
quelle
Ich habe eine Frage zu Stack Overflow gestellt, auf die die Antwort im Wesentlichen dieselbe ist (obwohl Ihre gründlicher ist). Warum ist das Verweisen auf eine Spalte (als linker Operand), die nicht Teil der abgefragten Tabelle ist, kein Fehler im EXISTS-Operator?
März 2377
2

Ich habe dies 2016 mit einem vereinfachten Beispiel reproduziert:

declare @t1 table (c1 int, c2 int, c3 int)
insert into @t1 values (1,2,3), (2,3,4), (3,4,5)

select * from @t1
where
    c1 not in 
    (select c2 where c3 = 3)

Es scheint, dass c2 und c3 für jede Zeile ausgewertet werden.

Paulbarbin
quelle
1

In SQL Server erfordert die SELECT-Syntax keinen FROM-Abschnitt. Wenn Sie FROM weglassen, verwendet die select-Anweisung eine "Dummy" -Tabelle mit einer Zeile und keinen Spalten. So

select 'x' as c where ...

gibt eine Zeile zurück, wenn der Ausdruck wahr ist, und keine Zeilen, wenn er falsch ist.

Piotr
quelle
Aber das funktioniert nicht, wenn Sie nur sagen, select cund cexistiert nicht in einem äußeren Objekt. Ich bin damit einverstanden, dass dies FROMnicht erforderlich ist, aber die hier verwendeten Mechanismen, wenn Sie eine Spalte, die in einem äußeren Bereich vorhanden ist, explizit benennen, unterscheiden sich definitiv von einer Dummy-Tabelle, und wenn Sie keine Konstante für eine Spalte angeben, die dies nicht tut vorhanden, erhalten Sie einen Laufzeitfehler, also auch keine Dummy-Tabelle. Dummy-Tabellen können in anderen Szenarien ins Spiel kommen, jedoch nicht, wenn sich die Referenz in einer Unterabfrage / abgeleiteten Tabelle befindet.
Aaron Bertrand
In Ihrem Beispiel handelt es sich um eine korrelierte Unterauswahl. Role_id und role_object_id gehören zu einer der Tabellen in der äußeren Auswahl.
Piotr
Richtig, aber zu sagen SELECT 'x' AS cist ein völlig anderes Szenario als die OPs, die es gerade gesagt haben SELECT c. In einer Unterabfrage / abgeleiteten Tabelle.
Aaron Bertrand