SQL Transpose Data with Column Name - sql

I am trying to transpose data in a SQL Server table with one row of data but with several columns, all into one column along with their respective column headers.
Original Data Table:
**TABLE Column Names:** Id, ColumnA , ColumnB , ColumnC , StartDate
**Data:** 1, 'aa' , 'bb' , 'cc', 2016-10-10
Required Format of Data:
**ColumnName Values**
Id 1
ColumnA aa
ColumnB bb
ColumnC cc
StartDate 2016-10-10
CREATE DATABASE ToDelete
GO
USE [ToDelete]
GO
CREATE TABLE [dbo].[sourceData](
[id] [int] NULL,
[ColumnA] [varchar](50) NULL,
[ColumnB] [varchar](50) NULL,
[ColumnC] [varchar](50) NULL,
[StartDate] [datetime] NULL
)
GO
INSERT [dbo].[sourceData] ([id], [ColumnA], [ColumnB], [ColumnC], [StartDate], [EndDate]) VALUES (1, 'aa', N'bb', N'cc', GETDATE())
GO
The query I am using to pull the table column names is:
SELECT c.name
FROM sys.tables t
JOIN sys.columns c
ON t.object_id = c.object_id
WHERE t.name = 'sourceData'
Your help would be appreciated.
Thank you

Here is an option that may help you create something more like an EAV structure
Example
Declare #YourTable Table ([Id] varchar(50),[ColumnA] varchar(50),[ColumnB] varchar(50),[ColumnC] varchar(50),[StartDate] date)
Insert Into #YourTable Values
(1,'aa','bb','cc','2016-10-10')
Select A.ID
,C.*
From #YourTable A
Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
Cross Apply (
Select Field = a.value('local-name(.)','varchar(100)')
,Value = a.value('.','varchar(max)')
From B.XMLData.nodes('/row') as C1(n)
Cross Apply C1.n.nodes('./#*') as C2(a)
Where a.value('local-name(.)','varchar(100)') not in ('ID','OtherColumnsTo','Exclude')
) C
Returns
ID Field Value
1 ColumnA aa
1 ColumnB bb
1 ColumnC cc
1 StartDate 2016-10-10

Try the Following Solution, I have Referred from the following article to write this query:
https://www.red-gate.com/simple-talk/sql/t-sql-programming/questions-about-pivoting-data-in-sql-server-you-were-too-shy-to-ask/
USE [ToDelete]
GO
DECLARE #sql AS NVARCHAR(2000);DECLARE #col AS NVARCHAR(2000);
DECLARE #col1 AS NVARCHAR(2000);
SELECT #col = ISNULL(#col + ', ', '') +
concat('cast(',QUOTENAME(column_name),'as nvarchar(max))',' ',
QUOTENAME(column_name) ),#col1=ISNULL(#col1 + ', ', '') +QUOTENAME(column_name)
FROM (SELECT DISTINCT column_name FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='sourceData') AS Colname;
SET #sql =N'select columnname,[values] from (select '+#col+ ' from dbo.sourceData) as D Unpivot
([values] for columnname in (' + #col1 + '))
as unpiv'
EXEC sp_executesql #sql;

Simply use Union all like this:
select Id,'ColumnA' columnName, ColumnA [Values]
from t
union all
select Id,'ColumnB' , ColumnB
from t
union all
select Id,'ColumnC' , ColumnC
from t
union all
select Id,'StartDate' , cast(StartDate as nvarchar(max))
from t;
SQL Fiddle Demo

Additional variant using unpivot:
dataset
DECLARE #YourTable TABLE
([Id] VARCHAR(10),
[ColumnA] VARCHAR(10),
[ColumnB] VARCHAR(10),
[ColumnC] VARCHAR(10),
[StartDate] DATE
);
INSERT INTO #YourTable
VALUES
(1, 'aa', 'bb', 'cc', '2016-10-10'),
(2, 'cc', 'dd', 'zz', '2016-10-11');
query
SELECT Id,
Field,
[Value]
FROM
( SELECT Id,
ColumnA,
ColumnB,
ColumnC,
CONVERT(VARCHAR(10), StartDate) AS StartDate
FROM #YourTable
) AS t UNPIVOT([Value] FOR [Field] IN ( ColumnA,
ColumnB,
ColumnC,
StartDate)) up;

