SQL Server Pivot with Multiple Rows Output - sql-server

I have the following data in a Table.
And i need output in below format.
I tried pivoting but seems like not able to resolve it.
Can someone please guide me here?
Thanks in advance.
Edit:
Data in Text Format.
EmployeeID
ShiftCode
AttendanceDate
inDateTime
outDateTime
OverTimeHours
26
ShiftCC1
01-03-2022
01-03-2022 09:10
01-03-2022 18:10
1
26
ShiftCC1
02-03-2022
02-03-2022 09:15
02-03-2022 18:15
2
26
ShiftCC1
03-03-2022
03-03-2022 09:05
03-03-2022 18:05
2
26
ShiftCC1
04-03-2022
04-03-2022 09:10
04-03-2022 18:10
1
26
ShiftCC1
05-03-2022
05-03-2022 09:13
05-03-2022 18:13
2
26
ShiftCC1
06-03-2022
06-03-2022 09:14
06-03-2022 18:14
3
26
ShiftCC1
07-03-2022
07-03-2022 09:16
07-03-2022 18:16
2
26
ShiftCC1
08-03-2022
08-03-2022 09:30
08-03-2022 18:30
1
26
ShiftCC1
09-03-2022
09-03-2022 09:20
09-03-2022 18:20
2
26
ShiftCC1
10-03-2022
10-03-2022 09:25
10-03-2022 18:25
3
output in Text Format:
EmployeeID
ShiftCode
DataType
01-03-2022
02-03-2022
03-03-2022
04-03-2022
05-03-2022
06-03-2022
07-03-2022
08-03-2022
09-03-2022
10-03-2022
26
ShiftCC1
InDateTime
01-03-2022 09:10
02-03-2022 09:15
03-03-2022 09:05
04-03-2022 09:10
05-03-2022 09:13
06-03-2022 09:14
07-03-2022 09:16
08-03-2022 09:30
09-03-2022 09:20
10-03-2022 09:25
26
ShiftCC1
OutDateTime
01-03-2022 18:10
02-03-2022 18:15
03-03-2022 18:05
04-03-2022 18:10
05-03-2022 18:13
06-03-2022 18:14
07-03-2022 18:16
08-03-2022 18:30
09-03-2022 18:20
10-03-2022 18:25
26
ShiftCC1
OverTimeHours
1
2
2
1
2
3
2
1
2
3

