Почему я не могу использовать оператор CASE, чтобы увидеть, существует ли столбец, а не SELECT из него?

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
4
@Brilliand sys.columns намного лучше ИМХО.
Аарон Бертран

Ответы:

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

Один из способов сделать это - проверить существование столбцов, а затем построить динамический SQL на основе того, существует этот столбец или нет.

Без динамического SQL SQL Server будет пытаться оценить, существует ли столбец, прежде чем он даже выполнит определение, что приведет к ошибке.

Это, однако, означает, что вам нужно будет написать 2 запроса и, возможно, изменить их в будущем. Но я не верю, что вы должны нацеливаться на 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'
jinzai
источник
1
Почему бы не использовать sysobjectsтакже в вашем запросе, чтобы проверить, есть ли в конкретной таблице такой столбец?
ypercubeᵀᴹ
Да ... я упомянул, что это можно сделать ... вы могли бы сделать то же самое для конкретной таблицы, к которой вы обращаетесь ... Я просто показал общий формат для использования COUNT, потому что COUNT не выдает ошибку, когда COUNT равен нулю и ... я полагаю, что я должен упомяните, что вы также можете назначить его переменной. (например, ВЫБЕРИТЕ COUNT (*) КАК myVarName…)
jinzai
1
Я не вижу, как это будет лучше, чем запрос Марка. SELECT 1 ...тоже не ошибка.
ypercubeᵀᴹ
Я не говорил, что это лучше, но это гораздо более простой способ добиться того же результата. ВЫБОР 1 может не быть ошибкой, но это не то же самое, что COUNT. SELECT возвращает что-то ... даже если это NULL. COUNT должен вернуть только один номер. Этот способ будет быстрее, и я упомянул, что счет может быть использован позже.
Джинзай
Если вам нужно считать хорошо. Но EXISTS (SELECT ...)обычно быстрее (SELECT COUNT(*) ...), а не наоборот.
ypercubeᵀᴹ
-3

Если я правильно понял ...

Вы можете использовать запрос, подобный приведенному ниже, и действовать соответствующим образом, основываясь на количестве ... Если счетчик> 1, то это означает, что у вас есть col в этой таблице, а count = 0, тогда у вас нет этого col в этом стол

SELECT count (*)
FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME IN ('Id')
И TABLE_SCHEMA = 'dbo' и TABLE_NAME = 'UserBase';

Sai
источник
4
Нет, вы не правильно поняли. Также проверьте Дело против представлений INFORMATION_SCHEMA от @AaronBertrand
Кин Шах