I'm trying to move certain fields of an ID into columns, but it doesn't appear to match all the pivot examples I am finding. All the examples I can find use some form of a grouping on a field value. I want to use more of a placement regardless of the value in the field. I want to do this in a query without looping via code. Data source example (sorry couldn't figure out how to format a table on the post so I used a code snippet):
+----+--------+--------+
| ID | Field1 | Field2 |
+----+--------+--------+
| 1 | NULL | NULL |
| 2 | Jim | 321 |
| 2 | Jack | 54 |
| 2 | Sue | 985 |
| 2 | Gary | 654 |
| 3 | Herb | 332 |
| 3 | Chevy | 10 |
+----+--------+--------+
Result set I'm trying to generate:
+----+------+------+-------+------+------+------+
| ID | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 |
+----+------+------+-------+------+------+------+
| 1 | NULL | NULL | | | | |
| 2 | Jim | 321 | Jack | 54 | Sue | 985 |
| 3 | Herb | 332 | Chevy | 10 | | |
+----+------+------+-------+------+------+------+
SQL Fiddle: http://sqlfiddle.com/#!3/a225a/1
;with cte as (
select id
, field1
, field2
, ROW_NUMBER() over (partition by id order by field1, field2) r
from #t
)
select c1.id
, c1.field1 col1
, c1.field2 col2
, c2.field1 col3
, c2.field2 col4
, c3.field1 col5
, c3.field2 col6
from cte c1
left outer join cte c2 on c2.id = c1.id and c2.r = c1.r + 1
left outer join cte c3 on c3.id = c1.id and c3.r = c1.r + 2
where (c1.r % 3) = 1
Explanation
ROW_NUMBER() over (partition by id order by field1, field2) r. This line ensures that we have a column counting up from 1 for each id. This allows us to distinguish between the multiple rows.
The CTE is used to save typing the same statement for c1, c2 and c3.
The joins ensure that all items in a row have the same id, and that data for col1, col3 and col5 (likewise for col2, col4 and col6) is taken from consecutive rows. We're using left outer joins because there may not rows in the source table for these columns.
The where statement says to take the first row of each set of 3 for the data in c1 (with c2 and c3 thus being the second and third of each set, thanks to the earlier join).
Here's a solution using dynamic sql that works though I'm sure there's a better way to do it. Caution, it's a bit painful. First it builds the list of columns to pivot and select, builds the dynamic sql and runs it.
DECLARE #PivotColumns as varchar(max), #SelectColumns as varchar(max), #sql as varchar(max)
SELECT #PivotColumns = ISNULL(#PivotColumns + ',', '') + ColNum,
#SelectColumns = ISNULL(#SelectColumns + ',', '') + 'NULLIF(' + ColNum + ', ''NULL'') as ' + ColNum
from (select distinct 'Col' + cast(ROW_NUMBER() OVER (partition by id order by id) as varchar) as ColNum
from (select id,
isnull(field1,'NULL') as field1,
isnull(field2,'NULL') as field2
from weirdpivot) cols
unpivot
(
value
for col in (field1, field2)
) unpivoted) DistinctColumns
set #sql = '
select id, + ' + #SelectColumns + '
from (select
''Col'' + cast(ROW_NUMBER() OVER (partition by id order by id) as varchar) as colnum
,id
,value
from (select id,
isnull(field1,''NULL'') as field1,
isnull(field2,''NULL'') as field2
from weirdpivot) cols
unpivot
(
value
for col in (field1, field2)
) u) unpivoted
pivot
(
max(value)
for colnum in (' + #PivotColumns + ')
) p'
exec (#sql)
Related
I am looking for a way to pick the highest and lowest value (integer) from a single row in table. There are 4 columns that i need to compare together and get highest and lowest number there is.
The table looks something like this...
id | name | col_to_compare1 | col_to_compare2 | col_to_compare3 | col_to_compare4
1 | John | 5 | 5 | 2 | 1
2 | Peter | 3 | 2 | 4 | 1
3 | Josh | 3 | 5 | 1 | 3
Can you help me, please? Thanks!
You can do this using CROSS APPLY and the VALUES clause. Use VALUES to group all your compared columns and then select the max.
SELECT
MAX(d.data1) as MaxOfColumns
,MIN(d.data1) as MinOfColumns
,a.id
,a.name
FROM YOURTABLE as a
CROSS APPLY (
VALUES(a.col_to_compare1)
,(a.col_to_compare2)
,(a. col_to_compare3)
,(a.col_to_compare4)
,(a. col_to_compare5)
) as d(data1) --Name the Column
GROUP BY a.id
,a.name
Assuming you are looking for min/max per row
Declare #YourTable table (id int,name varchar(50),col_to_compare1 int,col_to_compare2 int,col_to_compare3 int,col_to_compare4 int)
Insert Into #YourTable values
(1,'John',5,5,2,1),
(2,'Peter',3,2,4,1),
(3,'Josh',3,5,1,3)
Select A.ID
,A.Name
,MinVal = min(B.N)
,MaxVal = max(B.N)
From #YourTable A
Cross Apply (Select N From (values(a.col_to_compare1),(a.col_to_compare2),(a.col_to_compare3),(a.col_to_compare4)) N(N) ) B
Group By A.ID,A.Name
Returns
ID Name MinVal MaxVal
1 John 1 5
3 Josh 1 5
2 Peter 1 4
These solutions keep the current rows and add additional columns of min/max.
select *
from t cross apply
(select min(col) as min_col
,max(col) as max_col
from (
values
(t.col_to_compare1)
,(t.col_to_compare2)
,(t.col_to_compare3)
,(t.col_to_compare4)
) c(col)
) c
OR
select *
,cast ('' as xml).value ('min ((sql:column("t.col_to_compare1"),sql:column("t.col_to_compare2"),sql:column("t.col_to_compare3"),sql:column("t.col_to_compare4")))','int') as min_col
,cast ('' as xml).value ('max ((sql:column("t.col_to_compare1"),sql:column("t.col_to_compare2"),sql:column("t.col_to_compare3"),sql:column("t.col_to_compare4")))','int') as max_col
from t
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| id | name | col_to_compare1 | col_to_compare2 | col_to_compare3 | col_to_compare4 | min_col | max_col |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 1 | John | 5 | 5 | 2 | 1 | 1 | 5 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 2 | Peter | 3 | 2 | 4 | 1 | 1 | 4 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 3 | Josh | 3 | 5 | 1 | 3 | 1 | 5 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
A way to do this is to "break" apart the data
declare #table table (id int, name varchar(10), col1 int, col2 int, col3 int, col4 int)
insert into #table values (1 , 'John' , 5 , 5 , 2 , 1)
insert into #table values (2 , 'Peter' , 3 , 2 , 4 , 1)
insert into #table values (3 , 'Josh' , 3 , 5 , 1 , 3)
;with stretch as
(
select id, col1 as col from #table
union all
select id, col2 as col from #table
union all
select id, col3 as col from #table
union all
select id, col4 as col from #table
)
select
t.id,
t.name,
agg.MinCol,
agg.MaxCol
from #table t
inner join
(
select
id, min(col) as MinCol, max(col) as MaxCol
from stretch
group by id
) agg
on t.id = agg.id
Seems simple enough
SELECT min(col1), max(col1), min(col2), max(col2), min(col3), max(col3), min(col4), max(col4) FROM table
Gives you the Min and Max for each column.
Following OP's comment, I believe he may be looking for a min/max grouped by the person being queried against.
So that would be:
SELECT name, min(col1), max(col1), min(col2), max(col2), min(col3), max(col3), min(col4), max(col4) FROM table GROUP BY name
I have the following SQL result from a SELECT query:
ID | category| value | desc
1 | A | 10 | text1
2 | A | 11 | text11
3 | B | 20 | text20
4 | B | 21 | text21
5 | C | 30 | text30
This result is stored in a temporary table named #temptab. This temporary table is then used in another SELECT to build up a new colum via string concatenation (don't ask me about the detailed rationale behind this. This is code I took from a colleague). Via FOR XML PATH() the output of this column is a list of the results and is then used to send mails to customers.
The second SELECT looks as follows:
SELECT t1.column,
t2.column,
(SELECT t.category + ' | ' + t.value + ' | ' + t.desc + CHAR(9) + CHAR(13) + CHAR(10)
FROM #temptab t
WHERE t.ID = ttab.ID
FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)') AS colname
FROM table1 t1
...
INNER JOIN #temptab ttab on ttab.ID = someOtherTable.ID
...
Without wanting to go into too much detail, the column colname becomes populated with several entries (due to multiple matches) and hence, a longer string is stored in this column (CHAR(9) + CHAR(13) + CHAR(10) is essentially a line break). The result/content of colname looks like this (it is used to send mails to customers):
A | 10 | text1
A | 11 | text11
B | 20 | text20
B | 21 | text21
C | 30 | text30
Now I would like to know, if there is a way to more nicely format this output string. The best case would be to group the same categories together and add a heading and empty line between different categories:
*A*
A | 10 | text1
A | 11 | text11
*B*
B | 20 | text20
B | 21 | text21
*C*
C | 30 | text30
My question is: How do I have to modify the above query (especially the string-concatenation-part) to achieve above formatting? I was thinking about using a GROUP BY statement, but this obviously does not yield the desired result.
Edit: I use Microsoft SQL Server 2008 R2 (SP2) - 10.50.4270.0 (X64)
Declare #YourTable table (ID int,category varchar(50),value int, [desc] varchar(50))
Insert Into #YourTable values
(1,'A',10,'text1'),
(2,'A',11,'text11'),
(3,'B',20,'text20'),
(4,'B',21,'text21'),
(5,'C',30,'text30')
Declare #String varchar(max) = ''
Select #String = #String + Case when RowNr=1 Then Replicate(char(13)+char(10),2) +'*'+Category+'*' Else '' end
+ char(13)+char(10) + category + ' | ' + cast(value as varchar(25)) + ' | ' + [desc]
From (
Select *
,RowNr=Row_Number() over (Partition By Category Order By Value)
From #YourTable
) A Order By Category, Value
Select Substring(#String,5,Len(#String))
Returns
*A*
A | 10 | text1
A | 11 | text11
*B*
B | 20 | text20
B | 21 | text21
*C*
C | 30 | text30
This should return what you want
Declare #YourTable table (ID int,category varchar(50),value int, [desc] varchar(50))
Insert Into #YourTable values
(1,'A',10,'text1'),
(2,'A',11,'text11'),
(3,'B',20,'text20'),
(4,'B',21,'text21'),
(5,'C',30,'text30');
WITH Categories AS
(
SELECT category
,'**' + category + '**' AS CatCaption
,ROW_NUMBER() OVER(ORDER BY category) AS CatRank
FROM #YourTable
GROUP BY category
)
,Grouped AS
(
SELECT c.CatRank
,0 AS ValRank
,c.CatCaption AS category
,-1 AS ID
,'' AS Value
,'' AS [desc]
FROM Categories AS c
UNION ALL
SELECT c.CatRank
,ROW_NUMBER() OVER(PARTITION BY t.category ORDER BY t.Value)
,t.category
,t.ID
,CAST(t.value AS VARCHAR(100))
,t.[desc]
FROM #YourTable AS t
INNER JOIN Categories AS c ON t.category=c.category
)
SELECT category,Value,[desc]
FROM Grouped
ORDER BY CatRank,ValRank
The result
category Value desc
**A**
A 10 text1
A 11 text11
**B**
B 20 text20
B 21 text21
**C**
C 30 text30
My table is as below
recordId fwildcardId refNumber wildcardName wildcardValue comments
404450 154834 2 aaa p p
404450 154833 1 aa oi p
406115 154867 1 98 ff ff
406199 154869 1 aa aaaa ssss
406212 154880 1 bbbbb card comm
and I need the output as
RecordId fwildcardid1 refNo1 Name1 Value1 comments1 fwildcardid2 refNo2 Name2 Value2 comments2 fwildcardid3 refNo3 Name3 Value3 comments3
404450 154834 2 aaa p p 154833 1 aa oi p
406115 Null Null Null Null Null Null Null Null Null Null 154867 1 98 ff ff
406199 Null Null Null Null Null 154869 1 aa aaaa ssss Null Null Null Null
I tried pivoting like below , but didnt succeed .
select t1.recordId,t1.wildcardid as fwildcardId,t1.refNo as refNumber,t2.wildcardName,t1.attributeValue as wildcardValue,t1.comments
into #tempp
from fwildcards t1
inner join fwildcardattributes t2 on t2.WildcardID=t1.attributenameid and t2.MarketID=5
inner join fitems t3 on t3.recordid=t1.recordid and t3.marketid=5
order by recordid,attributenameid
select * from #tempp
pivot (min (wildcardValue) for wildcardName in ([aaa],[aa],[aaaa],[98],[kki],[bbbbb],[SUN])) as wildcardValuePivot
In order to get this result, you will have to UNPIVOT and hen PIVOT the data. The UNPIVOT will take the values in the columns fwildcardId, refNumber, wildcardName, wildcardValue and comments and turns them into rows. Once the data is in rows, then you can apply the PIVOT function to get the final result.
To unpivot the data, you can use either the UNPIVOT function or you can use the CROSS APPLY and VALUES clause.
UNPIVOT:
select recordid,
col+cast(rn as varchar(10)) col,
unpiv_value
from
(
select recordid,
cast(fwildcardid as varchar(10)) fwildcardid,
cast(refnumber as varchar(10)) refnumber,
cast(wildcardname as varchar(10)) name,
cast(wildcardvalue as varchar(10)) value,
cast(comments as varchar(10)) comments,
row_number() over(partition by recordid
order by fwildcardid) rn
from tempp
) d
unpivot
(
unpiv_value
for col in (fwildcardid, refnumber, name, value, comments)
) c
See SQL Fiddle with Demo.
CROSS APPLY and VALUES:
select recordid,
col+cast(rn as varchar(10)) col,
value
from
(
select recordid,
cast(fwildcardid as varchar(10)) fwildcardid,
cast(refnumber as varchar(10)) refnumber,
wildcardname,
wildcardvalue,
comments,
row_number() over(partition by recordid
order by fwildcardid) rn
from tempp
) d
cross apply
(
values
('fwildcardid', fwildcardid),
('refnumber', refnumber),
('name', wildcardname),
('value', wildcardvalue),
('comments', comments)
) c (col, value)
See SQL Fiddle with Demo.
These convert the results in a format:
| RECORDID | COL | VALUE |
------------------------------------
| 404450 | fwildcardid1 | 154833 |
| 404450 | refnumber1 | 1 |
| 404450 | name1 | aa |
| 404450 | value1 | oi |
| 404450 | comments1 | p |
| 404450 | fwildcardid2 | 154834 |
When you unpivot data into the same column, it has to be the same datatype. You will notice that I applied a cast to the columns so the datatype is the same.
Once the data is in the row format, you can convert it back into columns with PIVOT:
select *
from
(
select recordid,
col+cast(rn as varchar(10)) col,
unpiv_value
from
(
select recordid,
cast(fwildcardid as varchar(10)) fwildcardid,
cast(refnumber as varchar(10)) refnumber,
cast(wildcardname as varchar(10)) name,
cast(wildcardvalue as varchar(10)) value,
cast(comments as varchar(10)) comments,
row_number() over(partition by recordid
order by fwildcardid) rn
from tempp
) d
unpivot
(
unpiv_value
for col in (fwildcardid, refnumber, name, value, comments)
) c
) src
pivot
(
max(unpiv_value)
for col in (fwildcardid1, refnumber1, name1, value1, comments1,
fwildcardid2, refnumber2, name2, value2, comments2)
) piv;
See SQL Fiddle with Demo.
The above version works great if you have a known number of columns, but if you will have an unknown number of values that will be converted into columns, then you will need to use dynamic sql to get the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(c.col+cast(rn as varchar(10)))
from
(
select row_number() over(partition by recordid
order by fwildcardid) rn
from tempp
) t
cross apply
(
select 'fwildcardid' col, 1 sortorder union all
select 'refNumber', 2 union all
select 'name', 3 union all
select 'value', 4 union all
select 'comments', 5
) c
group by col, rn, sortorder
order by rn, sortorder
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT recordid,' + #cols + ' from
(
select recordid,
col+cast(rn as varchar(10)) col,
unpiv_value
from
(
select recordid,
cast(fwildcardid as varchar(10)) fwildcardid,
cast(refnumber as varchar(10)) refnumber,
cast(wildcardname as varchar(10)) name,
cast(wildcardvalue as varchar(10)) value,
cast(comments as varchar(10)) comments,
row_number() over(partition by recordid
order by fwildcardid) rn
from tempp
) d
unpivot
(
unpiv_value
for col in (fwildcardid, refnumber, name, value, comments)
) c
) src
pivot
(
max(unpiv_value)
for col in (' + #cols + ')
) p '
execute(#query);
See SQL Fiddle with Demo. Both of these give the result:
| RECORDID | FWILDCARDID1 | REFNUMBER1 | NAME1 | VALUE1 | COMMENTS1 | FWILDCARDID2 | REFNUMBER2 | NAME2 | VALUE2 | COMMENTS2 |
-------------------------------------------------------------------------------------------------------------------------------
| 404450 | 154833 | 1 | aa | oi | p | 154834 | 2 | aaa | p | p |
| 406115 | 154867 | 1 | 98 | ff | ff | (null) | (null) | (null) | (null) | (null) |
| 406199 | 154869 | 1 | kki | aaaa | ssss | (null) | (null) | (null) | (null) | (null) |
| 406212 | 154880 | 1 | bbbbb | card | comm | (null) | (null) | (null) | (null) | (null) |
No Pivot No Cross Apply
According to edited Question.
select
DISTINCT
A.recordId AS recordId,
A1.fwildcardId AS fwildcardId1,
A1.refNumber AS refNumber1,
A1.wildcardName AS wildcardName1,
A1.wildcardValue AS wildcardValue1,
A1.comments AS comments1,
A2.fwildcardId AS fwildcardId2,
A2.refNumber AS refNumber2,
A2.wildcardName AS wildcardName2,
A2.wildcardValue AS wildcardValue2,
A2.comments AS comments2,
A3.fwildcardId AS fwildcardId3,
A3.refNumber AS refNumber3,
A3.wildcardName AS wildcardName3,
A3.wildcardValue AS wildcardValue3,
A3.comments AS comments3,
A4.fwildcardId AS fwildcardId4,
A4.refNumber AS refNumber4,
A4.wildcardName AS wildcardName4,
A4.wildcardValue AS wildcardValue4,
A4.comments AS comments4,
A5.fwildcardId AS fwildcardId5,
A5.refNumber AS refNumber5,
A5.wildcardName AS wildcardName5,
A5.wildcardValue AS wildcardValue5,
A5.comments AS comments5,
A6.fwildcardId AS fwildcardId6,
A6.refNumber AS refNumber6,
A6.wildcardName AS wildcardName6,
A6.wildcardValue AS wildcardValue6,
A6.comments AS comments6,
A7.fwildcardId AS fwildcardId7,
A7.refNumber AS refNumber7,
A7.wildcardName AS wildcardName7,
A7.wildcardValue AS wildcardValue7,
A7.comments AS comments7,
A8.fwildcardId AS fwildcardId8,
A8.refNumber AS refNumber8,
A8.wildcardName AS wildcardName8,
A8.wildcardValue AS wildcardValue8,
A8.comments AS comments8,
A9.fwildcardId AS fwildcardId9,
A9.refNumber AS refNumber9,
A9.wildcardName AS wildcardName9,
A9.wildcardValue AS wildcardValue9,
A9.comments AS comments9,
A10.fwildcardId AS fwildcardId10,
A10.refNumber AS refNumber10,
A10.wildcardName AS wildcardName10,
A10.wildcardValue AS wildcardValue10,
A10.comments AS comments10
from Table_name A
LEFt JOIN Table_name A1 ON A.recordId=A1.recordId AND A1.wildcardName='aaa'
LEFT JOIN Table_name A2 ON A.recordId=A2.recordId AND A2.wildcardName='aa'
LEFT JOIN Table_name A3 ON A.recordId=A3.recordId AND A3.wildcardName='98'
LEFT JOIN Table_name A4 ON A.recordId=A4.recordId AND A4.wildcardName=''
LEFT JOIN Table_name A5 ON A.recordId=A5.recordId AND A5.wildcardName=''
LEFT JOIN Table_name A6 ON A.recordId=A6.recordId AND A6.wildcardName=''
LEFT JOIN Table_name A7 ON A.recordId=A7.recordId AND A7.wildcardName=''
LEFT JOIN Table_name A8 ON A.recordId=A8.recordId AND A8.wildcardName=''
LEFT JOIN Table_name A9 ON A.recordId=A9.recordId AND A9.wildcardName=''
LEFT JOIN Table_name A10 ON A.recordId=A10.recordId AND A10.wildcardName=''
SQL Fiddle
Okay I have the following table.
Name ID Website
Aaron | 2305 | CoolSave1
Aaron | 8464 | DiscoWorld1
Adriana | 2956 | NewCin1
Adriana | 5991 | NewCin2
Adriana | 4563 NewCin3
I would like to transform it into the following way.
Adriana | 2956 | NewCin1 | 5991 | NewCin2 | 4563 | NewCin3
Aaron | 2305 | CoolSave1 | 8464 | DiscoWorld | NULL | NULL
As you can see i am trying to take the first name from the first table and make a single row with all the IDs / Websites associated with that name. The problem is, there is a variable amount of websites that may be associated with each name. To handle this i'd like to just make a table with with the number of fields sequal to the max line item, and then for the subsequent lineitems, plug in a NULL where there are not enough data.
In order to get the result, you will need to apply both the UNPIVOT and the PIVOT functions to the data. The UNPIVOT will take the columns (ID, website) and convert them to rows, once this is done, then you can PIVOT the data back into columns.
The UNPIVOT code will be similar to the following:
select name,
col+'_'+cast(col_num as varchar(10)) col,
value
from
(
select name,
cast(id as varchar(11)) id,
website,
row_number() over(partition by name order by id) col_num
from yt
) src
unpivot
(
value
for col in (id, website)
) unpiv;
See SQL Fiddle with Demo. This gives a result:
| NAME | COL | VALUE |
-------------------------------------
| Aaron | id_1 | 2305 |
| Aaron | website_1 | CoolSave1 |
| Aaron | id_2 | 8464 |
| Aaron | website_2 | DiscoWorld1 |
As you can see I applied a row_number() to the data prior to the unpivot, the row number is used to generate the new column names. The columns in the UNPIVOT must also be of the same datatype, I applied a cast to the id column in the subquery to convert the data to a varchar prior to the pivot.
The col values are then used in the PIVOT. Once the data has been unpivoted, you apply the PIVOT function:
select *
from
(
select name,
col+'_'+cast(col_num as varchar(10)) col,
value
from
(
select name,
cast(id as varchar(11)) id,
website,
row_number() over(partition by name order by id) col_num
from yt
) src
unpivot
(
value
for col in (id, website)
) unpiv
) d
pivot
(
max(value)
for col in (id_1, website_1, id_2, website_2, id_3, website_3)
) piv;
See SQL Fiddle with Demo.
The above version works great if you have a limited or known number of values. But if the number of rows is unknown, then you will need to use dynamic SQL to generate the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME( col+'_'+cast(col_num as varchar(10)))
from
(
select row_number() over(partition by name order by id) col_num
from yt
) t
cross apply
(
select 'id' col union all
select 'website'
) c
group by col, col_num
order by col_num, col
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT name,' + #cols + '
from
(
select name,
col+''_''+cast(col_num as varchar(10)) col,
value
from
(
select name,
cast(id as varchar(11)) id,
website,
row_number() over(partition by name order by id) col_num
from yt
) src
unpivot
(
value
for col in (id, website)
) unpiv
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute(#query);
See SQL Fiddle with Demo. Both versions give the result:
| NAME | ID_1 | WEBSITE_1 | ID_2 | WEBSITE_2 | ID_3 | WEBSITE_3 |
------------------------------------------------------------------------
| Aaron | 2305 | CoolSave1 | 8464 | DiscoWorld1 | (null) | (null) |
| Adriana | 2956 | NewCin1 | 4563 | NewCin3 | 5991 | NewCin2 |
I'm trying to combine partial contents of rows that are the result set of a query from SQL Server 2005 that reads a .CSV. Here's a simplified version of the data I have:
objectID | value1 | value2
_________________________________
12 | R | 100
12 | R | 101
12 | S | 220
13 | D | 88
14 | K | 151
14 | K | 152
What I'm trying to get to is a grouping of each objectID's values on the same row, so that there is one and only one row for each objectID. In graphical terms:
objectID | value1a | value2a | value 1b | value2b | value1c | value2c
______________________________________________________________________________
12 | R | 100 | R | 101 | S | 220
13 | D | 88 | | | |
14 | K | 151 | K | 152 | |
Blank cells are blank.
I've been hoping to do this in Excel or Access without VB, but CONCAT and other similar functions (and responses here and elsewhere suggesting similar approaches) don't work because each value needs to stay in its own cell (this data will eventually be merged with a Word form). If the answer's a SQL stored procedure or cursor, that's okay, though I'm not terribly efficient at writing them just yet.
Thanks to all.
First import the data into a temp table. The temp table will end up something like this sample data:
create table #tmp (objectID int, value1 char(1), value2 int)
insert #tmp select
12 ,'R', 100 union all select
12 ,'R', 101 union all select
12 ,'S', 220 union all select
13 ,'D', 88 union all select
14 ,'K', 151 union all select
14 ,'K', 152
Then, you can use this SQL batch - which can be put into a Stored Procedure if required.
declare #sql nvarchar(max)
select #sql = ISNULL(#sql+',','')
+ 'max(case when rn=' + cast(number as varchar) + ' then value1 end) value' + cast(number as varchar) + 'a,'
+ 'max(case when rn=' + cast(number as varchar) + ' then value2 end) value' + cast(number as varchar) + 'b'
from master..spt_values
where type='P' and number between 1 and (
select top 1 COUNT(*)
from #tmp
group by objectID
order by 1 desc)
set #sql = '
select objectID, ' + #sql + '
from (
select rn=ROW_NUMBER() over (partition by objectID order by value2), *
from #tmp) p
group by ObjectID'
exec (#sql)
Output
objectID value1a value1b value2a value2b value3a value3b
----------- ------- ----------- ------- ----------- ------- -----------
12 R 100 R 101 S 220
13 D 88 NULL NULL NULL NULL
14 K 151 K 152 NULL NULL
Warning: Null value is eliminated by an aggregate or other SET operation.