How can I concatenate values from 3 column per id? - sql

Consider the following table #temp:
objectid field1 field2 field3
--------------------------------
1 X 001 foo
2 Y 022 bar
2 Z 033 baz
3 A 111 abc
3 B 222 def
3 C 333 ghi
4 Q 900 tom
I need the concatenate field1, field2 and field3 for each objectid:
objectid field1 field2 field3
------------------------------------------
1 X 001 foo
2 Y;Z 022;033 barbaz
3 A;B;C 111;222;333 abc;def;ghi
4 Q 900 tom
I trief this first for only field1 as follows:
select
c.FLUSObjectNumber,
[field1] = stuff(
(
select ';' + field1
from #temp as c2
where c2.objectid = c.objectid and c2.field1 = c.field1
for xml path(''), type
).value('.', 'varchar(max)'), 1, 1, ''
)
from #temp c
group by c.objectid, c.field1
;
However, this returns the list I already have in #temp.
What am I doing wrong? How can I generate the desired output?

declare #t table (objectid INT, field1 VARCHAR(50), field2 VARCHAR(50), field3 VARCHAR(50))
INSERT INTO #t (objectid,field1,field2,field3)values (1,'X','001','foo')
INSERT INTO #t (objectid,field1,field2,field3)values (2,'Y','022','bar')
INSERT INTO #t (objectid,field1,field2,field3)values (2,'Z','033','baz')
INSERT INTO #t (objectid,field1,field2,field3)values (3,'A','111','abc')
INSERT INTO #t (objectid,field1,field2,field3)values (3,'B','222','def')
INSERT INTO #t (objectid,field1,field2,field3)values (3,'C','333','ghi')
INSERT INTO #t (objectid,field1,field2,field3)values (4,'Q','900','tom')
Select distinct t.objectid ,
STUFF((Select distinct ',' + t1.field1
from #t t1
where t.objectid = t1.objectid
ORDER BY 1
FOR XML PATH(''))
, 1, 1, '')As field,
STUFF((Select distinct ',' + t2.field2
from #t t2
where t.objectid = T2.objectid
ORDER BY 1
FOR XML PATH(''),TYPE).value('.', 'NVARCHAR(MAX)')
, 1, 1, ' ')As field2,
STUFF((Select distinct ',' + T3.field3
from #t t3
where t.objectid = t3.objectid
ORDER BY 1
FOR XML PATH(''),TYPE).value('.', 'NVARCHAR(MAX)')
, 1, 1, ' ')As field3
from #t t
group by
t.objectid

