N-Tupling Data from one Table to another table! How?
-
Thursday, June 21, 2012 10:27 AM
Hi all,
I have two tables - Table1 and Table2. In Table1 I have a list of companies with performance data. There are ten companies and 10 bit of performance data. So my columns are Id, Company and Value.
In Table2 I have columns - Id, Col_01c, Col_01v, Col_02c, Col_02v ... Col_20c, Col_20v.
What I want to do is to take the ten companies and their values and put them into one row, the company into the ...c and the value into the ...v columns - so I end up with one row from the ten rows.
I beleive this is called N-Tupling. I have seen this done in a Filemaker database, but I want to do this in a SQL Server 2005 database using T-SQL in Management Studio.
Any ideas?
Lee Warren
All Replies
-
Thursday, June 21, 2012 11:36 AM
This is a typical pivot operation, and this is a question that is answered a couple of days in this forum.
I will have to assume that the primary of Table1 is (Id, Company) else this gets difficult.
SELECT Id,
'Company1' AS Col_01c,
...
'Company10' AS Col_10c,
MIN(CASE WHEN Company = 'Company1' THEN Value END) AS Col_01v,
...
MIN(CASE WHEN Company = 'Company10' THEN Value END) AS Col_10v
FROM Table1
GROUP BY IdThe purpose of the MIN operation is to put all values on a single row. Each MIN only sees one non-NULL value, so it would work with MAX as well.
SQL 2005 also offer a special PIVOT operator, but the pattern above ports to other products and is generally easier to master. (Which is testified by the fact that I don't know how to use PIVOT.)
Erland Sommarskog, SQL Server MVP, esquel@sommarskog.se- Proposed As Answer by Sali SQL Thursday, June 21, 2012 11:52 AM
-
Thursday, June 21, 2012 11:56 AM
Hi Erland,
Many thanks for your post. I will give it a try and post back with the results of how I got on.
Cheers!
Lee
Lee Warren
-
Thursday, June 21, 2012 12:21 PM
Hi Erland,
I run the following on Table002: (sorry, my column name was Country not Company)
SELECT Id,
'Country1' AS Col_01c,
'Country2' AS Col_02c,
'Country3' AS Col_03c,
'Country4' AS Col_04cMIN(CASE WHEN Company = 'Country1' THEN Value END) AS Col_01v,
MIN(CASE WHEN Company = 'Country2' THEN Value END) AS Col_02v,
MIN(CASE WHEN Company = 'Country3' THEN Value END) AS Col_03v,
MIN(CASE WHEN Company = 'Country4' THEN Value END) AS Col_04vFROM Table001
GROUP BY IdAnd I get the following error -
Msg 102, Level 15, State 1, Line 7
Incorrect syntax near 'MIN'.
Any ideas why?
Lee Warren
-
Thursday, June 21, 2012 12:24 PM
you have missed a comma after this 'Country4' AS Col_04c
you need to use:
'Country4' AS Col_04c,
|
Blog: MSBICOE.com |
MCITP - BI, SQL Developer & DBA
Hate to mislead others, if I'm wrong slap me. Thanks!
-
Thursday, June 21, 2012 12:24 PM
haven't run the query, but you're missing a comma after the 'country4' line, and before the MIN
Thanks,
Andrew Bainbridge
SQL Server DBA
Please click "Propose As Answer" if a post solves your problem, or "Vote As Helpful" if a post has been useful to you -
Thursday, June 21, 2012 12:28 PM
If I add the comma I get the following:
Msg 207, Level 16, State 1, Line 7
Invalid column name 'Company'.
Msg 207, Level 16, State 1, Line 8
Invalid column name 'Company'.
Msg 207, Level 16, State 1, Line 9
Invalid column name 'Company'.
Msg 207, Level 16, State 1, Line 10
Invalid column name 'Company'.
Lee Warren
-
Thursday, June 21, 2012 12:36 PM
In your question you mentioned that you have Table with columns:
So my columns are Id, Company and Value.
It is the same Company Column that is referred in the Query. If the column name in your table is different use that Column Name.
Please post you actual DDL script for others to help you..
The simpler the solution the stronger it is
If this post answers you, please mark it as answer..
If this post is useful, please vote it as useful.. -
Thursday, June 21, 2012 12:40 PMchange "WHEN COMPANY" TO "WHEN COUNTRY" on each of the 4 lines... (I presume, based on your comments and the query)
Thanks,
Andrew Bainbridge
SQL Server DBA
Please click "Propose As Answer" if a post solves your problem, or "Vote As Helpful" if a post has been useful to you -
Thursday, June 21, 2012 12:43 PM
Columns are Id, Country, Value.
This is what I am using:
SELECT Id, 'Country1' AS Col_01c, 'Country2' AS Col_02c, 'Country3' AS Col_03c, 'Country4' AS Col_04c, MIN(CASE WHEN Country = 'Country1' THEN Value END) AS Col_01v, MIN(CASE WHEN Country = 'Country2' THEN Value END) AS Col_02v, MIN(CASE WHEN Country = 'Country3' THEN Value END) AS Col_03v, MIN(CASE WHEN Country = 'Country4' THEN Value END) AS Col_04v FROM [A510945].[Table001] GROUP BY Id
And this is what I get now:
1 Country1 Country2 Country3 Country4 NULL NULL NULL NULL
2 Country1 Country2 Country3 Country4 NULL NULL NULL NULL
3 Country1 Country2 Country3 Country4 NULL NULL NULL NULL
4 Country1 Country2 Country3 Country4 NULL NULL NULL NULLLee Warren
-
Thursday, June 21, 2012 12:45 PM
Here is my Table001:
Id Country Value
1 UK 10
2 France 20
3 Italy 30
4 Spain 40Lee Warren
-
Thursday, June 21, 2012 1:28 PMModeratorRemove ID and GROUP BY ID from your query. I assume that ID is the unique column.
For every expert, there is an equal and opposite expert. - Becker's Law
My blog -
Thursday, June 21, 2012 1:32 PMModerator
Try:
SELECT cast('UK' as varchar(20)) AS Col_01c, cast('France' as varchar(20)) AS Col_02c, cast('Italy' as varchar(20)) AS Col_03c, cast('Spain' as varchar(20)) AS Col_04c, MIN(CASE WHEN Country = 'UK' THEN Value END) AS Col_01v, MIN(CASE WHEN Country = 'France' THEN Value END) AS Col_02v, MIN(CASE WHEN Country = 'Italy' THEN Value END) AS Col_03v, MIN(CASE WHEN Country = 'Spain' THEN Value END) AS Col_04v FROM [A510945].[Table001]
This code assumes your countries always be the same and never change. Otherwise we need to use dynamic query which is a bit more complex.
For every expert, there is an equal and opposite expert. - Becker's Law
My blog -
Thursday, June 21, 2012 1:38 PM
Try:
SELECT cast('UK' as varchar(20)) AS Col_01c, cast('France' as varchar(20)) AS Col_02c, cast('Italy' as varchar(20)) AS Col_03c, cast('Spain' as varchar(20)) AS Col_04c, MIN(CASE WHEN Country = 'UK' THEN Value END) AS Col_01v, MIN(CASE WHEN Country = 'France' THEN Value END) AS Col_02v, MIN(CASE WHEN Country = 'Italy' THEN Value END) AS Col_03v, MIN(CASE WHEN Country = 'Spain' THEN Value END) AS Col_04v FROM [A510945].[Table001]
This code assumes your countries always be the same and never change. Otherwise we need to use dynamic query which is a bit more complex.
For every expert, there is an equal and opposite expert. - Becker's Law
My blogThanks Naomi,
That works fine now. However, the names of the country will change. How do we do that?
Lee Warren
-
Thursday, June 21, 2012 2:04 PMModerator
In this case we need to generate the above dynamically, e.g.
Replace Productions with your table name in all the statements.USE tempdb CREATE TABLE Productions ( Id INT ,Country VARCHAR(20) ,Value INT ) INSERT INTO Productions VALUES ( 1 ,'UK' ,10 ) ,( 2 ,'France' ,20 ) ,( 3 ,'Italy' ,30 ) ,( 4 ,'Spain' ,40 ) DECLARE @SQL NVARCHAR(max) ,@Cols NVARCHAR(max) ,@ColsV NVARCHAR(max) SELECT @Cols = STUFF(( SELECT ', ' + 'cast(''' + Country + ''' AS VARCHAR(20)) AS [Col_0' +
convert(VARCHAR(10), Rn) + 'c]' FROM ( SELECT ID ,Country ,ROW_NUMBER() OVER ( ORDER BY ID ) AS Rn FROM Productions ) src ORDER BY ID FOR XML PATH('') ,type ).value('.', 'nvarchar(max)'), 1, 2, '') --print @Cols SELECT @ColsV = STUFF(( SELECT ', MAX(CASE WHEN Country =''' + Country + ''' THEN ' +
cast(Value AS VARCHAR(20)) + ' END) AS [Col_0' + convert(VARCHAR(10), Rn) + 'v]' FROM ( SELECT Value ,Country ,ID ,ROW_NUMBER() OVER ( ORDER BY ID ) AS Rn FROM Productions ) src ORDER BY Id FOR XML PATH('') ,type ).value('.', 'nvarchar(max)'), 1, 2, '') --print @ColsV SET @SQL = 'SELECT ' + @Cols + ', ' + @ColsV + ' FROM Productions' --PRINT @SQL EXECUTE (@SQL)
For every expert, there is an equal and opposite expert. - Becker's Law
My blog- Edited by Naomi NMicrosoft Community Contributor, Moderator Thursday, June 21, 2012 2:05 PM
- Marked As Answer by Lee 'Wozy' Warren Thursday, June 21, 2012 2:23 PM
-
Thursday, June 21, 2012 2:14 PM
You are on SQL 2005, which does not support the insert approach in the query Naomi gave you. Just change it to:
INSERT INTO Productions Select1, 'UK', 10 Union All Select 2, 'France', 20 Union All Select 3, 'Italy', 30 Union All Select 4, 'Spain', 40
In your production system you won't need this part anyway, just the bits after.
-
Thursday, June 21, 2012 2:19 PM
I amended the code slightly to this:
USE FILPUBLISHINGSolutions DECLARE @SQL NVARCHAR(max) ,@Cols NVARCHAR(max) ,@ColsV NVARCHAR(max) SELECT @Cols = STUFF(( SELECT ', ' + 'cast(''' + Country + ''' AS VARCHAR(20)) AS [Col_0' + convert(VARCHAR(10), Rn) + 'c]' FROM ( SELECT ID ,Country ,ROW_NUMBER() OVER ( ORDER BY ID ) AS Rn FROM Table001 ) src ORDER BY ID FOR XML PATH('') ,type ).value('.', 'nvarchar(max)'), 1, 2, '') --print @Cols SELECT @ColsV = STUFF(( SELECT ', MAX(CASE WHEN Country =''' + Country + ''' THEN ' + cast(Value AS VARCHAR(20)) + ' END) AS [Col_0' + convert(VARCHAR(10), Rn) + 'v]' FROM ( SELECT Value ,Country ,ID ,ROW_NUMBER() OVER ( ORDER BY ID ) AS Rn FROM Table001 ) src ORDER BY Id FOR XML PATH('') ,type ).value('.', 'nvarchar(max)'), 1, 2, '') --print @ColsV SET @SQL = 'SELECT ' + @Cols + ', ' + @ColsV + ' FROM Table001' --PRINT @SQL EXECUTE (@SQL)And got this:
UK France Italy Spain 10 20 30 40
Excellent. Many thanks. I just need to go through the code and work out exactly what you did... lol.
THanks.
Lee
Lee Warren
-
Thursday, June 21, 2012 2:20 PMModeratorYes, exactly. Just change the original insert statement. You don't even need it, you can go ahead and remove creation of the table, etc. and go directly for select statement using your own table name.
For every expert, there is an equal and opposite expert. - Becker's Law
My blog -
Thursday, June 21, 2012 2:27 PM
I tested Naomi's script and it runs without error.
I suppose that you adpoted the query to your table, and managed to introduced a syntax error. Now, generally, I expect people to be able to spot trivial syntax errors on their own. It may seem hard, but if you aspire to be an SQL programmer, you better get used to it, because you will encouter those sort of errors again and again.
Now, Naomi's solution is a lot more complex than my original solution, but you also added a very important piece of information.
Now, according to your initial post, the target table has 10 columns for names and 10 for values. Does this mean that you always have 10 countries? In such case, there is a solution which does not require dynamic SQL.
Overall, I would recommend you investigate your error mesages and try to address them on your own. I will have to admit that it is much more rewarding to help someone who is making an effort himself, than someone who just want to be spoonfed a solution.
Erland Sommarskog, SQL Server MVP, esquel@sommarskog.se -
Thursday, June 21, 2012 2:36 PM
Thank you all for your very valued input. I am trying to be a better programmer although it is not currently my main function.
Again, thank you all.
Best
Lee
Lee Warren
-
Thursday, August 09, 2012 2:15 PM
Hi Naomi,
I have been using the query that you have suggested and it works a treat. I am now needing to expand upon this and have been testing over the past few days, but with little success.
What I am trying to do is to get more 'rows' of data. eg:
I want to have each icrs number to be one table row. So in the above example, I should get 3 rows of data.
How do I amend the query to do this? I have had no luck in doing this so far.
Kind regards
Lee
Lee Warren
- Edited by Lee 'Wozy' Warren Thursday, August 09, 2012 2:16 PM missing text

