SQL combine three rows into one column - sql

Is there any way to solve this? I am practicing SQL and I don't know how to do this.
Image table
----------------------------
| prdctCode | imgPath |
| P0003 | P0003-1.jpg |
| P0003 | P0003-2.jpg |
| P0003 | P0003-3.jpg |
| P0004 | P0004-1.jpg |
| P0004 | P0004-2.jpg |
| P0004 | P0004-3.jpg |
----------------------------
Product table
-------------------------
| prdctCode | prdctName |
| P0003 | Hand Bag |
| P0004 | Pencil |
-------------------------
What I get
---------------------------------------
| prdctCode | prdctName | imgPath |
| P0003 | Hand Bag | P0003-1.jpg |
| P0003 | Hand Bag | P0003-2.jpg |
| P0003 | Hand Bag | P0003-3.jpg |
| P0004 | Pencil | P0004-1.jpg |
| P0004 | Pencil | P0004-2.jpg |
| P0004 | Pencil | P0004-3.jpg |
---------------------------------------
Expected output
--------------------------------------------------------------------
| prdctCode | prdctName | imgPath1 | imgPath2 | imgPath3 |
| P0003 | Hand Bag | P0003-1.jpg | P0003-2.jpg | P0003-3.jpg |
| P0004 | Pencil | P0004-1.jpg | P0004-2.jpg | P0004-3.jpg |
--------------------------------------------------------------------
This is my code
select prdTbl.prdctCode, prdTbl.prdctName, imgTbl.imgPath
from [product_tbl]prdTbl left join [image_tbl]imgTbl
on prdTbl.prdctCode = imgTbl.prdctCode
I'm not sure if this is possible.

If you know for sure that for a product will always be maximum 4 images and that the image is always ending with .jpg, maybe this select can help:
select prdTbl.prdctCode, prdTbl.prdctName, imgTbl2.imgPath as Path1, imgTbl2.imgPath as Path2, imgTbl3.imgPath as Path3, imgTbl4.imgPath as Path4
from [product_tbl]prdTbl
left join [image_tbl]imgTbl1 on prdTbl.prdctCode = imgTbl1.prdctCode and imgTbl1.imgPath like '%-1.jpg'
left join [image_tbl]imgTbl2 on prdTbl.prdctCode = imgTbl2.prdctCode and imgTb12.imgPath like '%-2.jpg'
left join [image_tbl]imgTbl3 on prdTbl.prdctCode = imgTbl3.prdctCode and imgTbl3.imgPath like '%-3.jpg'
left join [image_tbl]imgTbl4 on prdTbl.prdctCode = imgTbl4.prdctCode and imgTbl4.imgPath like '%-4.jpg'

