Выберите columnValue, если столбец существует, иначе null

Мне интересно, могу ли я выбрать значение столбца, если столбец существует, и просто выбрать значение null в противном случае. Другими словами, я хотел бы «поднять» оператор select для обработки случая, когда столбец не существует.

SELECT uniqueId
    ,  columnTwo
    ,  /*WHEN columnThree exists THEN columnThree ELSE NULL END*/ AS columnThree
FROM (subQuery) s

Обратите внимание, что я нахожусь в процессе закрепления своей модели данных и дизайна. Я надеюсь исключить эту логику в ближайшие недели, но я действительно хотел бы выйти за рамки этой проблемы, потому что исправление модели данных требует больше времени, чем я хотел бы заняться сейчас.

Также обратите внимание, я хотел бы сделать это в одном запросе. Так что я не ищу ответ, как

сначала проверьте, какие столбцы находятся в вашем подзапросе. Затем измените свой запрос, чтобы соответствующим образом обрабатывать столбцы в вашем подзапросе.


person Steven Wexler    schedule 06.06.2013    source источник
comment
Почему вы пишете код, который предполагает, что ваша модель данных будет волей-неволей? Почему бы не написать код для столбцов, которые существуют сейчас, и когда ColumnThree станет постоянным первоклассным гражданином в вашей модели данных, исправить запрос? Также я настоятельно рекомендую не говорить людям, что вы не ищете. Ваше ограничение, как указано, невозможно выполнить, и вам нужно указать более вескую причину, чем эта. В одном запросе очень мало смысла, когда вы можете инкапсулировать вещи в хранимую процедуру.   -  person Aaron Bertrand    schedule 06.06.2013
comment
@AaronBertrand Вы задали два вопроса, поэтому я отвечу на оба. Во-первых, моя цель — как можно скорее выпустить качественную функцию. Прямо сейчас мне нужны значения из ColumnThree, когда они существуют, чтобы моя функция работала правильно. Поэтому я буду использовать этот столбец, когда он будет существовать... даже если решение не будет элегантным.   -  person Steven Wexler    schedule 06.06.2013
comment
@AaronBertrand Во-вторых, я ценю вашу рекомендацию, но с уважением не согласен. Я хочу четко сформулировать свои требования, чтобы пользователи могли сосредоточиться на предложении решений, которые решают мою проблему, не тратя свое время на написание ответов, которые, как я знаю, будут неудовлетворительными. Я думаю, что точное указание того, что не будет удовлетворительным ответом, поможет пользователям сосредоточиться на более достойных решениях.   -  person Steven Wexler    schedule 06.06.2013


Ответы (3)


Вы не можете сделать это с помощью простого оператора SQL. SQL-запрос не будет компилироваться, если в таблице не существуют все ссылки на таблицы и столбцы.

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

В динамическом SQL вы бы сделали что-то вроде:

declare @sql nvarchar(max) = '
SELECT uniqueId, columnTwo, '+
    (case when exists (select *
                       from INFORMATION_SCHEMA.COLUMNS 
                       where tablename = @TableName and
                             columnname = 'ColumnThree' -- and schema name too, if you like
                      )
          then 'ColumnThree'
          else 'NULL as ColumnThree'
     end) + '