First of all, you can' t have the exactly output you want with your dataset. Either you need to give up your overtime column because after pivotting those columns automatically convert to date type or you could change datatype of your columns(I used view for that).
Also, I assumed your "AttendanceDate" is dynamic, so I used a dynamic solution.
I used #Taryn' s solution here:
Option 1:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.att_date)
FROM test1 c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT idx, name, ' + #cols + ' from
(
select idx,
name,
att_date,
att_entry
from test1
) x
pivot
(
max(att_entry)
for att_date in (' + #cols + ')
) p
union
SELECT idx, name, ' + #cols + ' from
(
select idx,
name,
att_date,
att_out
from test1
) x
pivot
(
max(att_out)
for att_date in (' + #cols + ')
) p '
execute(#query)
DBFiddle_Option1:
or
Option2:
You can create a view like this to bypass column incompatibility.
create view test2
as select idx, name as name, convert(varchar, att_date) as att_date,
convert(varchar, att_entry) as att_entry,
convert(varchar, att_out) as att_out,
convert(varchar, overtime) as overtime
from test1;
Then you can use this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.att_date)
FROM test2 c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT idx, name, ' + #cols + ' from
(
select idx,
name,
att_date,
overtime
from test2
) x
pivot
(
max(overtime)
for att_date in (' + #cols + ')
) p
union
SELECT idx, name, ' + #cols + ' from
(
select idx,
name,
att_date,
att_out
from test2
) x
pivot
(
max(att_out)
for att_date in (' + #cols + ')
) p
union
SELECT idx, name, ' + #cols + ' from
(
select idx,
name,
att_date,
att_entry
from test2
) x
pivot
(
max(att_entry)
for att_date in (' + #cols + ')
) p order by 3 desc'
execute(#query)
DBFiddle_Option2:
Note: without converting data to string it looks like this at the end:

Related

Concatenate comma separated strings using STUFF

I want to concatenate comma-separated string. I have a query where I am using the function to get the staff.
Currently, I am using [dbo].fn_fullname(A.REFNO) as Staff to get the staff but I want to include some conditions if the count of cast(S.SCH_NO as varchar) as ReferenceIdentifier is greater than 1 then concatenate the value. But not sure how it can be achieved inside the view itself.
Here I am trying to use STUFF to concatenate.
Here is the query:
Alter View vw_TestDB
AS
select
NEWID() as UniqueKey,
P.ID as Number,
cast(S.SCH_NO as varchar) as ReferenceIdentifier,
cast(S.START_D as datetime2) as StartDateTime,
staff = STUFF((
SELECT ',' + [dbo].fn_fullname(A.REFNO)
FROM [dbo].[vw_TESTDB]
FOR XML PATH('')
), 1, 1, '')
FROM [dbo].[vw_TestDB]
group by ReferenceIdentifier
having count(ReferenceIdentifier)>1,
[dbo].fn_fullname(A.REFNO) as Staff,
from [dbo].V_SCHEDULES S WITH (NOLOCK)
inner join [dbo].V_PAT P WITH (NOLOCK) on P.PAT_REFNO = S.PAT_REFNO
Here the function:
ALTER FUNCTION [dbo].[fn_fullname]
(
#refno as numeric(10, 0)
)
RETURNS varchar(100)
AS
BEGIN
DECLARE #name as varchar(100)
SELECT #name = Stuff(Coalesce(' ' +
CASE
WHEN proca.TITLE_REFNO = 3104
THEN NULL
ELSE
NullIf(dbo.fn_rfval(proca.TITLE_REFNO), '')
END,
'') +
Coalesce(' ' + proca.forename, '') +
Coalesce(' ' + proca.surname, ''),
1,
1,
'')
FROM dbo.v_carers_active proca (nolock)
WHERE refno = #refno
return #name
END
GO
Here are sample data
UniqueKey
Number
ReferenceIdentifier
StartDateTime
staff
70DB83D1-2900-4CF1-9CC4-CA6948AC0E91
A4286
2182823
2015-03-26 08:00:00.0000000
Ms S Taylor
310745CB-4724-4724-A5F0-7D9088317E58
A4286
2182823
2015-03-26 08:00:00.0000000
Ms D Kirkpatrick
CA6DDB25-AADD-4FC1-ABAA-2AF84016E6E5
A4286
2182834
2015-03-19 08:00:00.0000000
Ms D Kirkpatrick
6A3C0A3B-EAA3-4523-B4FD-2882E2C02B4A
A4286
2182844
2015-03-30 08:00:00.0000000
Mrs Nel McKinnon
6399662A-EC4D-4993-8D4F-0BC396D12C2C
A4286
2182844
2015-03-30 08:00:00.0000000
Ms Deb Kirkpatrick
Expected output
UniqueKey
Number
ReferenceIdentifier
StartDateTime
staff
70DB83D1-2900-4CF1-9CC4-CA6948AC0E91
A4286
2182823
2015-03-26 08:00:00.0000000
Ms S Taylor,Ms D Kirkpatrick
CA6DDB25-AADD-4FC1-ABAA-2AF84016E6E5
A4286
2182834
2015-03-19 08:00:00.0000000
Ms D Kirkpatrick
6A3C0A3B-EAA3-4523-B4FD-2882E2C02B4A
A4286
2182844
2015-03-30 08:00:00.0000000
Mrs Nel McKinnon,Ms Deb Kirkpatrick
Not quite understand what you wanted. Based on the sample data and expected output, it seems like you wanted
SELECT t.[Number], t.[ReferenceIdentifier], t.[StartDatetime],
STUFF (
(SELECT ',' + [dbo].fn_fullname(x.REFNO)
FROM yourtable AS x
WHERE x.[Number] = t.[Number]
AND x.[ReferenceIdentifier] = t.[ReferenceIdentifier]
AND x.[StartDatetime] = t.[StartDatetime]
FOR XML PATH('')),
1, 1, '') AS Staff
FROM yourtable t
GROUP BY t.[Number], t.[ReferenceIdentifier], t.[StartDatetime]
HAVING COUNT(*) > 1

Get correct week number for december 31st

