Dlaczego nie mogę użyć instrukcji CASE, aby sprawdzić, czy kolumna istnieje, a nie WYBIERZ z niej?

17

Dlaczego coś takiego nie działa?

SELECT
CASE 
WHEN NULLIF(COL_LENGTH('Customers', 'Somecol'), '') IS NULL THEN NULL
ELSE Somecol
END AS MyTest
FROM Customers;

Sprawdzam tylko, czy kolumna istnieje, jednak SQL Server narzeka, że Somecolnie istnieje. Czy istnieje alternatywa dla tego w jednym stwierdzeniu?

Carson Reinke
źródło
3
Czy masz przykład, dlaczego chcesz to zrobić? Nie rozumiem, dlaczego chcesz napisać zapytanie, które próbuje wybrać z kolumny, która może nie istnieć.
Mark Sinkinson
4
Program SQL Server ocenia poprawność składni instrukcji przed jej wykonaniem. Dlatego wszystkie kolumny, do których istnieją odniesienia, muszą istnieć w tabelach, nawet jeśli są zawinięte w CASEinstrukcję.
Mark Sinkinson
@ MarkSinkinson: Nazwy są sprawdzane po składni, ale tak, SQL Server robi to przed uruchomieniem partii.
Andriy M,
1
Wybór z INFORMATION_SCHEMAmoże działać jako obejście.
Brilliand
4
@Brilliand sys.columns jest znacznie lepszy IMHO.
Aaron Bertrand

Odpowiedzi:

43

Poniższe zapytanie wykorzystuje ten sam pomysł, jak w tej niesamowitej odpowiedzi przez ypercube :

SELECT x.*
FROM (SELECT NULL AS SomeCol) AS dummy
CROSS APPLY
(
  SELECT
    ID,
    SomeCol AS MyTest
  FROM dbo.Customers
) AS x;

Działa to tak:

  • jeśli dbo.Customerszawiera kolumnę o nazwie SomeCol, a następnie SomeColw SomeCol AS MyTestrozwiąże jak dbo.Customers.SomeCol;

  • jeśli tabela nie ma takiej kolumny, odwołanie będzie nadal ważne, ponieważ teraz zostanie rozwiązane jako dummy.SomeCol: dummykolumny mogą być przywoływane w tym kontekście.

W ten sposób możesz podać wiele „zapasowych” kolumn. Sztuczka polega na tym, aby nie używać aliasu tabeli dla takich kolumn (co w większości sytuacji jest nieprzyzwoite, ale w tym przypadku pominięcie aliasu tabeli pomaga rozwiązać problem).

Jeśli tabela jest używana w złączeniu, a druga tabela ma własną SomeCol, prawdopodobnie będziesz musiał użyć powyższej kwerendy jako tabeli pochodnej przed użyciem jej w złączeniu, aby utrzymać sztuczkę w działaniu, mniej więcej tak:

SELECT ...
FROM
(
  SELECT x.*
  FROM (SELECT NULL AS SomeCol) AS dummy
  CROSS APPLY (
    SELECT
      ID,
      SomeCol AS MyTest
    FROM dbo.Customers
  ) AS x
) AS cust
INNER JOIN ...
;
Andriy M.
źródło
1
Zastanawiam się, czy kompilator SQL jest tylko trochę skomplikowany. Super fajne, co możesz zrobić.
Max Vernon
9

Jednym ze sposobów jest sprawdzenie istnienia kolumn, a następnie zbudowanie dynamicznego SQL na podstawie tego, czy kolumna istnieje, czy nie.

Bez dynamicznego SQL SQL Server będzie próbował ocenić, czy kolumna istnieje, zanim jeszcze wykona statystykę, co spowoduje błąd.

Oznacza to jednak, że będziesz musiał napisać 2 zapytania i potencjalnie zmienić je w przyszłości. Ale nie sądzę, że naprawdę powinieneś kierować SELECToświadczenia na kolumny, które mogą nie istnieć.

declare @SQL varchar(max)

If exists (select 1 from sys.columns where Name = N'NameOfColumn' and object_id=object_id(N'yourTableName'))
begin
set @SQL = 'select ID, NameOfColumn from yourTableName'
exec(@sql)
end
else
begin
Print 'Column does not exist'
end
Mark Sinkinson
źródło
Tak, ma to jednak sens, musi być w jednym stwierdzeniu. Ostatecznie szukam prawdopodobnie jakiejś funkcji magicznego systemu, która nie istnieje.
Carson Reinke,
4

