Take the 2-minute tour ×
Database Administrators Stack Exchange is a question and answer site for database professionals who wish to improve their database skills and learn from others in the community. It's 100% free, no registration required.

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?

share|improve this question
    
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

2 Answers 2

up vote 14 down vote accepted

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] 
share|improve this answer
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

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.

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

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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