Чому я не можу використовувати оператор CASE, щоб перевірити, чи існує стовпець, а не ВИБІРИ з нього?


17

Чому щось подібне не працює?

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

Я просто перевіряю, чи існує стовпець, однак SQL Server скаржиться на те, що Somecolне існує. Чи є альтернатива цьому в одній заяві?


3
Чи є у вас приклад, чому ви хотіли б це зробити? Я не можу зрозуміти, чому ви хочете написати запит, який намагається вибрати зі стовпця, який може не існувати.
Марк Сінкінсон

4
SQL Server оцінює, що ваш синтаксис оператора правильний перед його виконанням. Тому всі стовпці, на які посилається, повинні існувати в таблицях, навіть якщо вони зафіксовані в CASEоператорі.
Марк Сінкінсон

@MarkSinkinson: Імена перевіряються після синтаксису, але так, SQL Server робить це перед тим, як реально запустити пакет.
Андрій М

1
Вибір з INFORMATION_SCHEMAможе працювати як спосіб вирішення.
Brilliand

Відповіді:


43

Наступний запит використовує ту ж ідею, що і в цьому дивовижному відповідь по ypercube :

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

Це працює так:

  • якщо dbo.Customersстовпець названий SomeCol, то SomeColв розділі SomeCol AS MyTestбуде вирішено як dbo.Customers.SomeCol;

  • якщо в таблиці немає такого стовпця, посилання все одно буде дійсним, тому що тепер воно буде вирішено як dummy.SomeCol: dummyстовпці можуть бути посиланнями в цьому контексті.

Ви можете вказати кілька "запасних" стовпців таким чином. Хитрість полягає в тому, щоб не використовувати псевдонім таблиці для таких стовпців (що в більшості ситуацій нахмурене, але в цьому випадку пропуск псевдоніма таблиці допомагає вирішити проблему).

Якщо таблиця використовується при з'єднанні, а інша таблиця має свою власну SomeCol, вам, ймовірно, потрібно буде використовувати вищезазначений запит як похідну таблицю перед тим, як використовувати його в з'єднанні, щоб зберегти трюк, як-от так:

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 ...
;

1
Цікаво, чи компілятор SQL - це просто крихітний склад. Супер круто, що ти можеш зробити.
Макс Вернон

9

Один із способів зробити це - перевірити наявність стовпців, а потім створити Dynamic SQL на основі того, існує цей стовпець чи ні.

Без Dynamic SQL, SQL Server намагатиметься оцінити, чи існує стовпець до того, як він навіть виконає встановлення, що призведе до помилки.

Це, однак, означає, що у вас буде два запити для запису та потенційні зміни в майбутньому. Але я не вірю, що ви дійсно повинні орієнтувати SELECTзаяви на стовпці, які можуть не існувати.

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

Так, має сенс, однак, повинен бути в одній заяві. Зрештою, я шукаю, ймовірно, якусь функцію магічної системи, якої не існує.
Карсон Рейнке

4

Ви можете використовувати деякі XML для запиту стовпців, які можуть бути в таблиці.

Створіть XML з усіх стовпців у рядку хрестом і застосуйте та витягніть значення за допомогою values()функції.

У цьому запиті відомий ідентифікатор запиту, тому отримуйте його безпосередньо з таблиці Col1 і Col2 можуть бути там чи не дуже, отримуйте їх за допомогою 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


-1

Мій підхід лише незначно відрізняється від інших. Я вважаю за краще використовувати систему для цього і просто отримати підрахунок, тому що ви можете призначити кількість стовпців змінній у верхній частині запиту, а потім вибрати, чи продовжувати це чи ні, не грунтуючись на цьому. Недоліком цього є… якщо у вас є одне ім’я стовпця у кількох таблицях, ви не впевнені, що стовпець існує у таблиці, яку ви хочете запитувати. Однак методика працює і на окремих таблицях, оскільки ви тільки шукаєте, щоб отримати кількість.

"Проблема" з проханням конкретно про це - проблема, яку ви відчуваєте. Загалом, якщо значення NULL викликає у вас проблеми ... знайдіть інший спосіб перевірити існування. Це один із способів зробити це, не ризикуючи засмутити сервер.

SELECT COUNT(*) FROM sys.columns WHERE sys.columns.name = 'FarmID'

1
Чому б не скористатися відповідним sysobjectsзапитом у вашому запиті, щоб перевірити, чи є в конкретній таблиці такий стовпець?
ypercubeᵀᴹ

Так ... я згадав, що це можна зробити ... ви навіть можете зробити те ж саме в таблиці, про яку ви запитуєте ... Я щойно показав загальний формат використання COUNT, оскільки COUNT не помиляється, коли COUNT є ZERO і ... я вважаю, що я повинен згадайте, що ви можете призначити його і змінної. (напр. ВИБРАТИ КУХНУ (*) AS myVarName…)
jinzai

1
Я не бачу, як це було б краще, ніж запит Марка. SELECT 1 ...також не помиляється.
ypercubeᵀᴹ

Я не сказав, що це краще, але це набагато простіший спосіб досягти того ж результату. SELECT 1 може не помилитися, але це не те саме, що і COUNT. SELECT повертає НЕЩО… навіть якщо це NULL. COUNT має повернути лише одне число. Цей шлях буде швидшим, і я зазначив, що підрахунок можна використовувати пізніше.
jinzai

Якщо вам потрібен підрахунок добре. Але EXISTS (SELECT ...)зазвичай швидше, ніж (SELECT COUNT(*) ...)навпаки.
ypercubeᵀᴹ

-3

Якби я правильно це зрозумів ...

Ви можете використовувати запит на кшталт нижче та діяти відповідно до підрахунку ... Якщо кількість рахунку> 1, то це означає, що ви маєте col у цій таблиці, а count = 0, тоді у вас немає цього кола стіл

ВИБРАТИ кількість (*)
ІНФОРМАЦІЯ_SCHEMA.COLUMNS, де COLUMN_NAME В ('Id')
І TABLE_SCHEMA = 'dbo' і TABLE_NAME = 'UserBase';


4
Ні, ви правильно не зрозуміли. Також перевірте справу проти INFORMATION_SCHEMA поглядів від @AaronBertrand
Kin Shah
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.