Related

Want to compare 4 different columns with the result of CTE

I have created a CTE (common table Expression) as follows:
DECLARE #N VARCHAR(100)
WITH CAT_NAM AS (
SELECT ID, NAME
FROM TABLE1
WHERE YEAR(DATE) = YEAR(GETDATE())
)
SELECT #N = STUFF((
SELECT ','''+ NAME+''''
FROM CAT_NAM
WHERE ID IN (20,23,25,30,37)
FOR XML PATH ('')
),1,1,'')
The result of above CTE is 'A','B','C','D','F'
Now I need to check 4 different columns CAT_NAM_1,CAT_NAM_2,CAT_NAM_3,CAT_NAM_4 in the result of CTE and form it as one column like follow:
Select
case when CAT_NAM_1 in (#N) then CAT_NAM_1
when CAT_NAM_2 in (#N) then CAT_NAM_2
when CAT_NAM_3 in (#N) then CAT_NAM_3
when CAT_NAM_4 in (#N) then CAT_NAM_4
end as CAT
from table2
When I'm trying to do the above getting error please help me to do.
If my approach is wrong help me with right one.
I am not exactly sure what you are trying to do, but if I understand the following script shows one possible technique. I have created some table variables to mimic the data you presented and then wrote a SELECT statement to do what I think you asked (but I am not sure).
DECLARE #TABLE1 AS TABLE (
ID INT NOT NULL,
[NAME] VARCHAR(10) NOT NULL,
[DATE] DATE NOT NULL
);
INSERT INTO #TABLE1(ID,[NAME],[DATE])
VALUES (20, 'A', '2021-01-01'), (23, 'B', '2021-02-01'),
(25, 'C', '2021-03-01'),(30, 'D', '2021-04-01'),
(37, 'E', '2021-05-01'),(40, 'F', '2021-06-01');
DECLARE #TABLE2 AS TABLE (
ID INT NOT NULL,
CAT_NAM_1 VARCHAR(10) NULL,
CAT_NAM_2 VARCHAR(10) NULL,
CAT_NAM_3 VARCHAR(10) NULL,
CAT_NAM_4 VARCHAR(10) NULL
);
INSERT INTO #TABLE2(ID,CAT_NAM_1,CAT_NAM_2,CAT_NAM_3,CAT_NAM_4)
VALUES (1,'A',NULL,NULL,NULL),(2,NULL,'B',NULL,NULL);
;WITH CAT_NAM AS (
SELECT ID, [NAME]
FROM #TABLE1
WHERE YEAR([DATE]) = YEAR(GETDATE())
AND ID IN (20,23,25,30,37,40)
)
SELECT CASE
WHEN EXISTS(SELECT 1 FROM CAT_NAM WHERE CAT_NAM.[NAME] = CAT_NAM_1) THEN CAT_NAM_1
WHEN EXISTS(SELECT 1 FROM CAT_NAM WHERE CAT_NAM.[NAME] = CAT_NAM_2) THEN CAT_NAM_2
WHEN EXISTS(SELECT 1 FROM CAT_NAM WHERE CAT_NAM.[NAME] = CAT_NAM_3) THEN CAT_NAM_3
WHEN EXISTS(SELECT 1 FROM CAT_NAM WHERE CAT_NAM.[NAME] = CAT_NAM_4) THEN CAT_NAM_4
ELSE '?' -- not sure what you want if there is no match
END AS CAT
FROM #TABLE2;
You can do a bit of set-based logic for this
SELECT
ct.NAME
FROM table2 t2
CROSS APPLY (
SELECT v.NAME
FROM (VALUES
(t2.CAT_NAM_1),
(t2.CAT_NAM_2),
(t2.CAT_NAM_3),
(t2.CAT_NAM_4)
) v(NAME)
INTERSECT
SELECT ct.NAM
FROM CAT_NAM ct
WHERE ct.ID IN (20,23,25,30,37)
) ct;

How to convert column into row with header sql

Note : i need the record like this
Scode value
BR 10
DL 7
One method is union all:
select 'br', br from t union all
select 'dl', dl from t union all
select 'AP', ap from t;
In a database that supports lateral joins, I would recommend something like this:
select v.*
from t cross apply
(values ('br', br), ('dl', dl), ('AP', ap)
) v(col, val);
use this query, it is dynamic and working.
CREATE TABLE [dbo].[temp](
[br] [int] NULL,
[dl] [int] NULL,
[ap] [int] NULL,
[hp] int null,
[kl] int null,
[mh] int null,
[py] int null
)
insert into temp values(10,7,7,7,8,8,0)
--Drop Table if exists #temp2
--drop table if exists #temp
create table #temp2(id int identity primary key,Scode varchar(10),value int)
declare #col varchar(max),#count int,#intcount int=1,#strquery varchar(max),#id int
select ROW_NUMBER() over (order by column_id) as RN,name into #temp from sys.columns where object_id=OBJECT_ID('temp')
select #count=count(1) from #temp
while(#intcount<=#count)
begin
set #strquery=''
set #col=''
set #id=0
select #col=name from #temp where RN=#intcount
set #strquery='select '+#col+' from temp'
insert into #temp2(value)
exec(#strquery)
select #id=SCOPE_IDENTITY()
update #temp2 set [Scode]=#col where id=#id
set #intcount=#intcount+1
end
select * from temp
select Scode,value from #temp2
Please go on this link and See the simple solution my problem is Solve.
https://www.codeproject.com/Answers/5259227/Convert-column-into-row-with-header-in-SQL#answer1
You could also use CROSS APPLY with UNION ALL to convert the columns:
select id, br,
dl,
ap
from yourtable
cross apply
(
select 'br', Indicator1 union all
select 'dl', Indicator2 union all
select 'ap', Indicator3
) c (indicatorname, indicatorvalue);

How to Pivot on two columns in SQL Server [duplicate]

This question already has an answer here:
TSQL PIVOT MULTIPLE COLUMNS
(1 answer)
Closed 4 years ago.
Below is the data rows I have:
ID Code OtherCol
7 Code1 NULL
7 code2 NULL
2 unk NULL
4 unk NULL
3 Code2 NULL
3 Code3 NULL
3 Code5 Other1
5 Code4 NULL
5 Code5 Other2
I am trying get this displayed as
ID name1 name2 name3 name4 name5 nameunk Othername
2 unk
3 code2 code3 code5 Other1
4 unk
5 code4 code5 Other2
7 code1 code2
I was able to pivot the first column but having a problem pivoting the second one.
And also there is a name for a given code, but the value under OtherCol are random.
I recommend conditional aggregation:
select id,
max(case when code = 'code1' then code end) as name1,
max(case when code = 'code2' then code end) as name2,
max(case when code = 'code3' then code end) as name3,
max(case when code = 'code4' then code end) as name4,
max(case when code = 'code5' then code end) as name5,
max(case when code = 'unk' then code end) as nameunk,
max(othercol) as othercol
from t
group by id;
This is full working example. You can change it a little bit to match your real data.
CREATE TABLE #DataSource
(
[ID] INT
,[Code] VARCHAR(12)
,[OtherCol] VARCHAR(12)
);
INSERT INTO #DataSource ([ID], [Code], [OtherCol])
VALUES (7, 'Code1', NULL)
,(7, 'code2', NULL)
,(2, 'Unk', NULL)
,(4, 'Unk', NULL)
,(3, 'Code2', NULL)
,(3, 'Code3', NULL)
,(3, 'Code5', 'Other1')
,(5, 'Code4', NULL)
,(5, 'Code4', 'Other2');
DECLARE #DynammicTSQLStatement NVARCHAR(MAX)
,#DynamicPIVOTColumns NVARCHAR(MAX);
SET #DynamicPIVOTColumns = STUFF
(
(
SELECT ',[' + CAST([value] AS VARCHAR(12)) + ']'
FROM
(
SELECT 0
,DENSE_RANK() OVER (ORDER BY [Code])
,REPLACE([Code], 'Code', 'name')
FROM #DataSource
WHERE [Code] IS NOT NULL
UNION
SELECT 1
,1
,'OtherCol'
) DS ([GroupID],[RowID], [value])
ORDER BY [GroupID], [RowID]
FOR XML PATH('') ,TYPE
).value('.', 'NVARCHAR(MAX)')
,1
,1
,''
);
SET #DynammicTSQLStatement = N'
SELECT *
FROM
(
SELECT [ID]
,[Code]
,REPLACE([Code], ''Code'', ''name'')
FROM #DataSource
UNION ALL
SELECT [ID]
,[OtherCol]
,''OtherCol''
FROM #DataSource
) DS ([ID], [value], [column])
PIVOT
(
MAX([value]) FOR [column] IN (' + #DynamicPIVOTColumns + ')
) PVT';
EXEC sp_executesql #DynammicTSQLStatement;
DROP TABLE #DataSource;
--PIVOT THE TABLE
select ID,[code1],[code2], [code3],[code4],[code5],[Unk]
into #resPivot
from
(
select ID, code
from tblTest
) src
pivot
(
max(code)
for code in ([code1], [code2], [code3],[code4],[code5],[Unk])
) piv;
--FIND ALL COLS WHERE OTHER COLUMN have value row 3,5 in your example
SELECT * INTO #distinct FROM tblTest where tblTest.otherCol IS NOT NULL
--PIVOTED RESULT WITH ABOVE TABLE
select distinct #resPivot.ID,[code1], [code2], [code3],[code4],[code5],[Unk],#distinct.otherCol
into #otherCol
from #resPivot inner join #distinct
on #distinct.id = #resPivot.id
--THIS IS PIVOTED RESULT WITH ALL RESULTS THAT HAS NO OTHER COL VALUE UNION with OTHER CALL VALUE
select distinct #resPivot.ID,[code1], [code2], [code3],[code4],[code5],[Unk],tblTest.otherCol
from #resPivot inner join tblTest
on tblTest.id = #resPivot.id
WHERE otherCol IS NULL and tblTest.ID NOT IN (SELECT ID FROM #otherCol)
UNION ALL
Select * from #otherCol
--DROP TEMP TABLES
Drop Table #resPivot
Drop Table #distinct
Drop Table #otherCol
A little simpler and faster version

SQL Server stored procedure store 2 table variable in 1 table with from and to values

I have this table stored in variable #oldValues and #newValues:
The two tables above will contain 1 row maximum. My goal is to insert this to a new table JSON TABLE:
DECLARE #jsonTable TABLE
(
[Field] nvarchar(max),
[From] nvarchar(max),
[To] nvarchar(max)
);
and store the from to values from old and new variable
Output must be like this:
[Field] [From] [To] // this is a column name
------------------------------------
CommitteeID 1 1
CommitteeName Test Test2
CommitteeMemberID 1 3
How can I achieve that?
Thanks in advance
It can be plain
select 'CommitteeId' [Field], (select cast(CommitteeId as varchar(max)) from #oldValues) [From], (select cast(CommitteeId as varchar(max)) from #newValues)[To]
union all
select 'CommitteeName', (select CommitteeName from #oldValues), (select CommitteeName from #newValues)
union all
select 'CommitteeId', (select cast(CommitteeMemberId as varchar(max)) from #oldValues), (select cast(CommitteeMemberId as varchar(max)) from #newValues)
If you have only one row:
select v.*
from #oldValues ov cross join
#newValues nv outer apply
(values ('CommitteeId', ov.CommitteeId, nv.CommitteeId),
('CommitteeName', ov.CommitteeName, nv.CommitteeName),
('CommitteeMemberID', ov.CommitteeMemberID, nv.CommitteeMemberID)
) v(field, [from], [to]);
Note: This assumes that the types for the values are all compatible. Otherwise, you may need to convert/cast values to strings.
EDIT:
To be explicit, the casts are:
select v.*
from #oldValues ov cross join
#newValues nv outer apply
(values ('CommitteeId', cast(ov.CommitteeId as nvarchar(255)), cast(nv.CommitteeId as nvarchar(255))),
('CommitteeName', cast(ov.CommitteeName as nvarchar(255)), cast(nv.CommitteeName as nvarchar(255))),
('CommitteeMemberID', cast(ov.CommitteeMemberID as nvarchar(255)), cast(nv.CommitteeMemberID as nvarchar(255)))
) v(field, [from], [to]);
I think UNPIVOT operator most proper solution for the need.
For UNPIVOT operation all column types should be same, that's why we cast all column type to the same.
DECLARE #oldValues as TABLE (CommitteeID INT, CommitteeName VARCHAR(20), CommitteeMemberID INT)
INSERT INTO #oldValues VALUES (1,'Test',1)
DECLARE #newValues as TABLE (CommitteeID INT, CommitteeName VARCHAR(20), CommitteeMemberID INT)
INSERT INTO #newValues VALUES (1,'Test2',3)
DECLARE #jsonTable TABLE
(
[Field] nvarchar(max),
[From] nvarchar(max),
[To] nvarchar(max)
);
;WITH FromTable AS (
SELECT [Field] , [From]
FROM (SELECT CAST(CommitteeID AS VARCHAR(255)) CommitteeID,
CAST(CommitteeName AS VARCHAR(255)) CommitteeName,
CAST(CommitteeMemberID AS VARCHAR(255)) CommitteeMemberID
FROM #oldValues) p
UNPIVOT ( [From] FOR [Field]
IN ( CommitteeID , CommitteeName , CommitteeMemberID)) as UNPVT
)
, ToTable AS (
SELECT [Field] , [To]
FROM (SELECT CAST(CommitteeID AS VARCHAR(255)) CommitteeID,
CAST(CommitteeName AS VARCHAR(255)) CommitteeName,
CAST(CommitteeMemberID AS VARCHAR(255)) CommitteeMemberID
FROM #newValues) p
UNPIVOT ( [To] FOR [Field]
IN ( CommitteeID , CommitteeName , CommitteeMemberID)) as UNPVT
)
SELECT F.*, T.[To] FROM FromTable F FULL JOIN ToTable T ON F.[Field] = T.[Field]
New columns can be easily added to SELECT and IN part of the query.
For easily determine missing column I used FULL JOIN

Unpivot table with multiple columns and dynamic column names

I am trying to unpivot a table with multiple rows and columns. Each row needs to be extratced to 2 rows with specific columns and the column names need to be renamed and a new column needs to added based on the columns selected!
I am including before and after sample data and a script to setup the data.
CREATE TABLE #tmpProducts (
ProductId INT,
ProductName nVARCHAR(100),
B2B_GrossRevenue DECIMAL(10,2),
B2B_DirectCost DECIMAL(10,2),
B2B_NetRevenue DECIMAL(10,2),
B2C_GrossRevenue DECIMAL(10,2),
B2C_DirectCost DECIMAL(10,2),
B2C_NetRevenue DECIMAL(10,2)
)
INSERT INTO #tmpProducts SELECT 1, 'Product1',1545.00,406.25,1138.75,195.00,35.10,159.90
INSERT INTO #tmpProducts SELECT 2, 'Product2',902.00,189.00,713.00,3280.00,590.40,2689.60
INSERT INTO #tmpProducts SELECT 3, 'Product3',15665.00,3988.39,11676.61,6247.00,1124.46,5122.54
INSERT INTO #tmpProducts SELECT 4, 'Product4',736.00,196.16,539.84,2395.00,431.10,1963.90
SELECT * FROM #tmpProducts
DROP TABLE #tmpProducts
CREATE TABLE #tmpProducts2 (
ProductId INT,
ProductName nVARCHAR(100),
[Type] nVARCHAR(3),
GrossRevenue DECIMAL(10,2),
DirectCost DECIMAL(10,2),
NetRevenue DECIMAL(10,2)
)
INSERT INTO #tmpProducts2 SELECT 1, 'Product1','B2B',1545.00,406.25,1138.75
INSERT INTO #tmpProducts2 SELECT 1, 'Product1','B2C',195.00,35.10,159.90
INSERT INTO #tmpProducts2 SELECT 2, 'Product2','B2B',902.00,189.00,713.00
INSERT INTO #tmpProducts2 SELECT 2, 'Product2','B2C',3280.00,590.40,2689.60
INSERT INTO #tmpProducts2 SELECT 3, 'Product3','B2B',15665.00,3988.39,11676.61
INSERT INTO #tmpProducts2 SELECT 3, 'Product3','B2C',6247.00,1124.46,5122.54
INSERT INTO #tmpProducts2 SELECT 4, 'Product4','B2B',736.00,196.16,539.84
INSERT INTO #tmpProducts2 SELECT 4, 'Product4','B2C',2395.00,431.10,1963.90
SELECT * FROM #tmpProducts2
DROP TABLE #tmpProducts2
I have attempted this but i can't get past the second column and im not sure how at add a new column with specific text, (probably dynamic sql but trying to avoid this if possible)
Here is the start of my attempt, any help would be much appreciated.
SELECT ProductId, ProductName,GrossRevenue
FROM (
SELECT ProductId, ProductName, B2B_GrossRevenue,B2C_GrossRevenue FROM #tmpProducts
) as t
UNPIVOT ( GrossRevenue for test IN (B2B_GrossRevenue,B2C_GrossRevenue)) AS unpvt
You just keep unpivoting
SELECT ProductId,ProductName, Substring(col1,1,3) as type, GrossRevenue, DirectCost, NetRevenue
FROM (
SELECT * FROM #tmpProducts
) as t
UNPIVOT ( GrossRevenue for col1 IN (B2B_GrossRevenue,B2C_GrossRevenue)) AS unpvt
unpivot ( DirectCost for col2 in (B2b_DirectCost, B2c_DirectCost)) up2
unpivot ( NetRevenue for col3 in (B2b_NetRevenue, B2c_NetRevenue)) up3
where SUBSTRING(col1,1,3)=SUBSTRING(col2,1,3)
and SUBSTRING(col1,1,3)=SUBSTRING(col3,1,3)
and join on the col columns to filter out mismatches
Here is a way to do this using Dynamic SQL. This will allow you to get all of the columns upon execution. Then you will not have to alter the query if you get any data changes. This does both an UNPIVOT and PIVOT:
DECLARE #colsUnPivot AS NVARCHAR(MAX),
#colsPivot AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SET #colsUnPivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('tmpProducts') and
C.name like 'B2%'
for xml path('')), 1, 1, '')
SET #colsPivot = stuff((select DISTINCT ','+quotename(right((C.name), len(C.name)-4))
from sys.columns as C
where C.object_id = object_id('tmpProducts') and
C.name like 'B2%'
for xml path('')), 1, 1, '')
set #query
= ' SELECT ProductId, ProductName, [type], ' + #colsPivot +'
FROM
(
SELECT ProductId, ProductName, substring(field, 1, 3) [type]
, value, right((field), len(field)-4) as col
from tmpProducts
unpivot
(
value
for field in (' + #colsUnPivot + ')
) unpvt
) x
PIVOT
(
sum(value)
FOR col IN (' + #colsPivot +')
)p'
execute(#query)
See SQL Fiddle with Demo
If you have multiple groups that you're looking to pivot around you can also do something like this:
Select *
from table
unpivot include nulls ( (test_name, test_date) for col_nm in ( (test1, date1) as 'test_val1'
,(test2, date2) as 'test_val2'
,(test3, date3) as 'test_val3'
)
)