10

Example Schema:

CREATE TABLE [dbo].[Base](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Option1ID] [int] NULL,
[Option2ID] [int] NULL,
[Option3ID] [int] NULL,
[Option1Name] [varchar] NULL,
[Option2Name] [varchar] NULL,
[Option3Name] [varchar] NULL,
[Option1LName] [varchar] NULL,
[Option2LName] [varchar] NULL,
[Option3LName] [varchar] NULL,)

Is there a way to get results that show up like:

ID | OptionID | OptionName | OptionLName

I have tried to achieve this using UNION ALL but this means going over the same row 3 times in my example, in my real problem I have to do it 10 times. I cannot normalize the table due to legacy code. Is there a way to only go over the Base row once?

  • You can try a pivot but I wasn't able to get that working for something like what you're trying. Since the project I was working on required exporting data anyway I used the pivot function in SSIS and that worked like a charm. The only thing I can think of that may be like that is writing a CLR proc that would do what you want. – cfradenburg Feb 27 '13 at 21:35
23

You can use CROSS APPLY ... VALUES to UNPIVOT multiple columns

SELECT ID,
       OptionID,
       OptionName,
       OptionLName
FROM   [dbo].[Base]
       CROSS APPLY (VALUES([Option1ID], [Option1Name], [Option1LName]),
                          ([Option2ID], [Option2Name], [Option2LName]),
                          ([Option3ID], [Option3Name], [Option3LName])) 
                     V( OptionID, OptionName, OptionLName) 

The execution plan for this has one scan of Base. The plan is in fact the same as for the 2005 compatible rewrite that uses UNION ALL

SELECT ID,
       OptionID,
       OptionName,
       OptionLName
FROM   [dbo].[Base]
       CROSS APPLY (SELECT [Option1ID], [Option1Name], [Option1LName] UNION ALL
                    SELECT [Option2ID], [Option2Name], [Option2LName] UNION ALL
                    SELECT [Option3ID], [Option3Name], [Option3LName]) 
                     V( OptionID, OptionName, OptionLName)  

But I presume the UNION ALL you were trying to avoid was the multiple scans of

SELECT ID,
       [Option1ID],
       [Option1Name],
       [Option1LName]
FROM   [dbo].[Base]
UNION ALL
SELECT ID,
       [Option2ID],
       [Option2Name],
       [Option2LName]
FROM   [dbo].[Base]
UNION ALL
SELECT ID,
       [Option3ID],
       [Option3Name],
       [Option3LName]
FROM   [dbo].[Base] 
  • 1
    Elegant solution, Martin. I was going to suggest UNPIVOT, but I had no idea how to do the CROSS APPLY piece! – Max Vernon Feb 27 '13 at 22:06
  • @MaxVernon Thanks. I first came across that trick here I think. – Martin Smith Feb 27 '13 at 22:13
  • 1
    That's a new one for me also, handy. – Mark Storey-Smith Feb 27 '13 at 23:11
2

I found a "limitation" to CROSS APPLY-VALUES usage. When a field is NULL, I'd like to remove it from the table, but with CROSS APPLY-VALUES, a NULL value will be inserted into the table with NULL fields. With UNPIVOT, NULL fields will be automatically excluded from and not inserted into the table.

  • 1
    Could you provide an example (with sample data)to clarify your answer about inserts generating NULL values ? – RolandoMySQLDBA May 12 '14 at 3:30
  • I wouldn't say it's a limitation. Sometimes you actually need to include NULLs – Mark Sinkinson May 12 '14 at 8:10
  • Yes, you could say it's a different behaviour. – ypercubeᵀᴹ May 12 '14 at 8:19
  • 4
    Just add a where clause if you don't want the nulls. – Martin Smith May 12 '14 at 13:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.