T-SQL query on dynamic field with pivot - sql-server

i have 3 table PhoneBook, DynamicField, PhoneBook_DynamicField_Rel
PhoneBook :
|---ID----|---Name----|
1 Reza
2 Ali
DynamicField :
|---ID----|--Caption--|
1 Job
2 Level
PhoneBook_DynamicField_Rel:
|---ID----|--PhoneBookID--|--DynamicFieldID--|---Value---|
1 1 1 Emp
2 1 2 1
3 2 1 SomeJob
i want execute query with this result :
|--PhoneBookID--|--Name--|--Job--|--Level--|
1 Reza Emp 1
2 Ali SomeJob NULL

Maybe something like this:
Test data:
CREATE TABLE #PhoneBook(ID INT,Name VARCHAR(100))
INSERT INTO #PhoneBook VALUES(1,'Reza'),(2,'Ali')
CREATE TABLE #DynamicField(ID INT,Caption VARCHAR(100))
INSERT INTO #DynamicField VALUES(1,'Job'),(2,'Level')
CREATE TABLE #PhoneBook_DynamicField_Rel(ID INT,PhoneBookID INT,
DynamicFieldID INT,Value VARCHAR(100))
INSERT INTO #PhoneBook_DynamicField_Rel
VALUES(1,1,1,'Emp'),(2,1,2,'1'),(3,2,1,'SomeJob')
Getting the colums
DECLARE #cols VARCHAR(MAX)
SELECT #cols=STUFF
(
(
SELECT
',' +QUOTENAME(tbl.Caption)
FROM
#DynamicField AS tbl
FOR XML PATH('')
)
,1,1,'')
Then the query like this:
DECLARE #query NVARCHAR(4000)=
N'SELECT
*
FROM
(
SELECT
*
FROM
(
SELECT
PhoneBook.ID,
PhoneBook.Name,
field.Caption,
rel.Value
FROM
#PhoneBook AS PhoneBook
JOIN #PhoneBook_DynamicField_Rel AS rel
ON PhoneBook.ID = rel.PhoneBookID
JOIN #DynamicField AS field
ON rel.DynamicFieldID=field.ID
) AS SourceTable
) AS p
PIVOT
(
MAX(Value) FOR Caption IN('+#cols+')
) AS pvt'
EXECUTE(#query)
Then in my case I will drop the temp tables:
DROP TABLE #PhoneBook
DROP TABLE #DynamicField
DROP TABLE #PhoneBook_DynamicField_Rel

What you need is dynamic pivot:
declare #collist nvarchar(max)
SET #collist = stuff((select distinct ',' + QUOTENAME(caption)
FROM DynamicField
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
declare #q nvarchar(max)
set #q = '
select PhoneBookID, Name, ' + #collist + '
from (
select PhoneBookID, Name, Caption, Value
from (
select PhoneBookID, Name, Caption, Value
from PhoneBook pb
inner join PhoneBook_DynamicField_Rel pbdf on pb.ID = pbdf.PhoneBookID
inner join DynamicField df on df.ID = pbdf.DynamicFieldID
) as x
) as source
pivot (
max(Value)
for caption in (' + #collist + ')
) as pvt
'
exec (#q)

Related

Convert Row data (Dates ) in Column headers in SQL

hello friends i m trying to convert row Data with dates in column headers
this is my sql attendanceData Table
Normal Table
Original Data
and i want to convert row dates into column headers like this
Transpose
can someone tell me how i can do this in sql
i m sure using pivot i can,
so i tried sql query to pivot
SELECT Enterprise_ID, Date, Attendance
FROM attendanceData natural
join ( SELECT Enterprise_ID
FROM attendanceData
group BY Enterprise_ID
)
pivot (max(P) for Attendance in ('P' as P, 'WO' as WO)
I tried using dynamic pivot
Sample script to generate data:
create table #a
(
entrprise_id varchar(50),
datee date,
attendance varchar(10)
)
insert into #a values
('james','2018-12-22','p'),
('james','2018-12-23','p'),
('pick','2018-12-23','p'),
('rick','2018-12-23','p'),
('pick','2018-12-22','p'),
('rick','2018-12-22','p')
PIVOT Query:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.datee)
FROM #a c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT entrprise_id, ' + #cols + ' from
(
select*
from #A
) x
pivot
(
max(attendance)
for datee in (' + #cols + ')
) p '
execute(#query)
or
select *
from
(
select *
from #A
) src
pivot
(
max(attendance)
for datee in ([2018-12-22], [2018-12-23])
) piv

Pivot getting multiple records

I'm working on pivot functionality.
I'm having 2 tables one is userinfo and another is hobbies.
My table will be as like below image.
I'm saving the hobbies with comma separated data in userinfo.
I want all the comma separated hobbies with their respective column names.
I tried, but getting indivdual records for each hobbies instead of the single row
as like below image.
My code is as follows :
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
--drop table Temptbl1
--drop table Temptbl2
IF EXISTS (SELECT * FROM Temptbl1)
drop table Temptbl1
IF EXISTS (SELECT * FROM Temptbl2)
drop table Temptbl2
SELECT * INTO Temptbl1 FROM UserInfo CROSS APPLY dbo.SplitData(Hobbies,',')
--select * from Temptbl1
Select * into Temptbl2 from Temptbl1 s,Hobbies h where s.DividedItem=h.Hid
--select * from Temptbl2
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(HName) from Hobbies
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')
print #cols
set #query = 'SELECT distinct UId,UName,UAge,Hid, '+#cols +' from ( select
* from Temptbl2 )
x
pivot
(max(DividedItem) for HName in (' + #cols + ') ) p '
select * from Temptbl2
exec(#query)
You are selecting all columns from the temptbl2 and you do not need them. The Hid column is selected there and it is making the rows unique and the pivot fails to aggregate the results correctly. Just change it to this:
set #query = 'SELECT distinct UId,UName,UAge, '+#cols +' from ( select UId,UName,UAge, DividedItem, HName
from Temptbl2 )
x
pivot
(max(DividedItem) for HName in (' + #cols + ') ) p '
And you will be OK.
Here is full working example on SQL Server 2016 SP1. I am using string_split to split the data and temporary table to store it. Also, change a little bit the join syntax (your is obsolete and should not be used). You can adapt the code below to work on your environment easily:
DECLARE #hobbies TABLE
(
[HiD] INT
,[HName] VARCHAR(12)
);
INSERT INTO #hobbies ([HiD], [HName])
VALUES (1, 'Reading')
,(2, 'Singing')
,(3, 'Dancing');
DECLARE #UserInfo TABLE
(
[UID] INT
,[UName] VARCHAR(12)
,[UAddress] VARCHAR(12)
,[UAge] TINYINT
,[Hobbies] VARCHAR(12)
)
INSERT INTO #UserInfo ([UID], [UName], [UAddress], [UAge], [Hobbies])
VALUES (1, 'Abc', 'addr1', 25, '2,3')
,(2, 'Def', 'addr2', 27, '1,2,3')
,(3, 'Ghi', 'addr3', 20, '1');
DROP TABLE IF EXISTS #TEST;
SELECT *
INTO #TEST
FROM #UserInfo UI
CROSS APPLY string_split(UI.[Hobbies], ',') HS
INNER JOIN #hobbies H
ON HS.[value] = h.[HiD]
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
Select #cols = STUFF((SELECT distinct ',' + QUOTENAME(HName) from #hobbies
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')
set #query = 'SELECT distinct UId,UName,UAge, '+#cols +' from ( select uid, uname, uage, hname, value
from #TEST )
x
pivot
(max(value) for HName in (' + #cols + ') ) p '
exec(#query)

Rows to Columns RDLC

i have data in below format. this data is coming through SQL Query.
i want to show it in below format either by query or by rdlc report.
You need to use dynamic SQL to make it.
From your expected result you can try to follow thoes step to make it.
use row_number function make row number by Name, because we need to join base on that row_number.
get the use MAX and MIN to make row number calendar table. from 1 to max(rn). the table can let use outer join
declare a var #tables to make the OUTER JOIN execute SQL (each LEFT JOIN maen a group of Crew#).
declare a var #col to make column, which you want to select (Employee) from each table.
then use execute dynamic execute it.
look like this.
create table T
(
Name varchar(50),
Employee VARCHAR(50)
)
insert into T values ('Crew#1','TR123');
insert into T values ('Crew#1','311');
insert into T values ('Crew#2','DDD');
insert into T values ('Crew#2','12121');
insert into T values ('Crew#1','SDDAS');
insert into T values ('Crew#3','31114312');
insert into T values ('Crew#3','DD14124D');
insert into T values ('Crew#3','1214124121');
insert into T values ('Crew#3','SDD412AS');
DECLARE #tables AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#col AS NVARCHAR(MAX);
SET #tables = STUFF((SELECT distinct ' LEFT JOIN ' + ' (SELECT * FROM CTE WHERE Name = '''+Name+''') '+QUOTENAME(Name)+' on t1.smallRN = '+QUOTENAME(Name)+'.rn'
FROM T
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #col = STUFF((SELECT distinct ', ' + QUOTENAME(Name)+'.Employee as '''+ QUOTENAME(Name) +''''
FROM T
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #col = substring(#col,1, len(#col))
set #query = '
WITH CTE AS (
SELECT *,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Name) rn
FROM T
),CTE1 AS(
SELECT MIN(rn) smallRN,MAX(rn) bigRN
FROM CTE
UNION ALL
SELECT smallRN+1,bigRN
FROM CTE1
WHERE smallRN < bigRN
)
SELECT '+#col+'
FROM CTE1 t1 ' + #tables
execute(#query)
sqlfiddle
Creatin tbale
First we will create a temp table where we will stock the data that you have and your table
create table #table1
(
[Crew Name] varchar(500) ,
Employee varchar(500)
)
INsert into #table1
values (....)
select * from #table1
Dynamic selection
then we will create a dynamic query to get the columns that we have, that way we can add as much crews as we want,
declare #DynamicPivotQuery as nvarchar(max)
declare #ColumnName as nvarchar(max)
select #ColumnName = ISNULL(#ColumnName +',','') + QUOTENAME([Crew Name])
from (select distinct [Crew Name] from #table1) as Country
set #DynamicPivotQuery = N'select ' +#ColumnName + '
from #table1
Pivot ( MAX(Employee)
FOR [Crew Name] in (' +#ColumnName+')) as Pivoted
'
exec (#DynamicPivotQuery)
this way we will get only the first row for every column
so we have to find a way to aggregate and get the other columns as well just to demonstrate i will union the Mmin also this is where i stoped my testes but you can do more then this with some testes
now the union :
declare #DynamicPivotQuery as nvarchar(max)
declare #ColumnName as nvarchar(max)
select #ColumnName = ISNULL(#ColumnName +',','') + QUOTENAME([Crew Name])
from (select distinct [Crew Name] from #table1) as Country
set #DynamicPivotQuery = N'select ' +#ColumnName + '
from #table1
Pivot ( MAX(Employee)
FOR [Crew Name] in (' +#ColumnName+')) as Pivoted
union
select ' +#ColumnName + '
from #table1
Pivot ( MIN(Employee)
FOR [Crew Name] in (' +#ColumnName+')) as Pivoted
'
exec (#DynamicPivotQuery)
here is the result :
if you follow this way i'm sure that you will find a way to union all the result
You can add this result into a temp table
then add a column which will be a reference into this temp table
then use pivot function
To know more about pivot Visit :
https://msdn.microsoft.com/en-us/azure/data-lake-analytics/u-sql/pivot-and-unpivot-u-sql
you can use also SSIS to a very handy tool and easy to use
Using dynamic PIVOT if you dont have a set Crew columns.
DECLARE #ColumnString VARCHAR(256)
DECLARE #ColumnHeadrer VARCHAR(256)
DECLARE #sql varchar(1000)
CREATE TABLE #ColumnValue
(
Value VARCHAR(500),
ColumnHeader VARCHAR(256)
)
INSERT INTO #ColumnValue (Value, ColumnHeader)
SELECT DISTINCT '[' + CrewName + ']',
'ISNULL(' + CrewName + ','''') AS ' + CrewName
FROM CrewTable
SELECT #ColumnString = COALESCE(#ColumnString + ',', '') + Value,
#ColumnHeadrer = COALESCE(#ColumnHeadrer + ',', '') + ColumnHeader
FROM #ColumnValue
SET #sql =
'
SELECT ' + #ColumnHeadrer + '
FROM
(
SELECT Employee,
CrewName,
ROW_NUMBER() OVER(PARTITION BY CrewName ORDER BY CrewName) AS rnk
FROM CrewTable
) AS P
PIVOT
(
MAX(Employee) FOR [CrewName] IN ('+#ColumnString+')
) AS pv
'
EXEC (#sql)

how to make the query pick distinct values from a column as pivot values?

Query:
select employee_sk,
[201701],
[201702]
from
(select Employee_SK, Period_NK, CalcClinical_FTE from cmgr.FACT_Payroll) as sourcetable
pivot
(
sum(CalcClinical_FTE)
for period_nk
in ([201701],[201702])
) as a
I have multiple periods in the period_nk column ranging from 201401 to 201801.
So, how can i assign the pivot values without having to write each column individually like 201701,201702, 201703...?
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.period_nk)
FROM temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT category, ' + #cols + ' from
(
select category,
period_nk,
amount
from temp
) x
pivot
(
max(amount)
for period_nk in (' + #cols + ')
) p '
execute(#query)
drop table temp
Output :
category 201701 201702 201703 201704 201705
1 ABC 1000,0000 NULL NULL NULL 1100,0000
2 DEF NULL 500,0000 NULL 700,0000 NULL
3 GHI NULL NULL 800,0000 NULL NULL
online demo

SQl server Dynamic query

I have a table Connection
KEY Base_TBL Connected_table Base_tbl_colmn Connected_table_colmn
---- ------ ------------- ------------ ------------------
PRM Table1 Table2,Table3 colm1 Colm2,colm3
FRN Table4 table5 colm4 colm5
I need to generate a dynamic Query which should give an output something like this
select * from table1
INNER JOIN Table 2
ON table1.colm1 =table2.colm2
INNER JOIN Table3
on Table1.colm1=tabl3.colm3
number of tables in Connected_table can be anything and i have to join on the basis of that.
I have tried REPLACE Function within The dynamic query but not getting the desired result.
CREATE PROCEDURE [dbo].jointables
[Key] nvarchar(10)
AS
BEGIN
SET NOCOUNT ON
SET ROWCOUNT 0
DECLARE #sql as nvarchar(4000)
select #sql= 'select * from '+ Base_TBL + 'inner join ' +
Please guide me how to proceed further
CREATE FUNCTION [dbo].[FN_SPLIT](#Long_str varchar(max),#split_str varchar(100))
RETURNS #tmp TABLE(
ID inT IDENTITY PRIMARY KEY,
SPLIT varchar(max)
)
AS
BEGIN
DECLARE #sxml XML
SET #sxml='<root><node>'+REPLACE(#Long_str,#split_str,'</node><node>')+'</node></root>'
INSERT INTO #tmp([SPLIT])
SELECT b.value('.','varchar(max)') FROM #sxml.nodes('root/node') AS s(b)
RETURN
END
GO
DECLARE #sql as nvarchar(4000)
IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp
;WITH testdata AS
(
select 'PRM' AS [Key],'Table1' AS Base_TBL,'Table2,Table3' AS Connected_table,'colm1' AS Base_tbl_colmn,'Colm2,colm3' AS Connected_table_colmn
)
SELECT * INTO #temp FROM testdata
select #sql= 'select * from '+ Base_TBL +' ' +t.joinstring
FROM #temp
CROSS APPLY (
SELECT STUFF((
SELECT ' INNER JOIN '+ ft.[SPLIT] + ' ON ' + Base_TBL+'.'+Base_tbl_colmn +'='+ft.[SPLIT]+'.'+fc.[SPLIT]
FROM
dbo.FN_SPLIT(Connected_table,',') AS ft
INNER JOIN dbo.FN_SPLIT(Connected_table_colmn,',') AS fc ON fc.ID=ft.ID
FOR XML PATH('')),1,1,'') AS joinstring
) AS t
SELECT #sql
Below example just process one line
when process multiple lines, you can use another stuff function combine multiple lines
SELECT STUFF((
select ' select * from '+ Base_TBL +' ' +t.joinstring
FROM #temp
CROSS APPLY (
SELECT STUFF((
SELECT ' INNER JOIN '+ ft.[SPLIT] + ' ON ' + Base_TBL+'.'+Base_tbl_colmn +'='+ft.[SPLIT]+'.'+fc.[SPLIT]
FROM
dbo.FN_SPLIT(Connected_table,',') AS ft
INNER JOIN dbo.FN_SPLIT(Connected_table_colmn,',') AS fc ON fc.ID=ft.ID
FOR XML PATH('')),1,1,'') AS joinstring
) AS t FOR XML PATH('')
),1,1,'')

Resources