Convert one column into one row in T-SQL - sql-server

I have a select statement which yields a single large column. What I would like to do is convert the one column into one row so that I can feed it into a another stored procedure. How could I transpose the table so that it is one single row? So far, I have tried UNIPIVOT but have not been able to get it working.
Here is the format of the current table (in reality, it's much longer with a variable amount of rows):
+------+
| Col1 |
+------+
| 1 |
| 56 |
| 83 |
| 345 |
| 4322 |
| 4456 |
+------+
which is stored in a local table #localtable
I would like to turn the above table into the below table:
+------+------+------+------+------+------+
| Col1 | Col2 | Col3 | Col4 | col5 | col6 |
+------+------+------+------+------+------+
| 1 | 56 | 83 | 345 | 4322 | 4456 |
+------+------+------+------+------+------+
as an intermediate step in converting it into the following comma-delimited string:
'1, 56, 83, 345, 4322, 4456'
With the goal of feeding it into an exec like:
exec myfunction '1, 56, 83, 345, 4322, 4456'

I was able to solve this with the following query, creating my desired comma-delimited string:
SELECT STUFF((SELECT ',' + CAST(Col1 AS VARCHAR(50))
FROM #localtable
FOR XML PATH('')), 1, 1, '') AS listStr

You can use string_agg() in the most recent versions of SQL Server:
select string_agg(col, ', ')
from #localtable;

Here is another solution that will work with older SQL Server version (where no string_agg is available):
--create test table
declare #tmp table (Col1 int)
--populate test table
insert into #tmp
values
( 1)
,( 56)
,( 83)
,( 345)
,( 4322)
,( 4456)
--declare a variable that will contain the final result
declare #result varchar(4000) = ''
--populate final result from table
select #result = #result + ', ' + cast(Col1 as varchar) from #tmp
--remove first unnecessary comma
set #result = STUFF(#result, 1, 1, '')
--print result
select #result as result
Result:

Related

How to SQL PIVOT on two columns and with dynamic column names?

I have a key value pair set of rows to associate to a unique identifier (ApplicationId).
The data would look something like this:
| ApplicationId | Key | Value | Date |
| 123 | A | abc | 2020-3-1 14:00:01.000 |
| 123 | B | abd | 2020-3-1 14:00:02.000 |
| 123 | C | abe | 2020-3-1 14:00:03.000 |
| 124 | A | abf | 2020-3-1 14:01:00.000 |
| 124 | D | abg | 2020-3-1 14:01:01.000 |
The end result i'm looking for would be this:
| ApplicationId | A | A_Date | B | B_Date | C | C_Date | D | D_Date |
| 123 | abc | 2020-3-1 14:00:01.000 | abd | 2020-3-1 14:00:02.000 | abe | 2020-3-1 14:00:03.000 | NULL | NULL |
| 124 | abf | 2020-3-1 14:01:00.000 | NULL | NULL | NULL | NULL | abg | 2020-3-1 14:01:01.000 |
The Keys A,B,C,D are unknown so hard coding the column names isn't possible.
Here is something that works with one PIVOT
IF OBJECT_ID('tempdb.dbo.#_BLAH') IS NOT NULL DROP TABLE #_BLAH
SELECT et.[ApplicationId] et.[Key], et.[Value], et.[Date]
INTO #_BLAH
FROM ExampleTbl et
WHERE et.[Date] > DATEADD(dd, -1, GetDate())
DECLARE #_cols AS NVARCHAR(MAX)
DECLARE #_sql AS NVARCHAR(MAX)
SELECT
#_cols += QUOTENAME([Key]) + ','
FROM
#_BLAH
GROUP BY
[Key];
SET #_cols = STUFF((SELECT ',' + QUOTENAME(T.[Key])
FROM #_BLAH AS T
GROUP BY T.[Key]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
set #_sql = 'SELECT [ApplicationId], ' + #_cols + '
FROM ( SELECT * FROM #_BLAH) AS SRC
PIVOT ( MAX([Value]) FOR [Key] IN (' + #_cols + ') ) AS p';
EXEC(#_sql)
I've so far been unable to find an example or an article attempting to make a second dynamic column and adding in the value that relates the specific Key in my example.
My SQL above will accomplish creating the row i want except for the #_Date column i need.
Try this:
DROP TABLE IF EXISTS #DataSource;
DROP TABLE IF EXISTS #DataSourcePrepared;
CREATE TABLE #DataSource
(
[ApplicationId] INT
,[Key] CHAR(1)
,[Value] VARCHAR(12)
,[Date] DATETIME2(0)
);
INSERT INTO #DataSource ([ApplicationId], [Key], [Value], [Date])
VALUES (123, 'A', 'abc', '2020-3-1 14:00:01.000')
,(123, 'B', 'abd', '2020-3-1 14:00:02.000')
,(123, 'C', 'abe', '2020-3-1 14:00:03.000')
,(124, 'A', 'abf', '2020-3-1 14:01:00.000')
,(124, 'D', 'abg', '2020-3-1 14:01:01.000');
CREATE TABLE #DataSourcePrepared
(
[ApplicationId] INT
,[ColumnName] VARCHAR(32)
,[Value] VARCHAR(32)
)
INSERT INTO #DataSourcePrepared ([ApplicationId], [ColumnName], [Value])
SELECT [ApplicationId]
,[Key]
,[value]
FROM #DataSource
UNION ALL
SELECT [ApplicationId]
,[Key] + '_Date'
,CONVERT(VARCHAR(19), [Date], 121)
FROM #DataSource;
DECLARE #DymanimcTSQLSatement NVARCHAR(MAX)
,#DynamicColumns NVARCHAR(MAX);
SET #DynamicColumns = STUFF
(
(
SELECT ',' + QUOTENAME([ColumnName])
FROM #DataSourcePrepared
GROUP BY [ColumnName]
ORDER BY [ColumnName]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1
,1
,''
);
SET #DymanimcTSQLSatement = N'
SELECT *
FROM #DataSourcePrepared
PIVOT
(
MAX([value]) FOR [ColumnName] IN (' + #DynamicColumns +')
) PVT;';
EXECUTE sp_executesql #DymanimcTSQLSatement;
You just need to prepare the data before the actual PIVOT. Also, note that I am ordering the columns when I am building the dynamic part by name. In your real case, you may want to change this to something complex.
you can try this
DECLARE #_cols AS NVARCHAR(MAX) =''
DECLARE #_sql AS NVARCHAR(MAX)
SELECT
#_cols +=','+ QUOTENAME([Key]) + ',' + QUOTENAME([Key]+'_Date')
FROM
(SELECT DISTINCT [Key] FROM ExampleTbl) T
SET #_cols = STUFF(#_cols,1,1,'')
set #_sql = 'SELECT * FROM (
SELECT ApplicationId, [Key], Value FROM ExampleTbl
UNION ALL
SELECT ApplicationId, [Key] + ''_Date'' AS [Key], CONVERT(VARCHAR(30), [Date],121 ) AS Value FROM ExampleTbl
) SRC
PIVOT (MAX(Value) FOR [Key] IN ('+#_cols +' )) AS PVT';
EXEC(#_sql)
Result:
ApplicationId A A_Date B B_Date C C_Date D D_Date
------------- ------- --------------------------- ---------- -------------------------- ------------ ------------------------- ------- -------------------------
123 abc 2020-03-01 14:00:01.000 abd 2020-03-01 14:00:02.000 abe 2020-03-01 14:00:03.000 NULL NULL
124 abf 2020-03-01 14:01:00.000 NULL NULL NULL NULL abg 2020-03-01 14:01:01.000

Join multiple "exec" as one output SQL server

I'm tring to get multiple results from a dynamic exec to be columns of a table in a single output using SQL Server, right now the exec gives me multiple outputs as different temporal tables.
Is there a way to merge them together and get a single output?
This is my code:
Declare #status nvarchar(255)
Declare POINTER cursor global for select distinct status from intermedio
open POINTER
fetch NEXT from POINTER into #status
while (##FETCH_STATUS=0)
begin
exec('select distinct(
Select COUNT(*) from (
SELECT DISTINCT PKN
FROM INTERMEDIO
WHERE PIECE IN ('+'''+891''','''+75'''+') and ESTATUS = '''+#status+'''
group by PKN
having count(*)=2
) ser) as '+#status);
fetch next from POINTER into #status
end;
close POINTER;
DEALLOCATE POINTER;
Edit: Tried to explain myself better, once again excuse my poor english.
My table has three columns
+-----------+-------+---------+
| PKN | PIECE | ESTATUS |
+-----------+-------+---------+
| Set_one | +891 | A1 |
| Set_one | +75 | A1 |
| Set_one | +45 | A1 |
| Set_two | +891 | A3 |
| Set_two | +75 | A3 |
| Set_three | +700 | B1 |
+-----------+-------+---------+
I'm trying to get the count of the PKNs that have both (+891, +75) and count how many exist under ESTATUS
The output I'm expecting is something like:
+----+----+----+
| A1 | A3 | B1 |
+----+----+----+
| 1 | 1 | 0 |
+----+----+----+
But it gives me the rows on different tables.
The reason I'm doing it like this is because the table can have many different ESTATUS at any time, and many different pieces on different PKN, the result will change constantly and the table generated will be different each time I execute the query.
I'm sorry if the question isn't clear, as english is not my first language.
You are looking for a dynamic pivot and a conditional aggregation. Here's an example.
create table #tempTable (PKN varchar(64), PIECE varchar(64), ESTATUS varchar(2))
insert into #tempTable
values
('Set_one','+891','A1'),
('Set_one','+75','A1'),
('Set_one','+45','A1'),
('Set_two','+891','A3'),
('Set_two','+75','A3'),
('Set_three','+700','B1')
select
ESTATUS
--here is the conditional aggregation
,floor(count(case when PIECE = '+891' or PIECE = '+75' then PKN end)/2) as CT
into #staging
from
#tempTable
group by ESTATUS
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME(ESTATUS)
FROM (SELECT DISTINCT ESTATUS FROM #staging) AS ESTATUS
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT ' + #ColumnName + '
FROM #staging
PIVOT(Sum(CT)
FOR ESTATUS IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
drop table #tempTable
drop table #staging
RETURNS
+----+----+----+
| A1 | A3 | B1 |
+----+----+----+
| 1 | 1 | 0 |
+----+----+----+

SQL Server Transpose Rows into Columns using IDs as Column Names

I have a large file that has the following fields:
Table 1:
+---------+--------+-----------+
| User_Id | Key_Id | Value |
+---------+--------+-----------+
| 100 | 74 | 37 |
| 100 | 65 | Male |
| 100 | 279 | G235467 |
+---------+--------+-----------+
and I have another file that tells what each 'Key_Id' is called (they are column names) e.g.
Table 2:
+--------+------------------+
| Key_Id | Key |
+--------+------------------+
| 65 | Gender |
| 66 | Height |
| 74 | Age |
| 279 | ReferenceNo |
I want to create a table using the Key_Id names found in the Key column of table 2, transpose all of the values from table 1 into table 2, but also include the User_Id from table 1 as this relates to an individual.
PS. Table 2 has nearly 300 keys that would need turning into individual fields
So ultimately I would like a table that looks like this:
+---------+---------+--------+-------+--------------+--------+
| User_Id | Gender | Height | Age | ReferenceNo | etc |
+---------+---------+--------+-------+--------------+--------+
| 100 | Male | | 37 | G235467 | |
So that each User_Id is a row and that all the Keys are columns with their respective values
You can use a dynamic sql query as below.
Query
declare #sql as varchar(max);
select #sql = 'select t1.[User_Id], ' + stuff((select +
', max(case t2.[Key_Id] when ' + cast([Key_Id] as varchar(100)) +
' then t1.[Value] end) as [' + [Key] + '] '
from Table2
for xml path('')
), 1, 2, '') +
'from Table1 t1 left join Table2 t2 on t1.[Key_Id] = t2.[Key_Id] group by t1.[User_Id];'
exec(#sql);
Find a demo here
You need to get a coma-separated list of those 300 key names to be used in PIVOT/UNPIVOT operators in T-SQL like described here
https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot
you can use pivot as below:
Select * from (
Select u.UserId, k.[key], u.[Value] from table1 u
join table2 k on u.keyid = k.keyid ) a
pivot ( max([Value]) for [key] in ([Gender], [Height], [Age], [ReferenceNo]) ) p
For dynamic list of keys you can use dynamic sql as below:
Declare #cols1 varchar(max)
Declare #query nvarchar(max)
Select #cols1 = stuff((select ','+QuoteName([Key]) from table2 group by [Key] for xml path('')),1,1,'')
Set #Query = 'Select * from (
Select u.UserId, k.[key], u.[Value] from table1 u
join table2 k on u.keyid = k.keyid ) a
pivot ( max([Value]) for [key] in (' + #cols1 + ') ) p '
Select #Query --Check the generated query and execute by uncommenting below query
--exec sp_executesql #Query

use row value as column and columns as rows

I'm trying to create a query in which I use pivot to transform row values into columns. That I am able to do fine. But the requirement is to also use the other columns as row values.
The table and query I have tried can be checked here.
The required output is here.
From the query, I need the values of 'C' to be columns then the other columns (A, B, D & E) to be row values for the first column.
Is it possible to use one single pivot query to display the required output? If not, what would be the best approach to the problem?
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE Table1
(
A varchar(10),
B varchar(10),
C int,
D varchar(10),
E varchar(10)
)
GO
INSERT INTO Table1 VALUES('A1', 'B1', 1, 'D1', 'E1');
INSERT INTO Table1 VALUES('A2', 'B2', 2, 'D2', 'E2');
INSERT INTO Table1 VALUES('A3', 'B3', 3, 'D3', 'E3');
Query 1:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(C)
from Table1
group by C
order by C
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N';WITH CTE AS
(SELECT *
FROM Table1 UNPIVOT (Vals FOR COLUMNNAMES IN (A,B,D,E))up
)
SELECT *
FROM CTE
PIVOT (MAX(Vals)
FOR C
IN(' + #cols + N'))p '
exec sp_executesql #query
Results:
| COLUMNNAMES | 1 | 2 | 3 |
|-------------|----|----|----|
| A | A1 | A2 | A3 |
| B | B1 | B2 | B3 |
| D | D1 | D2 | D3 |
| E | E1 | E2 | E3 |

Need help pivoting some data

I'm hoping someone can help me. I'm trying to pivot some data on SQL Server 2005 and can't quite get the results I'm looking for.
This is my current table schema:
| ProductCode | AttributeName | AttributeValue |
| 1 | AttributeA | 10 |
| 1 | AttributeB | 20 |
| 2 | AttributeA | 30 |
| 2 | AttributeB | 40 |
| 3 | AttributeA | 50 |
This is the results I'm trying to achieve:
| ProductCode | AttributeA | AttributeB |
| 1 | 10 | 20 |
| 2 | 30 | 40 |
| 3 | 50 | NULL |
I know that I can achieve this result with the following SQL:
SELECT DISTINCT ProductCode,
(SELECT AttributeValue
FROM attributes
WHERE ProductName = 'AttributeA' AND ProductCode=a.ProductCode) AttributeA,
(SELECT AttributeValue
FROM attributes
WHERE ProductName = 'AttributeB' AND ProductCode=a.ProductCode) AttributeB,
FROM attributes a
Although that SQL does produce the result I'm looking for, it's obviously not dynamic (in reality, I not only have more Attribute Types, but different products have different sets of attributes) and it also scans the table 3 times. It's also a maintenance nightmare.
I tried using the PIVOT functionality of SQL Server, but with no luck.
Can anyone help?
create table #attributes (ProductCode int,
AttributeName varchar(20),
AttributeValue int)
insert into #attributes values (1, 'AttributeA', 10)
insert into #attributes values (1, 'AttributeB', 20)
insert into #attributes values (2, 'AttributeA', 30)
insert into #attributes values (2, 'AttributeB', 40)
insert into #attributes values (3, 'AttributeA', 50)
declare #attributes_columns nvarchar(max)
set #attributes_columns
= (
select ', [' + AttributeName + ']'
from
(
select distinct AttributeName as AttributeName
from #attributes
) t
order by t.AttributeName
for xml path('')
)
set #attributes_columns = stuff(#attributes_columns,1,2,'')
declare #sql nvarchar(max)
set #sql = N'
select ProductCode, <attributes_columns>
from
(select ProductCode, AttributeName, AttributeValue
from #attributes )p
pivot
(
sum(AttributeValue) for AttributeName in (<attributes_columns>)
) as pvt
'
set #sql = replace(#sql, '<attributes_columns>', #attributes_columns)
print #sql
exec sp_executesql #sql
drop table #attributes

Resources