DECLARE #Temp TABLE
(
objectid INT,
field1 VARCHAR(20),
field2 VARCHAR(20),
field3 VARCHAR(20)
)
INSERT INTO #Temp
( objectid, field1, field2, field3 )
VALUES
(1 ,'X' ,'001' ,'foo'),
(2 ,'Y' ,'022' ,'bar'),
(2 ,'Z' ,'033' ,'baz'),
(3 ,'A' ,'111' ,'abc'),
(3 ,'B' ,'222' ,'def'),
(3 ,'C' ,'333' ,'ghi'),
(4 ,'Q' ,'900' ,'tom');
SELECT DISTINCT t.objectid, f1.f1, f2.f2, f3.f3
FROM #Temp t
OUTER APPLY
(
SELECT STUFF((SELECT ',' + Field1 FROM #Temp WHERE objectid = t.objectid ORDER BY field1 FOR XML PATH('')),1,1,'') f1
) f1
OUTER APPLY
(
SELECT STUFF((SELECT ',' + Field2 FROM #Temp WHERE objectid = t.objectid ORDER BY field2 FOR XML PATH('')),1,1,'') f2
) f2
OUTER APPLY
(
SELECT STUFF((SELECT ',' + Field3 FROM #Temp WHERE objectid = t.objectid ORDER BY field1 FOR XML PATH('')),1,1,'') f3
) f3
OUTPUT:
objectid f1 f2 f3
1 X 001 foo
2 Y,Z 022,033 bar,baz
3 A,B,C 111,222,333 abc,def,ghi
4 Q 900 tom

You don't need outer apply or anything, try this:
SELECT DISTINCT t.objectid,
(SELECT STUFF((SELECT ';' + Field1 FROM #temp WHERE objectid = t.objectid ORDER BY field1 FOR XML PATH('')),1,1,'')) AS [field1],
(SELECT STUFF((SELECT ';' + Field2 FROM #temp WHERE objectid = t.objectid ORDER BY field2 FOR XML PATH('')),1,1,'')) AS [field2],
(SELECT STUFF((SELECT ';' + Field3 FROM #temp WHERE objectid = t.objectid ORDER BY field1 FOR XML PATH('')),1,1,'')) AS [field3]
FROM #temp t

you missed taking field1 within the STUFF clause from the correct table.
select
c.FLUSObjectNumber,
[field1] = stuff(
(
select ';' + c2.field1
from #temp as c2
where c2.objectid = c.objectid and c2.field1 = c.field1
for xml path(''), type
).value('.', 'varchar(max)'), 1, 1, ''
)
from #temp c
group by c.objectid, c.field1
;

Related

How to get below query

I have a table with one field name as City.
Below are the data.
City
--------
A
B
C
D
E
F
G
H
Output will be as Column as well
Write a query or stored procedure or function?
If a pass 2, the query result will be below
Col1 Col2
A B
C D
E F
G H
If a pass 3, the query result will be below
Col1 Col2 Col3
A B C
D E F
G H
If a pass 4, the query result will be below
Col1 Col2 Col3 Clo4
A B C D
E F G H
--Here script for creating and adding data into temp table
create table #Cities( City varchar(max) )
insert into #Cities values
( 'A' ),
( 'B' ),
( 'C' ),
( 'D' ),
( 'E' ),
( 'F' ),
( 'G' ),
( 'H' )
declare #what_I_pass as int = 2; -- Here pass the number you want.
with Q1 as
(
select *, ROW_NUMBER() over (order by City) - 1 as n
from #Cities
),
Q2 as
(
select City, n / #what_I_pass as rn, n % #what_I_pass as cn
from Q1
)
select (stuff((select ' ' + City from Q2 as q where q.rn = Q2.rn order by cn for xml path('')), 1, 1, '')) as Result
from Q2
group by rn
order by rn
This would do:
declare #Cols int = 3 -- We define here how many output columns we want: 1, 2, 3, ... up to 10
create table #Cities( City varchar(max) )
insert into #Cities values
( 'A' ),
( 'B' ),
( 'C' ),
( 'D' ),
( 'E' ),
( 'F' ),
( 'G' ),
( 'H' )
;
with Cities as (
select City,
row_number() over (order by City) - 1 as Num
from #Cities
)
select Col1.City as Col1, Col2.City as Col2, Col3.City as Col3, Col4.City as Col4, Col5.City as Col5, Col6.City as Col6, Col7.City as Col7, Col8.City as Col8, Col9.City as Col9, Col10.City as Col10
from Cities as Rows
left join Cities as Col1 on 0 < #Cols and Col1.Num = Rows.Num * #Cols + 0
left join Cities as Col2 on 1 < #Cols and Col2.Num = Rows.Num * #Cols + 1
left join Cities as Col3 on 2 < #Cols and Col3.Num = Rows.Num * #Cols + 2
left join Cities as Col4 on 3 < #Cols and Col4.Num = Rows.Num * #Cols + 3
left join Cities as Col5 on 4 < #Cols and Col5.Num = Rows.Num * #Cols + 4
left join Cities as Col6 on 5 < #Cols and Col6.Num = Rows.Num * #Cols + 5
left join Cities as Col7 on 6 < #Cols and Col7.Num = Rows.Num * #Cols + 6
left join Cities as Col8 on 7 < #Cols and Col8.Num = Rows.Num * #Cols + 7
left join Cities as Col9 on 8 < #Cols and Col9.Num = Rows.Num * #Cols + 8
left join Cities as Col10 on 9 < #Cols and Col10.Num = Rows.Num * #Cols + 9
where Rows.Num <= (select max(Num) from Cities) / #Cols
drop table #Cities
Result for an input of 3 :
Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8 Col9 Col10
A B C NULL NULL NULL NULL NULL NULL NULL
D E F NULL NULL NULL NULL NULL NULL NULL
G H NULL NULL NULL NULL NULL NULL NULL NULL
PS: To avoid using dynamic SQL this query always returns a fixed number of 10 columns, but just the desired columns are filled. Now your presentation layer only needs to hide the unwanted empty columns.
Try below query:
--Here script for creating and adding data into temp table
declare #tbl table ( City varchar(max) )
insert into #tbl values
( 'A' ),
( 'B' ),
( 'C' ),
( 'D' ),
( 'E' ),
( 'F' ),
( 'G' ),
( 'H' )
declare #howManyCols int = 3;
;with cte as (
select City, grp,
row_number() over (partition by grp order by City) rn
from (
select *,
(row_number() over (order by City) - 1) % #howManyCols + 1 grp
from #tbl
) a
)
-- this query can be generated by dynamic SQL, because you can see pattern in following lft join's
select c1.City, c2.City, c3.City
from cte c1
left join cte c2 on c1.grp = c2.grp - 1 and c1.rn = c2.rn
left join cte c3 on c2.grp = c3.grp - 1 and c2.rn = c3.rn
where c1.grp = 1
For number of columns equal to three you get:
Try this script,
declare #flg int=3--test with 1,2,3,4 ...
create table #temp(id int identity(1,1),col varchar(10))
insert into #temp values ('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H')
create table #temp1(id int ,col varchar(10),flag int,rownum int)
declare #PvtCol varchar(500)=''
declare #Headcol varchar(500)
declare #Sql nvarchar(500)
WITH CTE
AS (SELECT id,
col,
CASE
WHEN(id % #flg) = 0
THEN #flg
ELSE(id % #flg)
END flag
FROM #temp),
CTE1
AS (SELECT *,
ROW_NUMBER() OVER(PARTITION BY flag
ORDER BY id) rownum
FROM cte)
INSERT INTO #temp1
(id,
col,
flag,
rownum
)
SELECT id,
col,
flag,
rownum
FROM cte1;
SELECT #PvtCol = COALESCE(#PvtCol + ', ' + QUOTENAME(rownum), QUOTENAME(rownum)),
#Headcol = COALESCE(#Headcol + ', ' + QUOTENAME(rownum) + ' as ' + 'col', QUOTENAME(rownum) + ' as ' + 'col') + CAST(rownum AS VARCHAR)
FROM #temp1
WHERE flag = 1;
SET #PvtCol = STUFF(#PvtCol, 1, 1, '');
SELECT #PvtCol,
#Headcol;
SET #Sql = 'select flag,' + #Headcol + ' from
(
select flag, col,rownum from #temp1
)src
pivot(max(col) for rownum in(' + #PvtCol + ')) as pvt';
PRINT #Sql;
EXECUTE sp_executesql
#Sql;
DROP TABLE #temp, #temp1;

SQL complex dynamic Pivoting 2

Hi I am trying in SQL Server the pivoting for the following table
REFID | COL1 | COL2 | Sequence
1 abc cde 1
1 lmn rst 2
1 kna asg 3
2 als zkd 2
2 zpk lad 1
I want the output as
REFID | 1COL1 | 2COL1 | 3COL1 |1COL2|2COL2|3COL2
1 abc lmn kna cde rst asg
2 zpk als null lad zkd null
The number of columns in the original table are known but the number of rows are not known. Can any one help
If you want to include the sequence number as part of your column names, then you will still need to unpivot your col1 and col2 columns first, then apply the pivot. The difference is that you will concatenate the sequence number to your column names created during the unpivot process.
For a known number of values the query would be:
select REFID,
[1col1], [2col1], [3col1],
[1col2], [2col2], [3col2]
from
(
select REFID,
col = cast(Sequence as varchar(10))+ col, value
from yourtable
cross apply
(
select 'COL1', col1 union all
select 'COL2', col2
) c (col, value)
) d
pivot
(
max(value)
for col in ([1col1], [2col1], [3col1],
[1col2], [2col2], [3col2])
) piv
order by refid;
Then if you have an unknown number the dynamic SQL version will be:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(cast(Sequence as varchar(10))+ col)
from yourtable
cross apply
(
select 'Col1', 1 union all
select 'Col2', 2
) c(col, so)
group by Sequence, col, so
order by so, sequence
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT refid, ' + #cols + '
from
(
select REFID,
col = cast(Sequence as varchar(10))+ col, value
from yourtable
cross apply
(
select ''COL1'', col1 union all
select ''COL2'', col2
) c (col, value)
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p
order by refid'
execute sp_executesql #query;

Dynamic columns not getting created in T-SQL

I have the following tables:
tblengineeringlookupcolumnmaster
elccolumnid | elclookupcode | elccolumnname | elcisrequired
1 | 64 | FirstName | 1
2 | 64 | LastName | 1
3 | 65 | abc | 1
4 | 65 | xyz | 1
tblengineeringlookupdetail
eldrecordId | eldlookupcode |eldlookupsequence |eldlookupvalue | eldlookupvaluedescription
245 | 64 | 0 | Red | Aravinth,Arumugam
246 | 64 | 1 | Blue | Santhosh,Chandran
247 | 64 | 2 | Green | Karthik,Balasubramanian
When I pass '64' as the parameter to the procedure.
I got the output as:
FirstName | LastName | eldRecordId
-------------------------------------
Aravinth | Arumugam | 245
Santhosh | Chandran | 246
Karthik | Balasubramanian| 247
The Store procedure used is
//SP
-- Select the columns to be used
DECLARE #tcol TABLE
(
ID INT IDENTITY(1,1)
, elclookupcode INT
, elccolumnname VARCHAR(100)
)
-- Insert the records into the table
INSERT INTO #tcol (elclookupcode, elccolumnname)
SELECT elclookupcode,elccolumnname FROM tblEngineeringLookupColumnMaster WHERE elclookupcode=#LookupCode
-- Select the columns which should be as output as a table
DECLARE #temp TABLE
(
elcLookupCode INT
, RecordId INT
, txt VARCHAR(8000)
)
-- Select the records from the table and insert
INSERT INTO #temp (elcLookupCode, RecordId, txt)
SELECT eldLookupCode,eldRecordId, eldLookupValueDescription FROM tblEngineeringLookupDetail WHERE eldLookupCode=#LookupCode
DECLARE #SQL NVARCHAR(MAX)
-- Have a table for the selected values
;WITH cte AS
(
SELECT
token = ', [' + d2.elccolumnname + '] = ''' + d.token + ''''
, d.RecordId
FROM (
SELECT
token = t.c.value('.', 'VARCHAR(50)')
, a.RecordId
, a.elcLookupCode
, rn = ROW_NUMBER() OVER (PARTITION BY a.RecordId ORDER BY a.RecordId)
FROM (
SELECT
RecordId
, elcLookupCode
, txml = CAST('<t>' + REPLACE(txt, ',', '</t><t>') + '</t>' AS XML)
FROM #temp
) a
CROSS APPLY txml.nodes('/t') t(c)
) d
-- Select the columns to be mapped
JOIN (
SELECT
elclookupcode
, elccolumnname
, rn = ROW_NUMBER() OVER (PARTITION BY elclookupcode ORDER BY elclookupcode)
FROM #tcol
) d2 ON d.elcLookupCode = d2.elclookupcode AND d2.rn = d.rn
)
-- Join all the records taken
SELECT #SQL = STUFF((
SELECT CHAR(13) + 'UNION ALL SELECT '+ STUFF((
SELECT t2.token
FROM cte t2
WHERE t2.RecordId = t.RecordId
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 2, ''), ',[RecordId] = ' + CAST(RecordId AS VARCHAR(10))
FROM #temp t
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 11, '')
PRINT #SQL
EXEC sp_executesql #SQL
END
The question now is:
If I pass '65' as parameter to the procedure,Where there are values in the first table(tblengineeringlookupcolumnmaster) and no corresponding values in the second table(tblengineeringlookupdetail) I'm getting no results( A message showing 3rows affected).
But I have to get the columns headers alone.
'Sample:
xyz | abc | eldrecordId
NULL |NULL | NULL
Why i'm not getting this? Where i'm wrong?
Try this one -
DECLARE #tcol TABLE
(
ID INT IDENTITY(1,1)
, elclookupcode INT
, elccolumnname VARCHAR(20)
)
INSERT INTO #tcol (elclookupcode, elccolumnname)
VALUES
(65, 'FirstName'),
(65, 'LastName')
DECLARE #temp TABLE
(
elcLookupCode INT
, eldRecordId INT
, txt VARCHAR(100)
)
INSERT INTO #temp (elcLookupCode, eldRecordId, txt)
VALUES
(64, 245, 'Aravinth,Arumugam'),
(64, 246, 'Santhosh,Chandran'),
(64, 247, 'Karthik,Balasubramanian')
DECLARE #SQL NVARCHAR(MAX)
;WITH cte AS
(
SELECT
token = ', [' + d2.elccolumnname + '] = ''' + d.token + ''''
, d.eldRecordId
FROM (
SELECT
token = t.c.value('.', 'VARCHAR(50)')
, a.eldRecordId
, a.elcLookupCode
, rn = ROW_NUMBER() OVER (PARTITION BY a.eldRecordId ORDER BY a.eldRecordId)
FROM (
SELECT
eldRecordId
, elcLookupCode
, txml = CAST('<t>' + REPLACE(txt, ',', '</t><t>') + '</t>' AS XML)
FROM #temp
) a
CROSS APPLY txml.nodes('/t') t(c)
) d
LEFT JOIN (
SELECT
elclookupcode
, elccolumnname
, rn = ROW_NUMBER() OVER (PARTITION BY elclookupcode ORDER BY elclookupcode)
FROM #tcol
) d2 ON d.elcLookupCode = d2.elclookupcode AND d2.rn = d.rn
)
SELECT #SQL = STUFF((
SELECT CHAR(13) + 'UNION ALL SELECT [eldRecordId] = ' + CAST(eldRecordId AS VARCHAR(10)) + ', ' +
'[elcLookupCode] = ' + CAST(elcLookupCode AS VARCHAR(10)) + ISNULL(STUFF((
SELECT t2.token
FROM cte t2
WHERE t2.eldRecordId = t.eldRecordId
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 2, ', '), '')
FROM #temp t
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 11, '')
PRINT #SQL
EXEC sys.sp_executesql #SQL

SQL count ids in fields

I have a table contains IDs in field. It looks like:
FieldName
-------------------------
1,8,2,3,4,10,5,9,6,7
-------------------------
1,8
-------------------------
1,8
I need to count these IDs to get result:
ID | Count
---|------
1 | 3
8 | 3
2 | 1
3 | 1
Any ideas?
Thanks!
Try this :
Declare #demo table(FieldName varchar(100))
insert into #demo values('1,8,2,3,4,10,5,9,6,7')
insert into #demo values('1,8')
insert into #demo values('1,8')
select ID, COUNT(id) ID_count from
(SELECT
CAST(Split.a.value('.', 'VARCHAR(100)') AS INT) AS ID
FROM
(
SELECT CAST ('<M>' + REPLACE(FieldName, ',', '</M><M>') + '</M>' AS XML) AS ID
FROM #demo
) AS A CROSS APPLY ID.nodes ('/M') AS Split(a)) q1
group by ID
I like Devart's answer because of the faster execution. Here is a modified earlier answer to suite your need :
Declare #col varchar(200)
SELECT
#col=(
SELECT FieldName + ','
FROM #demo c
FOR XML PATH('')
);
;with demo as(
select cast(substring(#col,1,charindex(',',#col,1)-1) AS INT) cou,charindex(',',#col,1) pos
union all
select cast(substring(#col,pos+1,charindex(',',#col,pos+1)-pos-1)AS INT) cou,charindex(',',#col,pos+1) pos
from demo where pos<LEN(#col))
select cou ID, COUNT(cou) id_count from demo
group by cou
Try this one -
Query:
SET NOCOUNT ON;
DECLARE #temp TABLE (txt VARCHAR(8000))
INSERT INTO #temp (txt)
VALUES ('1,8,2,3,4,10,5,9,6,7'), ('1,8'), ('1,8')
SELECT
t.ID
, [Count] = COUNT(1)
FROM (
SELECT
ID =
SUBSTRING(
t.string
, number + 1
, ABS(CHARINDEX(',', t.string, number + 1) - number - 1)
)
FROM (
SELECT string = (
SELECT ',' + txt
FROM #temp
FOR XML PATH(N''), TYPE, ROOT).value(N'root[1]', N'NVARCHAR(MAX)')
) t
CROSS JOIN [master].dbo.spt_values n
WHERE [type] = 'p'
AND number <= LEN(t.string) - 1
AND SUBSTRING(t.string, number, 1) = ','
) t
GROUP BY t.ID
ORDER BY [Count] DESC
Output:
ID Count
----- -----------
1 3
8 3
9 1
10 1
2 1
3 1
4 1
5 1
6 1
7 1
Query cost:

PIVOT on hierarchical data

I have a table like this:
Id Name parentId
--------------------------------
5 Rollers 2
2 Paint and Brushes 1
1 Decorating NULL
Using PIVOT or any other single query, can I get an output like this:
cat1id cat1name cat2id cat2name cat3id cat3Name
------------------------------------------------------------------------
1 Decorating 2 Paint and Brushes 5 Rollers
You can use PIVOT, UNPIVOT and a recursive query to perform this.
Static Version, is where you hard-code the values to the transformed:
;with hd (id, name, parentid, category)
as
(
select id, name, parentid, 1 as category
from yourtable
where parentid is null
union all
select t1.id, t1.name, t1.parentid, hd.category +1
from yourtable t1
inner join hd
on t1.parentid = hd.id
),
unpiv as
(
select value, 'cat_'+cast(category as varchar(5))+'_'+ col col_name
from
(
select cast(id as varchar(17)) id, name, parentid, category
from hd
) src
unpivot
(
value for col in (id, name)
) un
)
select [cat_1_id], [cat_1_name],
[cat_2_id], [cat_2_name],
[cat_3_id], [cat_3_name]
from unpiv
pivot
(
max(value)
for col_name in ([cat_1_id], [cat_1_name],
[cat_2_id], [cat_2_name],
[cat_3_id], [cat_3_name])
) piv
See SQL Fiddle with Demo
Dynamic Version, the values are generated at run-time:
;with hd (id, name, parentid, category)
as
(
select id, name, parentid, 1 as category
from yourtable
where parentid is null
union all
select t1.id, t1.name, t1.parentid, hd.category +1
from yourtable t1
inner join hd
on t1.parentid = hd.id
)
select category categoryNumber
into #temp
from hd
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + quotename('cat_'+cast(CATEGORYNUMBER as varchar(10))+'_'+col)
from #temp
cross apply (select 'id' col
union all
select 'name' col) src
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = ';with hd (id, name, parentid, category)
as
(
select id, name, parentid, 1 as category
from yourtable
where parentid is null
union all
select t1.id, t1.name, t1.parentid, hd.category +1
from yourtable t1
inner join hd
on t1.parentid = hd.id
),
unpiv as
(
select value, ''cat_''+cast(category as varchar(5))+''_''+ col col_name
from
(
select cast(id as varchar(17)) id, name, parentid, category
from hd
) src
unpivot
(
value for col in (id, name)
) un
)
select '+#cols+'
from unpiv
pivot
(
max(value)
for col_name in ('+#cols+')
) piv'
execute(#query)
drop table #temp
See SQL Fiddle with Demo
The Results are the same for both:
| CAT_1_ID | CAT_1_NAME | CAT_2_ID | CAT_2_NAME | CAT_3_ID | CAT_3_NAME |
--------------------------------------------------------------------------------
| 1 | Decorating | 2 | Paint and Brushes | 5 | Rollers |