Możesz skorzystać z niektórych plików XML, aby wyszukać kolumny, które mogą znajdować się w tabeli.

Zbuduj XML ze wszystkich kolumn w wierszu w krzyżowym zastosowaniu i wyodrębnij wartość, używając values() funkcji.

W tym zapytaniu znany jest identyfikator, więc pobierz go bezpośrednio z tabeli. Col1 i Col2 mogą tam być lub nie, więc zdobądź je za pomocą XML.

select T.ID,
       TX.X.value('(Col1/text())[1]', 'int') as Col1,
       TX.X.value('(Col2/text())[1]', 'int') as Col2
from T
  cross apply (select T.* for xml path(''), type) as TX(X)

SQL Fiddle

Mikael Eriksson
źródło
-1

Moje podejście różni się tylko nieznacznie od innych. Wolę do tego użyć systemu i po prostu uzyskać liczbę, ponieważ możesz przypisać liczbę kolumn do zmiennej u góry zapytania, a następnie wybrać kontynuowanie lub nie na podstawie tego. Minusem tego jest… jeśli masz tę samą nazwę kolumny w wielu tabelach, nie masz pewności, że kolumna istnieje w tabeli, którą chcesz zapytać. Jednak technika ta działa również na poszczególnych stołach, ponieważ chcesz tylko policzyć.

„Problem” z pytaniem o to w szczególności - problem, którego doświadczasz. Ogólnie, jeśli wartość NULL powoduje problemy… znajdź inny sposób weryfikacji istnienia. Jest to jeden ze sposobów, aby to zrobić bez ryzyka zakłócenia działania serwera.

SELECT COUNT(*) FROM sys.columns WHERE sys.columns.name = 'FarmID'
Jinzai
źródło
1
Dlaczego nie użyć sysobjectsrównież w zapytaniu, aby sprawdzić, czy konkretna tabela ma taką kolumnę?
ypercubeᵀᴹ
Tak… Wspomniałem, że można to zrobić… możesz nawet zrobić to samo w tabeli, o którą pytasz… Właśnie pokazałem ogólny format używania COUNT, ponieważ COUNT nie popełnia błędu, gdy COUNT to ZERO i… Przypuszczam, że powinienem wspomnij, że możesz także przypisać ją do zmiennej. (np. WYBIERZ LICZBA (*) JAKO moja_nazwa…)
jinzai
1
Nie rozumiem, jak byłoby to lepsze niż zapytanie Marka. SELECT 1 ...też nie popełnia błędu.
ypercubeᵀᴹ
Nie powiedziałem, że jest lepiej, ale jest to znacznie prostszy sposób na osiągnięcie tego samego rezultatu. WYBIERZ 1 może nie zawierać błędów, ale nie jest to to samo, co COUNT. WYBIERZ zwraca COŚ… nawet jeśli jest NULL. COUNT musi zwrócić tylko jeden numer. Ten sposób byłby szybszy i wspomniałem, że liczenia można użyć później.
jinzai
Jeśli potrzebujesz liczenia ok. Ale EXISTS (SELECT ...)zwykle jest szybszy niż (SELECT COUNT(*) ...)odwrotnie.
ypercubeᵀᴹ
-3

Jeśli dobrze to zrozumiałem ...

Możesz użyć zapytania podobnego do poniższego i działać odpowiednio na podstawie liczby ... Jeśli liczba wynosi> 1, oznacza to, że masz kolumnę w tej tabeli, a liczba = 0, to nie masz tej kolumny w tym stół

WYBIERZ liczbę (*)
OD INFORMACJI_SCHEMA.KOLUMNÓW, GDZIE KOLUMNA_NAME W („Id”)
ORAZ TABELA_SCHEMA = „dbo” i TABLE_NAME = „Baza użytkowników”;

Sai
źródło
4
Nie, nie zrozumiałeś poprawnie. Sprawdź także sprawę przeciwko poglądom INFORMACJE_SCHEMA autorstwa @AaronBertrand
Kin Shah