SQL Server multiple rows and two columns into single row with multiple columns - sql

I have a table with a columns for case ID, Action, and reason.
a single case ID can have multiple rows with different actions and codes. I can pivot and get multiple rows with columns action1, action2, action3, etc., but for the life of me, can't get case id, action1, reason1, action2, reason2, etc on a single row.

If you need to go a little more dynamic (n reasons)
Drop Table #Temp
Declare #YourTable table (ID int,Action varchar(50),Reason varchar(50))
Insert Into #YourTable values
(1,'Load Data','Boss said to'),
(1,'Run Query','It is what I do'),
(2,'Take Garbage Out','Wife makes me')
-- Convert Data to EAV Structure'ish
Declare #XML xml = (Select *,GrpSeq = Row_Number() over (Partition By ID Order By (Select NULL)) from #YourTable for XML RAW)
Select ID = r.value('#ID','int')
,ColSeq = Row_Number() over (Partition By r.value('#ID','int') Order By (Select NULL))
,Element = attr.value('local-name(.)','varchar(100)')+r.value('#GrpSeq','varchar(10)')
,Value = attr.value('.','varchar(max)')
Into #Temp
From #XML.nodes('/row') as A(r)
Cross Apply A.r.nodes('./#*') AS B(attr)
Where attr.value('local-name(.)','varchar(100)') not in ('ID','GrpSeq')
-- Get Cols in correct Order
Declare #Cols varchar(max)
Set #Cols = Stuff((Select ',' + QuoteName(Element)
From (Select Distinct Top 100 Percent ColSeq,Element From #Temp Order By ColSeq ) A
For XML Path(''), Type
).value('.', 'varchar(max)'),1,1,'')
-- Execute Dynamic Pivot
Declare #SQL varchar(max) = '
Select *
From (Select ID,Element,Value From #Temp) T
Pivot (
max(Value)
For [Element] in (' + #Cols + ')
) P '
Exec(#SQL)
Returns

Related

Transform a SELECT * query to string

I have a query that returns a row
SELECT *
FROM table
WHERE id = 1;
I want to save the result into a nvarchar sql variable. I have seen similar questions Convert SQL Server result set into string but they only use select with the name of the columns, never with *.
select *
from table
where id = 1
for xml path ('')
However the answer is <column1>value1</column1> <column2>value2</column2> and I just want it to be value1, value2
Is there a way to achieve this? thank you!
If open to a helper function.
This will convert virtually any row, table or query to a string (delimited or not).
In the following examples I selected a PIPE delimiter with a CRLF line terminator.
Please note the usage and placement of _RN when a line terminator is required. Also note the ,ELEMENTS XSINIL ... this will included null values as empty string. If you want to exclude null values, simply omit the ,ELEMENTS XSINIL
Example as Entire Table or dbFiddle
Declare #YourTable Table (id int,[col_1] varchar(50),[col_2] varchar(50),[col_3] varchar(50),[col_n] varchar(50)) Insert Into #YourTable Values
(1,'data1','data2','data3','data4')
,(2,'data5','data6','data7','data8')
-- Entire Table
Declare #XML xml = (Select *,_RN=Row_Number() over (Order By (Select null)) From #YourTable for XML RAW,ELEMENTS XSINIL )
Select [dbo].[svf-str-Data-To-Delimited]('|',char(13)+char(10),#XML)
Returns
1|data1|data2|data3|data4
2|data5|data6|data7|data8
Example as Row Based
Select A.ID
,AsAString = [dbo].[svf-str-Data-To-Delimited]('|',char(13)+char(10),B.XMLData)
From #YourTable A
Cross Apply ( values ( (select a.* for xml RAW,ELEMENTS XSINIL )) )B(XMLData)
Returns
ID AsAString
1 1|data1|data2|data3|data4
2 2|data5|data6|data7|data8
The Function if Interested
CREATE Function [dbo].[svf-str-Data-To-Delimited] (#Delim varchar(50),#EOL varchar(50),#XML xml)
Returns varchar(max)
Begin
Return(
Select convert(nvarchar(max),(
Select case when Item='_RN' then ''
else case when nullif(lead(Item,1) over (Order by Seq),'_RN') is not null
then concat(Value,#Delim)
else concat(Value,#EOL)
end
end
From (
Select Seq = row_number() over(order by (select null))
,Item = xAttr.value('local-name(.)', 'nvarchar(100)')
,Value = xAttr.value('.','nvarchar(max)')
From #XML.nodes('/row/*') xNode(xAttr)
) A
Order By Seq
For XML Path (''),TYPE).value('.', 'nvarchar(max)') )
)
End
You can easily store the result as an XML string:
select *
from (values (1, 'x', getdate())) v(id, a, b)
where id = 1
for xml path ('');
Or as a JSON string:
select *
from (values (1, 'x', getdate())) v(id, a, b)
where id = 1
for json auto;
If you don't mind Using dynamic SQL (and INFORMATION_SCHEMA dictionary), for example, for SQL Server this works:
DECLARE #sql nvarchar(max) = '',
#result nvarchar(max),
#id int = 1
SELECT #sql += '+'',''+convert(nvarchar,' + QUOTENAME(column_name) +')' from INFORMATION_SCHEMA.columns where table_name = 'Student'
SET #sql = 'select #result=' + stuff(#sql,1,5,'') + ' from student where id = ' + CAST(#id as nvarchar)
EXECUTE sp_executesql #sql, N'#result nvarchar(max) OUTPUT', #result=#result OUTPUT
SELECT #result as MyOutput

SQL: How to create columns dynamically

I have a table which is created dynamically. So the number of columns is unknown at the time of creation. I want to create copies of each column in the same table with first column holding the first part of comma separated value, second column the second part and so on
For example,
ID Value1 Value2 .... Valuen
1 1;2;3 4;5;6
2 A;B;C D;E;F
I want to get the output like
ID Value1Copy1 Value1Copy2 Value1Copy3 Value2Copy1 Value2Copy2 Value2Copy3 .... ValuenCopy1
1 1 2 3 4 5 6
2 A B C D E F
I am unable to achieve this for variable number of columns
The following will dynamically unpivot your data. You may notice that the only field specified is ID.
The results are dropped into a #Temp table. From there we perform a dynamic pivot
Example
Declare #YourTable table (ID int,Value1 varchar(50),Value2 varchar(50))
Insert Into #YourTable values
( 1, '1;2;3','4;5;6'),
( 2, 'A;B;C','D;E;F')
Select A.ID
,Col = concat(C.Item,'Copy',D.RetSeq)
,Value = D.RetVal
Into #Temp
From #YourTable A --<< Replace with Your actual table
Cross Apply (Select XMLData = cast((Select A.* For XML Raw) as xml ) ) B
Cross Apply (
Select Item = attr.value('local-name(.)','varchar(100)')
,Value = attr.value('.','varchar(max)')
From B.XMLData.nodes('/row') as A(r)
Cross Apply A.r.nodes('./#*') AS B(attr)
Where attr.value('local-name(.)','varchar(100)') not in ('ID','Other2Exclude')
) C
Cross Apply (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(C.Value,';','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) D
Where A.ID is not null -- or any other WHERE statement
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(Col) From #Temp Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select *
From #Temp
Pivot (max(Value) For [Col] in (' + #SQL + ') ) p'
Exec(#SQL);
Returns

SQL Server dynamic columns creation

I have a table with column and values as below
How do I fetch the result as in the second tabular column with the DYNAMIC column names as -- first with "prgmg_product_id" and the rest of the column as "source ID 1","source ID 2", "source ID 3"
To achieve this using Dynamic SQL, the below will help:
CREATE TABLE #Prgmg (
prgmg_product_id INT
,source_id_other INT
);
INSERT #Prgmg (
prgmg_product_id
,source_id_other
)
VALUES (3310,11478)
,(3337,10833)
,(3354,11466)
,(4039,4846)
,(4039,65454)
,(4039,65456);
DECLARE #DYColumns NVARCHAR(1000)
,#DYSqlQuery NVARCHAR(4000);
-- CREATE THE COLUMNS REQUIRED
SET #DYColumns = STUFF((
SELECT DISTINCT ','
+ N'sourceID'
+ CAST(ROW_NUMBER() OVER (PARTITION BY prgmg_product_id ORDER BY prgmg_product_id, source_id_other) AS NVARCHAR(10))
FROM #Prgmg
FOR XML PATH('')
), 1, 1, '');
-- CREATE THE DYNAMIC SQL AND ADD IN THE CREATED COLUMNS
SET #DYSqlQuery = '
SELECT prgmg_product_id,'
+ #DYColumns
+ ' FROM (
SELECT prgmg_product_id
,CAST(N''sourceID'' + CAST(ROW_NUMBER() OVER (
PARTITION BY prgmg_product_id ORDER BY prgmg_product_id, source_id_other
) AS NVARCHAR(10)) AS NVARCHAR(100)) AS Col
,source_id_other
FROM #Prgmg S1
) X
PIVOT(MIN(source_id_other) FOR Col IN (' + #DYColumns + ')) P'
EXECUTE sp_executesql #DYSqlQuery;
Whilst this does offer you the solution, you should spend time understanding the concepts used. Such as the creation of the Columns required using ROW_NUMBER and the how that maps to the use of the PIVOT.
I had a little time to I tossed this together. I know that around SO the preference is to use a dynamic pivot. I do not much care for PIVOT in sql server. I find the syntax to be very obtuse. I tend to prefer a cross tab query (also known as conditional aggregation) instead. The added benefit is that this approach is almost always slightly quicker than a dynamic PIVOT.
You also have to realize that more than half of the code posted here is setting up the problem. In the future you should post ddl and sample data in a consumable format like this. It makes it so much easier for us to help.
if OBJECT_ID('tempdb..#Something') is not null
drop table #Something
create table #Something
(
prgmg_product_id int,
source_id_other int
)
insert #Something (prgmg_product_id, source_id_other) values
(3310, 11478),
(3337, 10833),
(3354, 11466),
(4039, 4846),
(4039, 65454),
(4039, 65456)
declare #StaticPortion nvarchar(2000) =
'with OrderedResults as
(
select *, ROW_NUMBER() over(partition by prgmg_product_id order by source_id_other) as RowNum
from #Something
)
select prgmg_product_id';
declare #DynamicPortion nvarchar(max) = '';
declare #FinalStaticPortion nvarchar(2000) = ' from OrderedResults Group by prgmg_product_id order by prgmg_product_id desc';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select #DynamicPortion = #DynamicPortion +
', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then source_id_other end) as SourceID' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <=
(
select top 1 Count(*)
from #Something
group by prgmg_product_id
order by COUNT(*) desc
)
declare #SqlToExecute nvarchar(max) = #StaticPortion + #DynamicPortion + #FinalStaticPortion;
exec sp_executesql #SqlToExecute

Convert dynamic text data rows into multiple columns

I'm trying to convert my result table which consist of multiple rows into multiple columns. below are my sample result before and after:
Before:
After:
Got a some idea from here but still no luck.
Update 1:
select #cols = STUFF((select ',' + QUOTENAME(institution) + ',' + QUOTENAME(intstatus)
From #temp
group by refno,frmstatus
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
i have no idea how to insert each column into the pivot query
set #query = 'select refno, frmstatus,' + #cols + '
from (
select refno, frmstatus, institution, intstatus from #temp
) x
pivot
(
???????
)
Please try the below query:
CREATE TABLE #temp(refno nvarchar(20), firmstatus nvarchar(20), institution nvarchar(20), intstatus nvarchar(20), ranking int)
DECLARE #qu NVARCHAR(MAX), #pcol NVARCHAR(MAX)
INSERT INTO #temp
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY refno ORDER BY institution ASC) AS ranking
FROM temp
SELECT #pcol=
STUFF((
SELECT
DISTINCT N', Institution'+ CAST (ranking AS NVARCHAR(25)) +', '+ N'Intstatus'+ CAST (ranking AS NVARCHAR(25))
FROM #temp
FOR XML PATH('')),1,1,'')
SET #qu=N'SELECT refno, firmstatus,'+ #pcol +
N' FROM
(
select refno,firmstatus,ColData,colheader+ CAST(ranking as varchar) as colnames from
(select * from #temp)s
UNPIVOT
(ColData for colheader in ([institution], [intstatus])) up
)S
PIVOT
(MAX(ColData) FOR colnames IN ('+#pcol +N')) AS piv'
EXEC sp_executesql #qu -- execute the dynamic sql
DROP TABLE #temp -- remove the temp table
The temp table in above script was created like below
--create table temp( refno nvarchar(20), firmstatus nvarchar(20), institution nvarchar(20), intstatus nvarchar(20))
--insert into temp values
--('AAA/1','Active','InstA','Ongoing'),
--('AAA/1','Active','InstB','Ongoing'),
--('AAA/1','Active','InstC','Ongoing'),
--('AAA/2','Active','InstA','Ongoing'),
--('AAA/2','Active','InstB','Ongoing')
Result received:
If repeated columns number is small and restricted, you can use simple solution:
WITH A AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY refno, fmstatus ORDER BY institution) n
FROM myTable
),
B AS (
SELECT refno, fmstatus,
CASE WHEN n=1 THEN institution END institution,
CASE WHEN n=1 THEN intstatus END intstatus,
CASE WHEN n=2 THEN institution END institution1,
CASE WHEN n=2 THEN intstatus END intstatus1,
CASE WHEN n=3 THEN institution END institution2,
CASE WHEN n=3 THEN intstatus END intstatus2
FROM A
)
SELECT refno, fmstatus,
MAX(institution) institution,
MAX(intstatus) intstatus,
MAX(institution1) institution1,
MAX(intstatus1) intstatus1,
MAX(institution2) institution2,
MAX(intstatus2) intstatus2
FROM B
GROUP BY refno, fmstatus
Otherwise use PIVOT

Adding a WHERE statement refering another table in Dynamic SQL

I currently have the following script which is pivoting results from rows into columns. It works a Great, apart from two issues;
I have a flag in another table which I want to filter by (basically: WHERE Table.Stats=YES). I'd normally do this in a basic query with an inner join followed by that WHERE statement. In the query below, I already have a WHERE FileSeq=25, which works, but this criteria I need to get working is calling on a different table.
The query is returning a lot of uncessary NULL fields.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(col+CAST(rn AS varchar(6)))
FROM
(
SELECT row_number() over(partition by UID ORDER BY ClassCode) rn
FROM dbo.StudentClasses
) d
CROSS APPLY
(
SELECT 'ClassCode', 1
) c (col, so)
GROUP BY col, rn, so
ORDER BY rn, so
FOR XML PATH(''), TYPE
).VALUE('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT UID,' + #cols + '
FROM
(
SELECT UID, col+CAST(rn AS varchar(10)) col, VALUE
FROM
(
SELECT UID, classcode,
row_number() over(partition by UID ORDER BY classcode) rn
FROM StudentClasses WHERE FileSeq=25
) t
CROSS APPLY
(
SELECT ''classcode'', CAST(classcode AS varchar(6))
) c (col, VALUE)
) x
PIVOT
(
MAX(VALUE)
for col in (' + #cols + ')
) p '
EXECUTE(#query)
Any assistance appreciated