Please consider looking at that script :
CREATE TABLE #TempAppointmentTable (
UniqueID VARCHAR(MAX),
VAL nvarchar(500)
);
INSERT INTO #TempAppointmentTable
([UniqueID], [VAL])
VALUES
('P0003', 'P0003-1.jpg'),
('P0003', 'P0003-2.jpg'),
('P0003', 'P0003-3.jpg'),
('P0004', 'P0004-1.jpg'),
('P0004', 'P0004-2.jpg')
;
DECLARE #list_id TABLE (idx INT IDENTITY, id VARCHAR(MAX))
DECLARE #nb_rows INT
DECLARE #nb_cols INT
DECLARE #i INT = 0
DECLARE #j INT = 0
DECLARE #cur_id VARCHAR(MAX)
DECLARE #cur_nb_rows INT
DECLARE #sql VARCHAR(MAX)
INSERT INTO #list_id(id) SELECT [UniqueID] FROM #TempAppointmentTable GROUP BY [UniqueID] ORDER BY COUNT(*) DESC;
SELECT #nb_rows = COUNT(*) FROM #list_id;
SELECT #nb_cols = MAX(val) FROM (SELECT COUNT(*) FROM #TempAppointmentTable GROUP BY [UniqueID])t(val);
SET #sql = '
WITH TMP ([Row], [UniqueID], [VAL]) AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY [UniqueID] ORDER BY UniqueID), [UniqueID], [VAL] from #TempAppointmentTable
)'
WHILE #i < #nb_rows
BEGIN
SELECT #cur_id = id FROM #list_id WHERE idx = #i+1
SELECT #cur_nb_rows = COUNT(*) FROM #TempAppointmentTable WHERE [UniqueID] = #cur_id
IF #i != 0
SET #sql = #sql + '
UNION'
SET #sql = #sql + '
SELECT TMP' + CONVERT(VARCHAR,#i) + '0.[UniqueID], TMP' + CONVERT(VARCHAR,#i) + '0.[VAL]'
WHILE #j+1 < #nb_cols
BEGIN
SET #j = #j + 1
IF #j < #cur_nb_rows
SET #sql = #sql + ', TMP'+ CONVERT(VARCHAR,#i) + CONVERT(VARCHAR,#j) + '.[VAL] AS [VAL' + CONVERT(VARCHAR,#j) + ']'
ELSE
SET #sql = #sql + ', NULL'
END
SET #j = 0
SET #sql = #sql + '
FROM TMP AS TMP' + CONVERT(VARCHAR,#i) + '0'
WHILE #j+1 < #nb_cols
BEGIN
SET #j = #j + 1
IF #j < #cur_nb_rows
SET #sql = #sql + '
INNER JOIN TMP AS TMP' + CONVERT(VARCHAR,#i) + CONVERT(VARCHAR,#j) + ' ON TMP' + CONVERT(VARCHAR,#i) + CONVERT(VARCHAR,#j) + '.Row = ' + CONVERT(VARCHAR,#j+1) +' AND TMP' + CONVERT(VARCHAR,#i) + CONVERT(VARCHAR,#j) + '.UniqueID = ''' + CONVERT(VARCHAR,#cur_id) + ''''
END
SET #sql = #sql + '
WHERE TMP' + CONVERT(VARCHAR,#i) + '0.Row = 1 AND TMP' + CONVERT(VARCHAR,#i) + '0.UniqueID = ''' + CONVERT(VARCHAR,#cur_id) + ''''
SET #j = 0
SET #i = #i + 1
END
PRINT(#sql)
EXEC(#sql)
Drop table #TempAppointmentTable
The output is
UniqueID
VAL
VAL1
VAL2
P0003
P0003-1.jpg
P0003-2.jpg
P0003-3.jpg
P0004
P0004-1.jpg
P0004-2.jpg
NULL
As you can see it considers the number of elements, so the day you'll have 100 rows for a product (p0003 or any other) it will have 101 columns :D
You can modify that script for your neeeds
I hope it can feets your needs
For any other question I'll be right there

Related

How do I include an additional non-aggregated column for each of my in my PIVOT values?

I have the following code fragment which gives me the current results below. I'm attempting to add an additional column for each of my pivoted values in order to include the lastview data for each of my siteuserid / tagname combo (see expected results). Since this column isn't an aggregation, I don't believe an additional pivot would help. I've tried multiple ways of adding lastview, but it always results in additional rows rather than the desired output.
create table #taghits (userid int, email varchar(20), tagname varchar(20), hits int, lastview date)
insert into #taghits select 1, 'email1#here.com', 'tag1', 3, '2020-03-24';
insert into #taghits select 2, 'email2#here.com', 'tag1', 1, '2020-03-17';
insert into #taghits select 2, 'email2#here.com', 'tag2', 1, '2020-03-18';
insert into #taghits select 3, 'email3#here.com', 'tag1', 2, '2020-03-25';
insert into #taghits select 3, 'email3#here.com', 'tag2', 5, '2020-03-28';
select * from #taghits;
DECLARE #Columns3 as NVARCHAR(MAX)
SELECT #Columns3 = ISNULL(#Columns3 + ', ','') + QUOTENAME(TagName)
FROM (
select distinct TagName
from #taghits
) AS TagNames
ORDER BY TagNames.TagName
DECLARE #scolumns as NVARCHAR(MAX)
SELECT #scolumns = ISNULL(#Scolumns + ', ','')+ 'ISNULL(' + QUOTENAME(TagName) + ', 0) AS '+ QUOTENAME(TagName)
FROM (select distinct TagName
from #taghits) AS TagNames
ORDER BY TagNames.TagName
DECLARE #SQL as NVARCHAR(MAX)
SET #SQL = '
select userid, email, ' + #scolumns + '
from
(
select userid, email, tagname, hits
from #taghits
) as TagHits
PIVOT (
SUM(hits)
FOR TagName IN (' + #Columns3 + ')
) AS PivotTable
order by userId
'
exec sp_executesql #SQL;
Current Result
| userid | email | tag1 | tag2 |
|--------|-----------------|------|------|
| 1 | email1#here.com | 3 | 0 |
| 2 | email2#here.com | 1 | 1 |
| 3 | email3#here.com | 2 | 5 |
Desired Result
| userid | email | tag1_hits | tag1_lastview | tag2_hits | tag2_lastview |
|--------|-----------------|-----------|---------------|-----------|---------------|
| 1 | email1#here.com | 3 | 2020-03-24 | 0 | null |
| 2 | email2#here.com | 1 | 2020-03-17 | 1 | 2020-03-18 |
| 3 | email3#here.com | 2 | 2020-03-25 | 5 | 2020-03-28 |
try the following:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((select distinct ',
SUM(CASE WHEN tagname=''' + CAST(tagname as varchar(10)) + ''' THEN [hits] ELSE 0 END) AS [' + CAST(tagname as varchar(10)) + '_hits],
MAX(CASE WHEN tagname=''' + CAST(tagname as varchar(10)) + ''' THEN [lastview] ELSE NULL END) AS [' + CAST(tagname as varchar(10)) + '_lastview]'
/*---------------You can add other columns here similar to above--------------*/
FROM #taghits
FOR XML PATH(''),type).value('.','varchar(max)'),1,2,'')
SET #query = 'SELECT userid, email, ' + #Cols + ' FROM #taghits group by userid, email'
print (#query)
exec(#query)
Please see db<>fiddle here.

Dynamic Statement to Get Top "n" for Each Individual Column from Ms SQL Table

Help me with this Dynamic statement perform faster, the statement will fetch top n values for each column from a table.
The table will have an "n" number of columns but will have a primary key. NULLs couldn't have been avoided as any other value is considered as VALID and should go to the database.
Table
+-------+------+------+------+
| Depth | RPMA | ROP | WOB |
+-------+------+------+------+
| 6111 | 72 | 14.6 | 0 |
| 6110 | 72 | 14.1 | 1 |
| 6109 | 66 | 15.2 | NULL |
| 6108 | 68 | 14 | NULL |
| 6107 | 69 | 14 | NULL |
| 6106 | 61 | 14.8 | NULL |
| 6105 | 70 | NULL | NULL |
| 6104 | 64 | NULL | NULL |
| 6103 | 59 | NULL | NULL |
| 6102 | 49 | NULL | NULL |
+-------+------+------+------+
Result set,
+-------+------+------+------+
| Depth | RPMA | ROP | WOB |
+-------+------+------+------+
| 6111 | 72 | NULL | 0 |
| 6110 | 72 | NULL | 1 |
| 6109 | NULL | 15.2 | NULL |
| 6106 | NULL | 14.8 | NULL |
+-------+------+------+------+
Dynamic SQL used to get current result set,
DECLARE #Columns VARCHAR(MAX); -- Param1
DECLARE #IdxColumn VARCHAR(250); --Param2
DECLARE #Limit VARCHAR(11); --Param3
DECLARE #SQL NVARCHAR(MAX)=''; --Param4
DECLARE #query NVARCHAR(MAX) = ' SELECT TOP (' + #pLimit + ') ' + #IdxColumn + ', ' + #Columns + ' FROM [Table] WHERE '
SET #SQL = #query + REPLACE(#Columns,',', ' IS NOT NULL ORDER BY '+ #IdxColumn + ' ASC ' + N' UNION' + #query) + ' IS NOT NULL ORDER BY ' + #IdxColumn
SET #SQL = 'SELECT * FROM ('+#SQL+') T ORDER BY ' + #IdxColumn + ' ASC'
EXEC (#SQL)
The following query should work for the sample data:
WITH cte AS (
SELECT *
, DENSE_RANK() OVER (ORDER BY RPMA DESC) AS RPMA_RANK
, DENSE_RANK() OVER (ORDER BY ROP DESC) AS ROP_RANK
, DENSE_RANK() OVER (ORDER BY WOB DESC) AS WOB_RANK
FROM t
)
SELECT Depth
, CASE WHEN RPMA_RANK <= 2 THEN RPMA END
, CASE WHEN ROP_RANK <= 2 THEN ROP END
, CASE WHEN WOB_RANK <= 2 THEN WOB END
FROM cte
WHERE RPMA_RANK <= 2
OR ROP_RANK <= 2
OR WOB_RANK <= 2
Note that it returns three rows for RPMA column (there are two 72 and one 70). For n number of columns you need to use dynamic SQL.
This doesn't answer the question, but does fix the terrifying security flaws in the above.
There are multiple problems with the above, so please note that this is a significant but needed change to the SQL you have. Right now you are injecting unsantised parameters into your code, and also using datatypes that are vastly too large. #Columns is varchar(MAX), meaning that someone has 2GB worth of characters to inject into your system. #IdxColumn is a varchar(250) and references a single column; an object can at most be 128 characters long so there's no need for the other 122 characters. Also #Limit is a varchar, despite being an int and should be a parameter.
Firstly, rather than using a varchar(MAX) for #Columns I suggest a table type object:
CREATE TYPE dbo.ObjectList (ObjectName sysname);
sysname is a synonym of nvarchar(128) NOT NULL; and is the data type used for object names in SQL Server. You'll then need to INSERT the names of the columns into a declared table type parameter; one row for each Column Name
Then we can safely inject and parametrise your query:
--Parameters
DECLARE #Columns dbo.ObjectList,
#IdxColumn sysname, --sysname as well
#Limit int; --not varchar
--Variables needed in the SQL:
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = N'SELECT TOP (#Limit)' + #CRLF +
N' ' + QUOTENAME(#IdxColumn) + N',' + #CRLF +
STUFF((SELECT N',' + #CRLF +
N' ' + QUOTENAME(C.ObjectName)
FROM #Columns C
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + #CRLF +
N'FROM dbo.[Table]' + #CRLF + --Should dbo.[Table] also not be safely injected?
N'WHERE ' +
STUFF((SELECT #CRLF +
N' OR ' + QUOTENAME(C.ObjectName) + N' IS NOT NULL'
FROM #Columns C
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,8,N'') + #CRLF +
N'ORDER BY ' + QUOTENAME(#IdxColumn) + N' ASC;'
EXEC sp_executesql #SQL, N'#Limit int', #Limit;

Moving result from 1:n query to repeating columns

I would like to achieve the following from a result of a 1:n join using T-sql
Surname | Givename |..Address | City
Name1....| Givename1.|..Addr11...| City11
Name1....| Givename1.|..Addr12...| City12
Name2....| Givename2.|..Addr21...| City21
Name2....| Givename2.|..Addr22...| City22
Name2....| Givename2.|..Addr23...| City23
TO:
Surname | Givename.. | Address | City... | Address | City... | Address | City
Name1....| Givename1...| Addr11....| City11. | Addr12....| City12. |
Name2....| Givename2...| Addr21....| City21. | Addr22....| City22. | Addr23....| City23
I not care about repeating columnames. Up if there is a soultion with numbers in the repeating columns it would be fine too.
Thanks
In my opinion Pivot is not a solution in this case. Because the column name should repat, and in pivot, cell values are moved to column names, also is unlike pivot no aggregate function involved.
In my thought, the following query will handle your issue. However the SQL Server has column number limit for tables.
Columns per table 1,024 Tables that include sparse column sets
include up to 30,000 columns. See sparse column sets.
You should take this considiration for your data.
DROP TABLE IF EXISTS #Test
DROP TABLE IF EXISTS ##PivotUnlimited
CREATE TABLE #Test(Surname VARCHAR(100) , GivenName VARCHAR(200),
Adress VARCHAR(100),City VARCHAR(100))
INSERT INTO #Test
VALUES
('Name1','Givename1','Addr11','City11'),
('Name1','Givename1','Addr12','City12'),
('Name2','Givename2','Addr21','City21'),
('Name2','Givename2','Addr22','City21'),
('Name2','Givename2','Addr23','City23')
,('Name5','Givename5','Addr51','City51'),
('Name5','Givename5','Addr52','City52'),
('Name5','Givename5','Addr53','City53'),
('Name5','Givename5','Addr54','City54'),
('Name3','Givename3','Addr31','City31')
DECLARE #Counter AS INT=1
DECLARE #Max AS INT
DECLARE #SQL AS VARCHAR(MAX)
SELECT #Max= MAX(DetMax) FROM
(
SELECT ROW_NUMBER() OVER(Partition by Surname ORDER BY Surname ) AS DetMax FROM #Test
) AS TMP
DROP TABLE IF EXISTS ##PivotUnlimited
CREATE TABLE ##PivotUnlimited (Surname VARCHAR(100),GivenName VARCHAR(100))
WHILE #Counter <=#Max
BEGIN
SET #SQL= 'ALTER TABLE ##PivotUnlimited ADD ADDR' + CONVERT(VARCHAR,#Counter) + ' VARCHAR(100)'
EXEC(#SQL)
SET #SQL= 'ALTER TABLE ##PivotUnlimited ADD City' + CONVERT(VARCHAR,#Counter) + ' VARCHAR(100)'
EXEC(#SQL)
SET #Counter=#Counter+1
END
INSERT INTO ##PivotUnlimited (Surname,GivenName)
SELECT DISTINCT Surname , GivenName FROM #Test
DECLARE #Name AS VARCHAR(100)
DECLARE cursorT CURSOR
FOR
SELECT DISTINCT Surname from #test
OPEN cursorT
FETCH NEXT FROM cursorT INTO #Name
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #CounterCursor AS INT=1
DECLARE #MaxCursort AS INT =0
DECLARE #UpdateSQL AS VARCHAR(MAX)
DECLARE #UptAdr AS VARCHAR(100)
DECLARE #UptCity AS VARCHAR(100)
SELECT #MaxCursort=RowNumber FROM (
SELECT ROW_NUMBER() OVER(Partition by Surname ORDER BY Surname ) AS RowNumber FROM #Test
WHERE Surname=#Name
) AS TMP_TBL
WHILE #CounterCursor<= #MaxCursort
BEGIN
SELECT #UptAdr=Adress ,#UptCity=City FROM (
SELECT ROW_NUMBER() OVER(Partition by Surname ORDER BY Surname ) AS RowNumber,* FROM #Test
) AS TMP_TBL WHERE RowNumber=#CounterCursor and Surname=#Name
SET #UpdateSQL= 'UPDATE ##PivotUnlimited SET ADDR' + CONVERT(VARCHAR,#CounterCursor) + ' = ' + '''' + #UptAdr +'''' + ' , '
+ ' City' + CONVERT(VARCHAR,#CounterCursor) + ' = ' + '''' + #UptCity +'''' + ' WHERE Surname = ' + '''' + #Name + ''''
EXEC (#UpdateSQL)
SET #CounterCursor=#CounterCursor+1
END
FETCH NEXT FROM cursorT INTO #Name
END
CLOSE cursorT
DEALLOCATE cursorT
SELECT * FROM ##PivotUnlimited
+---------+-----------+--------+--------+--------+--------+--------+--------+--------+--------+
| Surname | GivenName | ADDR1 | City1 | ADDR2 | City2 | ADDR3 | City3 | ADDR4 | City4 |
+---------+-----------+--------+--------+--------+--------+--------+--------+--------+--------+
| Name1 | Givename1 | Addr11 | City11 | Addr12 | City12 | NULL | NULL | NULL | NULL |
| Name2 | Givename2 | Addr21 | City21 | Addr22 | City21 | Addr23 | City23 | NULL | NULL |
| Name3 | Givename3 | Addr31 | City31 | NULL | NULL | NULL | NULL | NULL | NULL |
| Name5 | Givename5 | Addr51 | City51 | Addr52 | City52 | Addr53 | City53 | Addr54 | City54 |
+---------+-----------+--------+--------+--------+--------+--------+--------+--------+--------+

Dynamic Pivot Table across multiple columns

I'm a fairly new to SQL and could use some help. I have a table of time sheet data with a separate time sheet on each row. Each time sheet has a column with jobcode1 to jobcode16 that stores a string indicating a job code. Each of those has a corresponding TotalJob1 to TotalJob16.
I've managed to create a pivot on the JobCode1 no problem with a column for each Job and the total from TotalJob1. I used this to build it.
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
SELECT #ColumnName = ISNULL(#ColumnName + ',','') + QUOTENAME(TS_Job1Code)
FROM (SELECT DISTINCT TS_Job1Code FROM
dbo.timesheetData) as timesheetdata
SET #DynamicPivotQuery =
N'SELECT VolumeID, ' + #ColumnName + '
FROM dbo.timesheetData
PIVOT(SUM(TS_TotalJob1)
FOR TS_Job1Code IN (' + #ColumnName + ')) AS PVTTable'
EXEC sp_executesql #DynamicPivotQuery
I'm struggling to iterate over the other Job columns and merge them into one big pivot table and was hoping someone might be able to give me a pointer?
My thought was to try and repeat the step 16 times but I don't think this is even close to the right way.
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
DECLARE #N AS INT
DECLARE #NCHAR AS NVARCHAR(MAX)
SET #N = 1
WHILE #N < 17
BEGIN
SET #NCHAR = CAST(#N as VARCHAR)
SELECT #ColumnName = ISNULL(#ColumnName + ',','') + QUOTENAME(('TS_Job' +
#NCHAR + 'Code'))
FROM (SELECT DISTINCT ('TS_Job' + #NCHAR + 'Code') FROM
dbo.timesheetData) as timesheetdata
SET #DynamicPivotQuery =
N'SELECT ' + #ColumnName + '
FROM dbo.timesheetData
PIVOT(SUM(TS_TotalJob' + #NCHAR + ')
FOR TS_Job' + #NCHAR + 'Code IN (' + #ColumnName + ')) AS PVTTable'
EXEC sp_executesql #DynamicPivotQuery
SET #N = #N + 1
END
EXEC sp_executesql #SQL
Original
+-------------+----------+----------+----------+-----------+-----------+-----------+
| TimesheetID | JobCode1 | JobCode2 | JobCode3 | TotalJob1 | TotalJob2 | TotalJob3 |
+-------------+----------+----------+----------+-----------+-----------+-----------+
| 1 | J1 | J3 | | 10 | 9 | |
+-------------+----------+----------+----------+-----------+-----------+-----------+
| 2 | J2 | J1 | J3 | 5 | 5 | 5 |
+-------------+----------+----------+----------+-----------+-----------+-----------+
| 3 | J2 | | | 6 | 3 | 1 |
+-------------+----------+----------+----------+-----------+-----------+-----------+
What I want to achieve
+-------------+----+----+----+----+----+
| TimesheetID | J1 | J2 | J3 | J4 | J6 |
+-------------+----+----+----+----+----+
| 1 | 10 | | 9 | | |
+-------------+----+----+----+----+----+
| 2 | 5 | 5 | 5 | | |
+-------------+----+----+----+----+----+
| 3 | | 6 | | 3 | 1 |
+-------------+----+----+----+----+----+
It's going to get painfully complicated, but one thing you can do is to UNPIVOT your data so that it looks like this:
TimesheetId JobCode JobTotal
1 J1 10
1 J3 9
2 J1 5
....
And then PIVOT that derived table to get your desired result.
Like Tab mentioned, you can unpivot your data first and then Pivot it again.
You can use CROSS APPLY and VALUES to unpivot your table into a temp table.
SELECT ca.*
INTO #temp
FROM timesheet
CROSS APPLY (VALUES
(TimesheetID, JobCode1, TotalJob1),
(TimesheetID, JobCode2, TotalJob2),
(TimesheetID, JobCode3, TotalJob3)
) ca(TimesheetID, JobCode, TotalJob)
this gives you a table like
TimesheetID JobCode TotalJob
----------- ------- -----------
1 J1 10
1 J3 9
1 NULL NULL
2 J2 5
2 J1 5
2 J3 5
3 J2 6
3 NULL 3
3 NULL 1
not sure if you'd have null jobcodes in actual data.. but you can eliminate them using Where ca.JobCode IS NOT NULL above
Then you create your dynamic column string from the temp table
DECLARE #JobCodes nvarchar(MAX)
SELECT #JobCodes = COALESCE(#JobCodes + ',','') + QUOTENAME(JobCode)
FROM #temp
GROUP BY JobCode
ORDER BY JobCode
Then build your dynamic pivot. Instead of creating global temp table, just use the same query before in your pivot query.
DECLARE #Sql nvarchar(max)
SET #Sql = N'
SELECT TimeSheetID,' + #JobCodes
+ 'FROM (
SELECT ca.*
FROM timesheet
CROSS APPLY (VALUES
(TimesheetID, JobCode1, TotalJob1),
(TimesheetID, JobCode2, TotalJob2),
(TimesheetID, JobCode3, TotalJob3)
) ca(TimesheetID, JobCode, TotalJob)
) t
PIVOT (
SUM(TotalJob)
FOR JobCode IN (' + #JobCodes + ')
) p'
EXEC sp_executesql #Sql

left join of 3 tables and display like a dynamic pivot

i have 3 tables:
payorderType :
---------
typeID | TypeName |
1 | accounting |
2 | budget |
----------
step:
----------
StepID | StepName | typeID
1 | payorder | 1
2 | cheque | 1
3 | cheque | 2
----------
user:
----------
userID | StepName | StepID
7878 | payorder | 1
4547 | cheque | 2
6538 | cheque | 1
----------
I want to make a table which users exists in row and columns includes with concat of step and payorderType. same as below:
users | accounting_payorder | accounting_cheque | budget_cheque |
7878 | 1 | 0 | 0 |
4547 | 0 | 1 | 0 |
6538 | 0 | 1 | 0 |
My quesdtion is : if i don't know number of payorderType rows and number of step rows, how should i write it?
My Script is here :
First I create a table in cursor for concat payorderType and step:
CREATE PROC sp_payOrderType
AS
BEGIN
DECLARE a CURSOR
FOR SELECT DISTINCT p.TypeName,s.StepName
FROM
dbo.PayOrderType p LEFT JOIN
dbo.vStep s ON s.TypeID = p.TypeID
FOR READ ONLY
DECLARE #payOrderType NVARCHAR(50),#stepName NVARCHAR(50)
DECLARE #SQL NVARCHAR(MAX)=''
OPEN a
FETCH NEXT FROM a INTO #payOrderType, #stepName
WHILE ##FETCH_STATUS=0
BEGIN
DECLARE #b VARCHAR(max), #b2 VARCHAR(max)
SELECT #b = ISNULL(#b ,'') +'['+ ISNULL(#payOrderType ,'')+ '____'+ISNULL(#stepName ,'')+ ']'+ ' NVARCHAR(1000) ,'
FETCH NEXT FROM a INTO #payOrderType,#stepName
END
CLOSE a
DEALLOCATE a
SELECT #SQL = 'ALTER table AA(' + SUBSTRING(#b,1, LEN(#b)-1) + ')'
SELECT #SQL
END
but i don't know how i should relate rows(userID) with columns ?
You should have determined output structure. Its little risky to have variable output structure.
But here we go:
Make your structure simple (remove most of variables) - create view (or use derived table) payorderType + step
-- should be inner join probably instead of left join
-- if you use left join you have to isnull s.StepName
SELECT
u.userID,
s.StepID,
p.TypeName,
s.StepName,
p.TypeName + '_' + s.StepName StepType,
-- Your column can be like `coalesce(p.TypeName + '_' + s.StepName, p.TypeName, s.StepName) StepType` for left joins
1 Point
FROM dbo.PayOrderType p
INNER JOIN dbo.vStep s ON s.TypeID = p.TypeID
INNER JOIN dbo.user u ON u.StepID = s.StepID
Make your queries more clear (can all yours fields have null values?. Now you can work with only one column/variable.
Now is time for pivot:
SELECT
userID,
[accounting_payorder],
[accounting_cheque],
[budget_cheque]
FROM newview v
PIVOT(MAX(point) FOR StepType in ([accounting_payorder], [accounting_cheque], [budget_cheque])
If its necessary you can use dynamic query:
declare #header varchar(max),
#columns varchar(max)
select #header = coalesce(#header + ', ', '') + 'isnull(''' + StepType + ''', 0) ' + '[' + StepType + ']',
#columns = coalesce(#columns + ', ', '') + '[' + StepType + ']'
from newview
group by StepType
declare #sqlpvt varchar(4000) -- limited by lenght of exec statement
set #sqlpvt = 'select userID, $HEADER FROM newview v PIVOT(MAX(point) FOR StepType in ($COLUMNS)'
-- replace pseudovariables
set #sqlpvt = replace(#sqlpvt, '$HEADER', #header)
set #sqlpvt = replace(#sqlpvt, '$COLUMNS', #columns)
print #sqlpvt
exec (#sqlpvt)
Sorry if somethink is wrong (writed on blind), but i think for guide it's enough. But you should prefer end on step 2 (non-static code is dangerous).