2015-12-16 12 views
5

(przepraszam z góry za okropnego wyjaśnienia, ale jeśli uruchomić zapytań Poniżej powinien zobaczyć, co mam na myśli!)Dlaczego SQL ocenia wyciągi w prawdziwej sekcji konstruktu if, nawet jeśli `if exists` zwraca false?

Dlaczego MSSQL oceny sprawozdań w sekcji true wystąpienia if exists konstruktem, nawet jeśli if exists powraca false, powodując błędy?

Na przykład w dwóch poniższych zapytaniach pierwsza sprawdza, czy tabela istnieje (co robi), a także sprawdza, czy ta tabela zawiera określone kolumny. Z jakiegoś powodu uruchomienie tej kwerendy powoduje następujące błędy, ponieważ tabela istnieje, ale kolumny nie.

Msg 207, Level 16, State 1, Line 21 
Invalid column name 'colB'. 
Msg 207, Level 16, State 1, Line 21 
Invalid column name 'colC'. 
Msg 207, Level 16, State 1, Line 21 
Invalid column name 'colA'. 

Zachowanie Spodziewałem tutaj było dla SQL po prostu przenieść na falsepart konstruktu, bez rzucania błędów. (Podobnie jak w przypadku następnego zapytania).

Jednak drugi skrypt (który jest identyczny, nazwy tablic słupkowych) działa poprawnie. Dzieje się tak dlatego, że tabela, której szuka zapytanie, nie istnieje.

--Scripts to setup the example. 
CREATE DATABASE TEST 
GO 
USE TEST 
GO 
CREATE TABLE t1 (colD VARCHAR(255)) --Create a table with the correct name, but incorrect column names. 
GO 

--This query fails, because t1 exists, even though the columns in t1 don't. 
IF EXISTS (select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1' AND COLUMN_NAME IN ('colA','colB','colC')) 
BEGIN 
    SELECT colA FROM t1 WHERE colB = 0 AND colC = 1 
END 
ELSE BEGIN 
    SELECT 'FALSE' 
END 

GO 

--This query executes ok, because t2 does not exist. 
IF EXISTS (select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't2' AND COLUMN_NAME IN ('colA','colB','colC')) 
BEGIN 
    SELECT colA FROM t2 WHERE colB = 0 AND colC = 1 
END 
ELSE BEGIN 
    SELECT 'FALSE' 
END 

Czy ktoś może mi wyjaśnić, dlaczego błędy pierwszego zapytania, gdy drugie zapytanie działa poprawnie?

Do tej pory udało mi się jedynie do sprawdzenia tego w Microsoft SQL Server 2012.

+4

Nie udało się * skompilować * partii, nawet nie udało się uzyskać * uruchomienia * kodu. Rozważmy, w języku C#, czy masz ładunek wywołań odbicia, aby sprawdzić, czy dana klasa zawiera konkretną metodę. Nie możesz po prostu mieć tych kontroli w 'if', a następnie w treści' if' mieć linię, która * wywołuje * tę metodę na instancji klasy - z tego samego powodu - nie powiedzie się * skompilować*. –

+0

@Damien_The_Unbeliever, Rozumiem, co masz na myśli, ale jeśli nie uda się skompilować partii, ponieważ * niektóre * obiektów nie istnieją, dlaczego drugie zapytanie uruchamia się, gdy * none * obiektu istnieje? – KidCode

+4

Sytuacja z tabelą jest nieco dziwniejsza, ale dzieje się tak z powodu [rozpoznawania nazw odroczonych] (https://technet.microsoft.com/en-us/library/ms190686 (v = sql.105) .aspx) (jedno miejsce gdzie SQL celowo odkłada kompilację, aż do wyciągnięcia tego oświadczenia) –

Odpowiedz

4

Aby odpowiedzieć na pierwszą część tego pytania. Zakładając znajomość języka (takiego jak C#), który ma jakąś formę inspekcji typu środowiska wykonawczego (np. Odbicie).

Załóżmy masz kod tak:

SomeType t = GetSomeTypeFromSomewhere(); 
if(t.GetType().GetMethod("FunTimes")!=null) 
{ 
    t.FunTimes(); 
} 

i zakładamy, że SomeTypenie zawierać metodę publiczną o nazwie FunTimes. Mimo że napisałem strażnika próbującego wywołać metodę FunTimes, pojawia się błąd. I, konkretnie, dostaję kompilować błąd czasu - kompilator C# nie może nawet wygenerować kodu, nie mówiąc już o zbliżeniu się do uruchomienia kodu, uzyskując wynik z GetMethod() i decydując się nie uruchamiać kodu w zagnieżdżonym bloku.

Aby przełączyć się z powrotem do kodu, dokładnie taki sam rodzaj analizy stosuje się tutaj:

IF EXISTS (select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1' AND COLUMN_NAME IN ('colA','colB','colC')) 
BEGIN 
    SELECT colA FROM t1 WHERE colB = 0 AND colC = 1 
END 
ELSE BEGIN 
    SELECT 'FALSE' 
END 

SQL Server próbuje skompilować tę partię i kończy się niepowodzeniem. Nigdy nie wykonuje kodu, więc nigdy nie dojdzie do momentu podjęcia decyzji, którą gałąź (IF lub ELSE) podjąć.

Jeśli więc powyższe stwierdzenie jest prawdziwe, dlaczego działa drugi fragment kodu? Wynika to z określonej cechy T-SQL o nazwie Deferred Name Resolution. Zasadniczo istnieje specjalna reguła, która ma zastosowanie, gdy brakujący obiekt to tabela (lub widok, ponieważ te dwa elementy są nieodróżnialne, dopóki obiekt nie zostanie znaleziony). W tym konkretnym przypadku SQL Server nie będzie natychmiast sygnalizować błędu kompilacji.

Pod nazwą rozpoznawania nazw odroczonych rozpocznie się wykonywanie, a jeśli coś spowoduje zmiany schematu (na przykład przez dodanie brakującej tabeli/widoku), spowoduje to, że system ponownie skompiluje pozostałą część kodu.

0

myślę, że jesteś oceny wyników źle (i to nie jest twoja wina IMHO).

EXISTS część zwraca FAŁSZ w obu przypadkach. Jednak parser zapytań SQL jest zabawny, analizuje wewnętrzne wyrażenia i podaje błąd przed wykonaniem instrukcji, tylko jeśli brakuje kolumn (kolumn), nie daje błędu, jeśli brakuje tabeli.

W pierwszym zapytaniu, w którym wygląda na PRAWDA, spróbuj zmienić nazwę tabeli na coś podobnego do t2, a zobaczysz, że działa i ma wartość FALSE w obu.

Powiązane problemy