In SQL Server, I am trying to get the week number (with the year) of a given date, European style, so I'm using DATEPART with ISO_WEEK argument:
SELECT CAST(DATEPART(year, myDate) AS VARCHAR) + RIGHT('0' + CAST(DATEPART(ISO_WEEK, myDate) AS VARCHAR), 2);
This works well, except for December 31st of 2018, which falls on the 1st week of 2019, but since I'm using DATEPART for the year and the week separately, this obviously can't work. Here's an example when myDate is 31-12-2018:
SELECT CAST(DATEPART(year, '31-12-2018') AS VARCHAR) + RIGHT('0' + CAST(DATEPART(ISO_WEEK, '31-12-2018') AS VARCHAR), 2);
The above query returns '201801'.
Is there a way to simply get 201901 for December 31st 2018, without testing explicitly for this date ?
See the possible duplicate reference and a little tidy up:
declare #d datetime = '2018-12-31';
select cast(year(dateadd(day, 26 - datepart(ISO_WEEK, #d), #d)) as varchar(4)) + right(('0' + cast(datepart(ISO_WEEK, #d) as varchar(2))), 2)
results in 201901.
Test code:
select cast(ds.d as date) as 'date', cast(year(dateadd(day, 26 - datepart(ISO_WEEK, ds.d), ds.d)) as varchar(4)) + right(('0' + cast(datepart(ISO_WEEK, ds.d) as varchar(2))), 2) as "IsoWeek"
from (
select dateadd(day, x.num, '2018-12-30') as d
from (
select h.num + d.num + s.num as num
from (
select 0 num union select 100 num union select 200 num union select 300 num
) h
cross join (
select 0 num union select 10 num union select 20 num union select 30 num
) d
cross join (
select 0 num union select 1 num union select 2 union select 3 num
) s
) x
) ds
order by ds.d
which results in:
date IsoWeek
2018-12-30 201852
2018-12-31 201901
2019-01-01 201901
2019-01-02 201901
2019-01-09 201902
2019-01-10 201902
2019-01-11 201902
2019-01-12 201902
2019-01-19 201903
2019-01-20 201903
2019-01-21 201904
2019-01-22 201904
2019-01-29 201905
2019-01-30 201905
2019-01-31 201905
2019-02-01 201905
2019-04-09 201915
2019-04-10 201915
2019-04-11 201915
2019-04-12 201915
2019-04-19 201916
2019-04-20 201916
2019-04-21 201916
2019-04-22 201917
2019-04-29 201918
2019-04-30 201918
2019-05-01 201918
2019-05-02 201918
2019-05-09 201919
2019-05-10 201919
2019-05-11 201919
2019-05-12 201919
2019-07-18 201929
2019-07-19 201929
2019-07-20 201929
2019-07-21 201929
2019-07-28 201930
2019-07-29 201931
2019-07-30 201931
2019-07-31 201931
2019-08-07 201932
2019-08-08 201932
2019-08-09 201932
2019-08-10 201932
2019-08-17 201933
2019-08-18 201933
2019-08-19 201934
2019-08-20 201934
2019-10-26 201943
2019-10-27 201943
2019-10-28 201944
2019-10-29 201944
2019-11-05 201945
2019-11-06 201945
2019-11-07 201945
2019-11-08 201945
2019-11-15 201946
2019-11-16 201946
2019-11-17 201946
2019-11-18 201947
2019-11-25 201948
2019-11-26 201948
2019-11-27 201948
2019-11-28 201948
the problem is that there is a unique case where the week number can be very 'early' in the year, although it is actually month 12, therefore it is easy to check if there is a start of year week in December, and in that case simply add one to the year,
DECLARE #mydate as datetime2 = '20270101';
SELECT CAST(DATEPART(year, #myDate)
+ case when DATEPART(ISO_WEEK, #myDate) < 2 and month(#mydate) =12 then
1
else
0
end
+ case when DATEPART(ISO_WEEK, #myDate) >10 and month(#mydate) =1 then
-1
else
0
end
AS VARCHAR)
+ RIGHT('0' + CAST(DATEPART(ISO_WEEK, #myDate) AS VARCHAR), 2);
I added a correction for the 2027 problem highlighted in another answer.
Check following query, its using last day of the week to get the year value
SELECT CAST(DATEPART(year, DATEADD(wk, 1, DATEADD(DAY, 0-DATEPART(WEEKDAY, '2018-12-31'), DATEDIFF(dd, 0, '2018-12-31')))) AS VARCHAR) + RIGHT('0' + CAST(DATEPART(ISO_WEEK, '2018-12-31') AS VARCHAR), 2);

How to use dynamically pivot to generate timetable

I want to generate a timetable and I have these columns:
Time Itinerary ID
06:35 3579 87069
07:15 3031 63722
07:15 3031 68218
16:30 3031 82745
16:30 3031 88308
21:00 3031 72212
21:00 3031 76241
06:55 3576 87069
07:25 3333 63722
07:25 3333 68218
16:40 3333 82745
And I want to get this:
3579 3031 3576 3333
87069 06:35 06:55
63722 07:15
68218 07:15
.
.
.
The number of Itineraries are variable and i can get this information from one table.
Full working example of dynamic PIVOT:
CREATE TABLE #DataSource
(
[Time] TIME
,[Itinerary] INT
,[ID] INT
);
INSERT INTO #DataSource ([Time], [Itinerary], [ID])
VALUES ('06:35', '3579', '87069')
,('07:15', '3031', '63722')
,('07:15', '3031', '68218')
,('16:30', '3031', '82745')
,('16:30', '3031', '88308')
,('21:00', '3031', '72212')
,('21:00', '3031', '76241')
,('06:55', '3576', '87069')
,('07:25', '3333', '63722')
,('07:25', '3333', '68218')
,('16:40', '3333', '82745');
DECLARE #DynammicTSQLStatement NVARCHAR(MAX)
,#DynamicPIVOTColumns NVARCHAR(MAX);
SET #DynamicPIVOTColumns = STUFF
(
(
SELECT ',[' + CAST([Itinerary] AS VARCHAR(12)) + ']'
FROM #DataSource
GROUP BY [Itinerary]
ORDER BY [Itinerary]
FOR XML PATH('') ,TYPE
).value('.', 'NVARCHAR(MAX)')
,1
,1
,''
);
SET #DynammicTSQLStatement = N'
SELECT *
FROM #DataSource
PIVOT
(
MAX([Time]) FOR [Itinerary] IN (' + #DynamicPIVOTColumns + ')
) PVT';
EXEC sp_executesql #DynammicTSQLStatement;
DROP TABLE #DataSource;

SQL Server Pivot Multiple Values

Using SQL Server 2012.
I have the following table. The Style and colour are passed as a parameter:
Style Colour Size Whse Stock Sales 4WeekSales ATP
ABC123 AS12 10 London 2 3 6 7
ABC123 AS12 12 London 4 6 8 10
ABC123 AS12 14 New York 6 8 9 12
ABC123 AS12 10 New York 7 5 7 5
But need the data to look like this with the sizes along the top:
Whse 10 12 14
Lon
Stock 2 4 6
Sales 3 6 8
4WeekSales 6 8 9
ATP 7 10 12
New York
Stock 7 6
Sales 5 8
4WeekSales 7 9
ATP 5 12
Points to note - the size field needs to be dynamic - sometimes it can be 6 /8/10/12, sometimes it can be XS/S/M/L etc
Also their are more than two whse's - this is just an example.
I did make a start in T-SQL:
use SafetyStock
go
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', p.' + QUOTENAME(Size)
FROM (SELECT p.Size FROM dbo.vw_optimums AS p
GROUP BY p.Size) AS x;
SET #sql = N'
SELECT SKU, Style,' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT SKU, Style, p.Size, p.SAFETYSTOCK
FROM dbo.vw_optimums AS p
) AS j
PIVOT
(
SUM(SAFETYSTOCK) FOR Size IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
) AS p;';
PRINT #sql;
EXEC sp_executesql #sql;
However, this works but only pivots on the stock - how do I also pivot by Sales\4WeekSales\ATP and also groupb by the whse?
Thank you in advance.
This is my latest code. If I take the SEQNO out it works, but I need this so the sizes appear along the top correctly e.g. S / M / L / XL / XXL etc or 6/ 8 / 10 / 12
DECLARE #SizeColums VARCHAR(MAX)
DECLARE #Seq Integer
SELECT
#SizeColums = COALESCE(#SizeColums + ',','') + QUOTENAME([Size]),
#Seq = SEQNO
FROM vw_optimums1
GROUP BY [Size],[SEQNO]
ORDER BY [SEQNO]
DECLARE #Sql NVARCHAR(MAX) = N'
SELECT Whse,
[Types],' +
#SizeColums + '
FROM (SELECT * FROM vw_optimums1) t
UNPIVOT (
[Type]
FOR [Types] IN ([Stock],[LWSALES],[L4WSALES],[ATP]) ) up
PIVOT (
MAX([Type])
FOR [Size] IN (' + #SizeColums + ')
) p
'
EXEC sp_executesql #sql;
About the closest I can get you is this.
DECLARE #SizeColums VARCHAR(MAX)
SELECT #SizeColums = COALESCE(#SizeColums + ',','') + QUOTENAME([Size])
FROM vw_optimums
GROUP BY [Size]
DECLARE #Sql NVARCHAR(MAX) = N'
SELECT Whse,
[Types],' +
#SizeColums + '
FROM (SELECT * FROM vw_optimums
) t
UNPIVOT (
[Type]
FOR [Types] IN ([Stock],[Sales],[4WeekSales],[ATP]) ) up
PIVOT (
MAX([Type])
FOR [Size] IN (' + #SizeColums + ')
) p
'
This actually uses UNPIVOT first to get the breakdown by size, then pivots based on size.
You'll get a result like this based on the sample data.
Whse Types 10 12 14
-------- -------------- ----------- ----------- -----------
London 4WeekSales 6 8
London ATP 7 10
London Sales 3 6
London Stock 2 4
New York 4WeekSales 7 9
New York ATP 5 12
New York Sales 5 8
New York Stock 7 6

comma separated names based on company ID

I have following table of Employee details
EmployeeName CompayId CompanyLastActive
---------------------------------------------------------
robort 112 10 Jun 2015 09:30
john 113 11 Jun 2015 11:10
sunny 114 14 Jun 2015 16:10
sumanth 114 15 Jun 2015 18:11
usha 115 07 Jun 2015 13:14
sudheer 115 14 Jun 2015 17:10
sweety 115 08 Jun 2015 16:34
I need to get the latest employee active time based on CompanyID with Comma separated EmployeeName like below
EmployeeName CompayId CompanyLastActive
---------------------------------------------------------
robort 112 10 Jun 2015 09:30
john 113 11 Jun 2015 11:10
sunny, sumanth 114 15 Jun 2015 18:11
usha, sudheer, sweety 115 14 Jun 2015 17:10
please help me how to solve.
SELECT EmployeeName = STUFF((
SELECT ',' + e1.EmployeeName
FROM dbo.Employee e1
WHERE e.CompayId = e1.CompayId
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''),
e.CompayId,
MAX(CompanyLastActive) as CompanyLastActive
FROM dbo.Employee e
GROUP BY e.CompayID
ORDER BY e.CompayId
Result:
EmployeeName CompayId CompanyLastActive
-------------------------------------------------------
robort 112 June, 10 2015 09:30:00
john 113 June, 11 2015 11:10:00
sunny,sumanth 114 June, 15 2015 18:11:00
usha,sudheer,sweety 115 June, 14 2015 17:10:00
Sample result in SQL Fiddle.
Check this out, i have not checked this as i'm feeling lazy to build schema, it would probably throw group by error, You can handle it
SELECT
Results.CompayId,
STUFF((
SELECT ', ' + CAST(EmployeeName AS VARCHAR(MAX))
FROM YourTable
WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
,max(Results.CompanyLastActive) as CompanyLastActive
FROM YourTable Results
GROUP BY CompayId
You can use this query:
SELECT EmployeeNames = dbo.EmployeeNamesPerCompany(CompanyID,', '),
CompanyID,
CompanyLastActive = MAX(CompanyLastActive)
FROM Employee e
GROUP BY CompanyID
ORDER BY MAX(CompanyLastActive)
If you have created a scalar valued function like this:
CREATE FUNCTION [dbo].[EmployeeNamesPerCompany]
(
#companyID Int,
#delimiter varchar(5)
)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #Names VARCHAR(8000)
SELECT #Names = COALESCE(#Names + #delimiter, '') + e.EmployeeName
FROM Employee e
WHERE e.CompanyId = #companyID
ORDER BY e.EmployeeName
return #Names
END
Sql-Fiddle Demo

Resources