FROM (select * from '+@SourceName+' s
';

exec sp_executesql @sql;

Для реального подзапроса вы можете приблизиться к тому же самому, проверив, возвращает ли подзапрос что-то с этим именем столбца. Один из способов сделать это — запустить запрос: select top 0 * into #temp from (<subquery>) s, а затем проверить столбцы в #temp.

РЕДАКТИРОВАТЬ:

Обычно я не обновляю такие старые вопросы, но на основе комментария ниже. Если у вас есть уникальный идентификатор для каждой строки подзапроса, вы можете запустить следующее:

select t.. . .,  -- everything but columnthree
       (select column3   -- not qualified!
        from t t2
        where t2.pk = t.pk
       ) as column3
from t cross join
     (values (NULL)) v(columnthree);

Подзапрос выберет column3 из внешнего запроса, если он не существует. Однако это критически зависит от наличия уникального идентификатора для каждой строки. Вопрос явно о подзапросе, и нет оснований ожидать, что строки легко однозначно идентифицируются.

person Gordon Linoff    schedule 06.06.2013
comment
Я так не думаю. Из любопытства, как бы я сделал это с динамическим SQL? - person Steven Wexler; 06.06.2013
comment
Вы строите свой оператор sql как строку, а затем используете команду exec для выполнения созданной вами строки. - person liebs19; 06.06.2013
comment
И, насколько я понимаю динамический sql, разве для этого не потребуется два запроса? - person Steven Wexler; 06.06.2013
comment
Правильно, вам понадобится запрос, чтобы увидеть, существует ли поле, и если оно существует, добавьте строку к вашему динамическому запросу, который вы затем сможете выполнить. - person liebs19; 06.06.2013
comment
Хорошо, я понимаю, что вы имеете в виду из вашего обновления. Это в основном то, что было моей первоначальной мыслью. Я планировал не использовать динамический sql и вместо этого создавать свой запрос на С#. Однако я надеялся избежать первого запроса, определяющего столбцы. Я думаю, этого не избежать... - person Steven Wexler; 06.06.2013
comment
@Gordon +1, хотя это можно сделать (см. мой ответ с CROSS APPLY) - person ypercubeᵀᴹ; 23.02.2017
comment
Классический пример того, почему никогда нельзя утверждать что-то, чего точно делать нельзя! Вы действительно говорите, что не можете придумать, как это можно сделать, но, возможно, есть другой способ, о котором вы не можете подумать. - person Arthur Tacca; 28.01.2021
comment
@АртурТакка. . . Я расширил ответ. Я придерживаюсь первой части, потому что вопрос довольно общий и есть случаи, когда он не может быть решен. Во многих (практических) случаях для этого существуют методы. - person Gordon Linoff; 28.01.2021

Как уже предлагали другие, разумный подход состоит в том, чтобы иметь запросы, соответствующие дизайну вашей таблицы.

Однако существует довольно экзотический подход для достижения того, чего вы хотите в (чистом, а не динамическом) SQL. Аналогичная проблема была опубликована на DBA.SE: Как выбрать определенные строки, если столбец существует, или все строки, если столбца нет, но это было проще, поскольку в результате требовалась только одна строка и один столбец. Ваша проблема более сложная, поэтому запрос более запутанный, если не сказать больше. Вот безумный подход:

; WITH s AS
  (subquery)                                    -- subquery
SELECT uniqueId
    ,  columnTwo
    ,  columnThree =
       ( SELECT ( SELECT columnThree 
                  FROM s AS s2
                  WHERE s2.uniqueId = s.uniqueId
                ) AS columnThree
         FROM (SELECT NULL AS columnThree) AS dummy
       )
FROM s ;

Также предполагается, что uniqueId уникален в результирующем наборе подзапроса.

Протестировано в SQL-Fiddle.


И более простой метод, который имеет дополнительное преимущество, позволяющее использовать более одного столбца с одним подзапросом:

SELECT s.*     
FROM
    ( SELECT NULL AS columnTwo,
             NULL AS columnThree,
             NULL AS columnFour
    ) AS dummy 
  CROSS APPLY
    ( SELECT 
          uniqueId,
          columnTwo,
          columnThree,
          columnFour
      FROM tableX
    ) AS s ;

Этот вопрос также был задан на DBA.SE, и на него ответил @Andriy M (тоже используя CROSS APPLY !) и Майкл Эрикссон (используя XML):
Почему я не могу использовать оператор CASE, чтобы узнать, существует ли столбец, а не SELECT из него?

person ypercubeᵀᴹ    schedule 11.07.2013
comment
Это очень круто! Есть ли способ заставить это работать с несколькими столбцами, но без дополнительных объединений? - person surjikal; 23.02.2017
comment
Спасибо за быстрый ответ! К сожалению, я использую SAP Hana, и у него нет CROSS APPLY. Можете ли вы придумать альтернативное решение? Я нашел этот stackoverflow.com/questions/26020765 / но я не уверен, что это сработает. - person surjikal; 23.02.2017
comment
@surjikal Я не понимаю, как этот ответ сработает. Я не знаком с SAP Hana, поэтому не могу ответить. Какую версию вы используете и какая СУБД за ней стоит? СУБД называется SAP Hana или она общается с другой СУБД. Если вы можете найти документацию по SQL вашего продукта, это поможет. Некоторые базы данных имеют CROSS/OUTER APPLY, другие имеют LATERAL соединения, которые похожи. Вы также можете опубликовать другой вопрос с тегом SAP Hana (либо здесь, либо предпочтительно на dba.stackexchange.com). Включите ссылку на этот вопрос, если да! - person ypercubeᵀᴹ; 23.02.2017
comment
@ypercubeᵀᴹ . . . Это очень умно, используя правила области видимости для обработки несуществующих столбцов. Слава! - person Gordon Linoff; 23.02.2017
comment
SAP Hana — это СУБД, и, похоже, она не поддерживает расширения SQL. У него самые обычные соединения, но, к сожалению, нет соединений CROSS/OUTER APPLY или LATERAL. Документация находится здесь: help-legacy.sap.com /saphelp_hanaplatform/helpdata/ru/20/ - person surjikal; 23.02.2017
comment
@surjikal <collection_derived_table> (UNNEST), кажется, обеспечивает аналогичную функциональность. Попробуйте преобразовать CROSS APPLY, используя это. - person ypercubeᵀᴹ; 23.02.2017
comment
Отлично, это здорово, спасибо за подсказку! Я сообщу о своих выводах. - person surjikal; 23.02.2017
comment
@surjikal, если вы выполнили эту работу в SAP Hana, не стесняйтесь редактировать ответ и добавлять код. - person ypercubeᵀᴹ; 27.02.2018
comment
@ypercubeᵀᴹ Я этого не делал :( - person surjikal; 28.02.2018
comment
Прекрасное решение! - person user732456; 23.10.2019
comment
Отличное решение, безусловно, самое простое и изящное, большое спасибо, правда - person Joachim; 16.07.2020

вы можете использовать динамический SQL.

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

DECLARE @query NVARCHAR(MAX) = '
SELECT FirstColumn, SecondColumn, '+
  (CASE WHEN exists (SELECT 1 FROM syscolumns 
  WHERE name = 'ColumnName' AND id = OBJECT_ID('TableName'))
      THEN 'ColumnName'
      ELSE 'NULL as ThreeColumn'
   END) + '
FROM TableName'

EXEC sp_executesql @query;
person Reza Jenabi    schedule 23.09.2019