67

I'm wondering if I can select the value of a column if the column exists and just select null otherwise. In other words I'd like to "lift" the select statement to handle the case when the column doesn't exist.

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

Note, I'm in the middle to solidifying my data model and design. I hope to exclude this logic in the coming weeks, but I'd really like to move beyond this problem right because the data model fix is a more time consuming endeavor than I'd like to tackle now.

Also note, I'd like to be able to do this in one query. So I'm not looking for an answer like

check what columns are on your sub query first. Then modify your query to appropriately handle the columns on your sub query.

Steven Wexler
  • 16,589
  • 8
  • 53
  • 80
  • 1
    Why are you writing code that assumes your data model will be willy-nilly? Why not code for the columns that exist now, and when ColumnThree becomes a permanent first-class citizen in your data model, fix the query? Also I strongly recommend not telling people what you're not looking for. Your restriction as stated is not possible to meet, and you need to give a better reason than that. There is very little meaning to "in one query" when you can encapsulate things in a stored procedure. – Aaron Bertrand Jun 06 '13 at 04:01
  • 2
    @AaronBertrand You asked two questions, so I'll address both. First, my goal is to ship a high quality feature as soon as possible. Right now I need values from `ColumnThree` when they exist to make my feature work correctly. So I'm going to use that column when it exists...even if the solution is not elegant. – Steven Wexler Jun 06 '13 at 05:20
  • 14
    @AaronBertrand Second, I appreciate your recommendation but respectfully disagree. I want to express my requirements clearly so users can focus on suggesting solutions that solve my problem without wasting their time by writing answers I know will be unsatisfactory. I think stating exactly what won't be a satisfactory answer helps users focus on more worth while solutions. – Steven Wexler Jun 06 '13 at 05:21

3 Answers3

37

You cannot do this with a simple SQL statement. A SQL query will not compile unless all table and column references in the table exist.

You can do this with dynamic SQL if the "subquery" is a table reference or a view.

In dynamic SQL, you would do something like:

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;

For an actual subquery, you could approximate the same thing by checking to see if the subquery returned something with that column name. One method for this is to run the query: select top 0 * into #temp from (<subquery>) s and then check the columns in #temp.

EDIT:

I don't usually update such old questions, but based on the comment below. If you have a unique identifier for each row in the "subquery", you can run the following:

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

The subquery will pick up column3 from the outer query if it doesn't exist. However, this depends critically on having a unique identifier for each row. The question is explicitly about a subquery, and there is no reason to expect that the rows are easily uniquely identified.

Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
  • I don't think so. Out of curiosity, how would I do this with dynamic SQL? – Steven Wexler Jun 06 '13 at 01:47
  • You build your sql statement as a string and then use the exec command to execute the string you built up. – liebs19 Jun 06 '13 at 01:49
  • And from my understanding of dynamic sql, wouldn't that require two queries? – Steven Wexler Jun 06 '13 at 01:49
  • Right, you would need a query to see if the field exists and if it does, append a string to your dynamic query that you can then execute. – liebs19 Jun 06 '13 at 01:58
  • Ok, I see what you mean from your update. That's basically what my initial thought was. I was planning on not using dynamic sql and instead constructing my query in C#. However, the first query that determines the columns is what I was hoping to avoid. I guess it can't be avoided... – Steven Wexler Jun 06 '13 at 02:00
  • 2
    @Gordon +1 although it **can be done** (see my answer with `CROSS APPLY`) – ypercubeᵀᴹ Feb 23 '17 at 01:39
  • A classic example of why you should never assert something definitely can't be done! You're really saying you can't think of a way it can be done, but maybe there's another way you can't think of. – Arthur Tacca Jan 28 '21 at 09:31
  • @ArthurTacca . . . I extended the answer. I stand by the first part because the question is quite general and there are cases where it cannot be solved. In many (practical) cases, there are methods to accomplish this. – Gordon Linoff Jan 28 '21 at 17:31
30

As others already suggested, the sane approach is to have queries that meet your table design.

There is a rather exotic approach to achieve what you want in (pure, not dynamic) SQL though. A similar problem was posted at DBA.SE: How to select specific rows if a column exists or all rows if a column doesn't but it was simpler as only one row and one column was wanted as result. Your problem is more complex so the query is more convoluted, to say the least. Here is, the insane approach:

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

It also assumes that the uniqueId is unique in the result set of the subquery.

Tested at SQL-Fiddle


And a simpler method which has the additional advantage that allows more than one column with a single subquery:

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 ;

The question has also been asked at DBA.SE and has been answered by @Andriy M (using CROSS APPLY too!) and Michael Ericsson (using XML):
Why can't I use a CASE statement to see if a column exists and not SELECT from it?

Community
  • 1
  • 1
ypercubeᵀᴹ
  • 113,259
  • 19
  • 174
  • 235
  • 1
    This is pretty cool! Is there a way to make this work with multiple columns, but without doing more joins? – surj Feb 23 '17 at 01:19
  • Thanks for the quick reply! Unfortunately, I'm using SAP Hana and it doesn't have `CROSS APPLY`. Can you think of an alternative solution? I found this https://stackoverflow.com/questions/26020765/sap-hana-alternative-for-cross-apply but I'm not sure if it will work. – surj Feb 23 '17 at 02:50
  • @surjikal I don't see how that answer would work. I'm not familiar with SAP Hana so I can't answer. Which version are you using and what's the DBMS behind it? Is the DBMS called SAP Hana or does it talk to another dbms. If you can find the SQL documentation of your product, it would help. Some databases have CROSS/OUTER APPLY, other have `LATERAL` joins which are similar. You can also post another question, tagged with SAP Hana (either here or preferably at dba.stackexchange.com ). Include a link to this question, if you do! – ypercubeᵀᴹ Feb 23 '17 at 03:03
  • 5
    @ypercubeᵀᴹ . . . This is very clever, using scoping rules to handle non-existent columns. Kudos! – Gordon Linoff Feb 23 '17 at 03:31
  • SAP Hana is the DBMS, and it doesn't seem to have much support for SQL extensions. It has most regular joins but no CROSS/OUTER APPLY or LATERAL joins unfortunately. The documentation is here: http://help-legacy.sap.com/saphelp_hanaplatform/helpdata/en/20/fcf24075191014a89e9dc7b8408b26/content.htm – surj Feb 23 '17 at 03:38
  • @surjikal the `` (`UNNEST`) seem to provide similar functionality. Try to convert the CROSS APPLY using that. – ypercubeᵀᴹ Feb 23 '17 at 03:53
  • Excellent that's great, thanks for the hint! I'll report back with my findings. – surj Feb 23 '17 at 04:06
  • @surjikal if you made this work in SAP Hana, feel free to edit the answer and add the code. – ypercubeᵀᴹ Feb 27 '18 at 17:33
  • @ypercubeᵀᴹ I did not :( – surj Feb 28 '18 at 08:27
  • Lovely solution! – user732456 Oct 23 '19 at 13:46
  • Excellent solution, by far the simplest and most elegant, thanks a lot, really – Joachim Jul 16 '20 at 07:02
5

you can use dynamic SQL.

first you need to check exist column and then create dynamic query.

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;
Reza Jenabi
  • 3,884
  • 1
  • 29
  • 34