I need help with multiple column aggregate using pivot in mssql.
Below is the temporary table for class assessments. This table contain list of class assessment which include:
assessment code
date of assessment
total item
passing percentage
create table #class_assessments (class_assessment_id int identity(1,1),
class_assessment_code varchar(10),
class_assessment_date datetime,
class_assessment_total_item decimal(8,3),
class_assessment_passing_item decimal(8,2))
insert into #class_assessments values ('a1', convert(varchar(10), getdate(), 101), 10.0, 50.0)
insert into #class_assessments values ('a2', convert(varchar(10), getdate()+ 1, 101), 20.0, 50.0)
insert into #class_assessments values ('a3', convert(varchar(10), getdate()+ 2, 101), 30.0, 50.0)
insert into #class_assessments values ('a4', convert(varchar(10), getdate()+ 3, 101), 40.0, 50.0)
Below is the employee assessments. This table contains the list of employee who took the assessments:
create table #emp_assessments (emp_assessment_id int identity(1,1),
class_assessment_id int,
emp_name varchar(100),
assessment_score decimal(8,2),
assessment_comment varchar(100))
insert into #emp_assessments values(1, 'emp_name1', 5.0, 'comment1-1')
insert into #emp_assessments values(1, 'emp_name2', 5.0, 'comment1-2')
insert into #emp_assessments values(2, 'emp_name1', 5.0, 'comment2-1')
insert into #emp_assessments values(2, 'emp_name2', 5.0, 'comment2-2')
insert into #emp_assessments values(3, 'emp_name1', 5.0, 'comment3-1')
insert into #emp_assessments values(3, 'emp_name2', 5.0, 'comment3-2')
insert into #emp_assessments values(4, 'emp_name3', 5.0, 'comment4-3')
insert into #emp_assessments values(4, 'emp_name4', 5.0, 'comment4-4')
My base table is #emp_assessment_scores. This table contains the summary of all employee assessments including the percentage score and status if passed of failed.
create table #emp_assessment_scores (id int identity(1,1),
emp_assessment_id int,
class_assessment_id int,
emp_name varchar(100),
assessment_score decimal(8,2),
assessment_comment varchar(100),
class_assessment_code varchar(10),
class_assessment_date datetime,
class_assessment_total_item decimal(8,2),
class_assessment_passing_item decimal(8,2),
score_percent decimal(8,2),
score_status varchar(10))
insert into #emp_assessment_scores
select ea.emp_assessment_id,
ea.class_assessment_id,
ea.emp_name,
ea.assessment_score,
ea.assessment_comment,
ca.class_assessment_code,
ca.class_assessment_date,
ca.class_assessment_total_item,
ca.class_assessment_passing_item,
ea.assessment_score / ca.class_assessment_total_item * 100,
case when ea.assessment_score / ca.class_assessment_total_item * 100 >= ca.class_assessment_passing_item then 'passed' else 'failed' end
from #emp_assessments as ea inner join #class_assessments as ca on ea.class_assessment_id = ca.class_assessment_id
below is my pivot script
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX),#PivotColumnNames AS NVARCHAR(MAX)
SET #PivotColumnNames = N'';
SELECT #PivotColumnNames = #PivotColumnNames + N', ' + QUOTENAME(class_assessment_code)
FROM( SELECT distinct(class_assessment_code) FROM #emp_assessment_scores AS p GROUP BY class_assessment_code ) AS x;
SET #DynamicPivotQuery = N'
SELECT emp_name' + #PivotColumnNames + 'FROM (
SELECT emp_name, score_percent, class_assessment_code FROM #emp_assessment_scores) AS j
PIVOT (max(score_percent) FOR class_assessment_code in ('+ STUFF(#PivotColumnNames, 1, 1, '') +')) AS s ';
EXEC sp_executesql #DynamicPivotQuery
It shows this result:
+-----------+-------+-------+-------+-------+
| emp_name | a1 | a2 | a3 | a4 |
+-----------+-------+-------+-------+-------+
| emp_name1 | 50.00 | 25.00 | 16.67 | NULL |
| emp_name2 | 50.00 | 25.00 | 16.67 | NULL |
| emp_name3 | NULL | NULL | NULL | 12.50 |
| emp_name4 | NULL | NULL | NULL | 12.50 |
+-----------+-------+-------+-------+-------+
But I wanted to have the result shown below:
+-----------+---------+------------+----------+------------+------------+-----------+---------+------------+----------+------------+------------+-----------+---------+------------+----------+------------+------------+-----------+---------+------------+----------+------------+------------+-----------+
| emp_name | a1_item | a1_passing | a1_score | a1_percent | a1_comment | ai_status | a2_item | a2_passing | a2_score | a2_percent | a2_comment | a2_status | a3_item | a3_passing | a3_score | a3_percent | a3_comment | a3_status | a4_item | a4_passing | a4_score | a4_percent | a4_comment | a4_status |
+-----------+---------+------------+----------+------------+------------+-----------+---------+------------+----------+------------+------------+-----------+---------+------------+----------+------------+------------+-----------+---------+------------+----------+------------+------------+-----------+
| emp_name1 | 10.00 | 50.00 | 5.00 | 50.00 | comment1-1 | passed | 20.00 | 50.00 | 5.00 | 25.00 | comment2-1 | failed | 30.00 | 50.00 | 5.00 | 16.67 | comment3-1 | failed | null | null | null | null | null | null |
| emp_name2 | 10.00 | 50.00 | 5.00 | 50.00 | comment1-2 | passed | 20.00 | 50.00 | 5.00 | 25.00 | comment2-2 | failed | 30.00 | 50.00 | 5.00 | 16.67 | comment3-2 | failed | null | null | null | null | null | null |
| emp_name3 | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | 40.00 | 50.00 | 5.00 | 12.50 | comment4-3 | failed |
| emp_name4 | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | 40.00 | 50.00 | 5.00 | 12.50 | comment4-3 | failed |
+-----------+---------+------------+----------+------------+------------+-----------+---------+------------+----------+------------+------------+-----------+---------+------------+----------+------------+------------+-----------+---------+------------+----------+------------+------------+-----------+
You can use dynamic TSQL combined with group by to generate all the columns you need:
--declare variable that will hold the dynamic TSQL statement
declare #sql nvarchar(max)='select emp_name '
--generate the select statements for your dynamic query for each class_assessment_code
select
#sql = #sql +' ,sum(case when class_assessment_code='''+class_assessment_code
+''' then class_assessment_total_item else null end) as '+class_assessment_code
+'_item ,sum(case when class_assessment_code='''+class_assessment_code
+''' then class_assessment_passing_item else null end) as '+class_assessment_code
+'_passing ,sum(case when class_assessment_code='''+class_assessment_code
+''' then assessment_score else null end) as '+class_assessment_code
+'_score ,sum(case when class_assessment_code='''+class_assessment_code
+''' then score_percent else null end) as '+class_assessment_code
+'_percent ,max(case when class_assessment_code='''+class_assessment_code
+''' then assessment_comment else null end) as '+class_assessment_code
+'_comment ,max(case when class_assessment_code='''+class_assessment_code
+''' then score_status else null end) as '+class_assessment_code+'_status'
from #emp_assessment_scores
group by class_assessment_code
--add group by clause to dynamic query
set #sql = #sql +' FROM #emp_assessment_scores group by emp_name'
--execute the dynamic query
exec(#sql)
Result:
Related
Compare historical rows (LAG rows based on ResultChngDt) and combine changed column values to single column. Looking for help in writing elegant/efficient SQL Server 2016 TSQL Code(without cursors).
I have a table with the structure and data like this:
+----+-------+--------------+---------------+--------+--------+--------------+
| ID | RepID | CollctedDate | CompletedDate | Result | Tcode | ResultChngDt |
+----+-------+--------------+---------------+--------+--------+--------------+
| 1 | 101 | 11/20/2017 | 12/13/2017 | | L-2190 | 12/13/2017 |
| 1 | 101 | 11/22/2017 | 12/15/2017 | POS | L-Afb | 1/5/2018 |
| 1 | 102 | 11/22/2017 | 12/15/2017 | | L-2191 | 12/15/2017 |
| 1 | 102 | 11/22/2017 | 12/15/2017 | POS | L-2192 | 12/31/2017 |
+----+-------+--------------+---------------+--------+--------+--------------+
I need to generate a report/result as follows:
+----+-------+---------------------------+--------------------------+--+
| ID | RepID | Previous | Current | |
+----+-------+---------------------------+--------------------------+--+
| 1 | 101 | CollctedDate:11/20/2017 | CollctedDate:11/22/2017 | |
| | | CompletedDate:12/13/2017 | CompletedDate:12/15/2017 | |
| | | Result: | Result:POS | |
| | | Tcode:L-2190 | Tcode:L-Afb | |
| 1 | 102 | CollctedDate:11/22/2017 | CollctedDate:11/22/2017 | |
| | | CompletedDate:12/15/2017 | CompletedDate:12/15/2017 | |
| | | Result: | Result:POS | |
| | | Tcode:L-2191 | Tcode:L-2192 | |
+----+-------+---------------------------+--------------------------+--+
CREATE TABLE [dbo].[Table1]
(
[ID] INT NULL,
[RepID] INT NULL,
[CollctedDate] DATETIME NULL,
[CompletedDate] DATETIME NULL,
[Result] VARCHAR(3) NULL,
[Tcode] VARCHAR(10) NULL,
[ResultChngDt] DATETIME NULL
) ON [PRIMARY];
GO
INSERT INTO [dbo].[Table1] ([ID], [RepID], [CollctedDate], [CompletedDate], [Result], [Tcode], [ResultChngDt])
VALUES (1, 101, N'11/20/2017', N'12/13/2017', N'', N'L-2190', N'12/13/2017')
, (1, 101, N'11/22/2017', N'12/15/2017', N'POS', N'L-Afb', N'1/5/2018')
, (1, 102, N'11/22/2017', N'12/15/2017', N'', N'L-2191', N'12/15/2017')
, (1, 102, N'11/22/2017', N'12/15/2017', N'POS', N'L-2192', N'12/31/2017')
Here's my query for your question:
WITH cte_LEADLAG AS(
SELECT ID,
RepID,
CollctedDate,
CompletedDate,
Result,
Tcode,
ResultChngDt,
CONCAT('CollectedDate:',CAST(CollctedDate AS DATETIME2), ' CompletedDate:', CAST(CompletedDate AS DATETIME2), ' Result:', Result, ' Tcode', Tcode) AS dates,
LAG(CollctedDate) OVER(PARTITION BY RepID ORDER BY CollctedDate) AS 'LAGCollectedDate' ,
lead(CollctedDate) OVER(PARTITION BY RepID ORDER BY CollctedDate) AS 'LEADCollectedDate',
LAG(CompletedDate) OVER(PARTITION BY RepID ORDER BY CompletedDate) AS 'LAGCompDate' ,
lead(CompletedDate) OVER(PARTITION BY RepID ORDER BY CompletedDate) AS 'LEADcompDate' ,
LEAD(Result) OVER(PARTITION BY RepID ORDER BY CompletedDate) AS 'LEADResult' ,
LEAD(Tcode) OVER(PARTITION BY RepID ORDER BY CompletedDate) AS 'LEADTcode'
FROM #temp
),
cte_FINAL AS(
SELECT distinct ID,
RepID,
CASE WHEN cte.LAGCollectedDate IS NULL THEN CONCAT('CollectedDate:',CAST(CollctedDate AS DATETIME2), ' CompletedDate:', CAST(CompletedDate AS DATETIME2), ' Result:', Result, ' Tcode', Tcode) end AS 'Previous',
CASE WHEN cte.LEADCollectedDate IS not NULL THEN CONCAT('CollectedDate:',CAST(cte.LEADCollectedDate AS DATETIME2), ' CompletedDate:', CAST(LEADcompDate AS DATETIME2), ' Result:', cte.LEADResult, ' Tcode', cte.LEADTcode) end AS 'Current'
FROM cte_LEADLAG AS cte
WHERE cte.LEADCollectedDate IN (SELECT MAX(LEADCollectedDate) FROM cte_LEADLAG WHERE cte_LEADLAG.RepID = cte.RepID))
)
SELECT *
FROM cte_FINAL;
Result:
with data as (
select *, row_number() over (partition by RepID order by ResultChgDt desc) as rn
from dbo.Table1
)
select
from data as d1 left outer join data as d2 on d2.rn = d1.rn + 1
where d1.rn = 1 -- I suppose you only want the two most recent??
This gives you all the data you need in a single row. You can handle report formatting to suit whatever requirements you have in whatever tool you're using for that.
I am using an SQL Server database and have these following tables
Table "Data"
------------------
| Id | data_name |
------------------
| 1 |Data 1 |
| 2 |Data 2 |
| 3 |Data 3 |
| 4 |Data 4 |
| 5 |Data 5 |
------------------
and Table "Value_data"
--------------------------------------------------------------------------------------------------------------
| Id | data_id | date | col_1_type | col_1_name | col_1_value | col_2_type | col_2_name | col_2_value |
--------------------------------------------------------------------------------------------------------------
| 1 | 1 | 2017-01-01 | A | Alpha | 12 | B | Beta | 23 |
| 2 | 1 | 2017-02-01 | A | Alpha | 32 | B | Beta | 42 |
---------------------------------------------------------------------------------------------------------------
And i want to make result like so
-----------------------------------------------------------------
|value_id | data_id | data_name | date | A-Alpha | B-Beta |
-----------------------------------------------------------------
|1 | 1 | Data 1 | 2017-01-01 | 12 | 23 |
|2 | 1 | Data 1 | 2017-02-01 | 32 | 42 |
-----------------------------------------------------------------
I've search multiple times for solutions, i've tried using Pivot for example for a static result,
DECLARE #Data TABLE ( Id INT, data_name VARCHAR(10) )
INSERT INTO #Data VALUES
( 1 ,'Data 1'),
( 2 ,'Data 2'),
( 3 ,'Data 3'),
( 4 ,'Data 4'),
( 5 ,'Data 5')
DECLARE #Value_data TABLE (Id INT, data_id INT, [date] DATE, col_1_type VARCHAR(10), col_1_name VARCHAR(10), col_1_value INT, col_2_type VARCHAR(10), col_2_name VARCHAR(10), col_2_value INT)
INSERT INTO #Value_data VALUES
( 1, 1, '2017-01-01','A','Alpha','12','B','Beta','23'),
( 2, 1, '2017-02-01','A','Alpha','32','B','Beta','42')
;WITH CTE AS (
select vd.Id value_id
, vd.data_id
, d.data_name
, vd.[date]
, vd.col_1_type + '-' +vd.col_1_name Col1
, vd.col_1_value
, vd.col_2_type + '-' +vd.col_2_name Col2
, vd.col_2_value
from #Value_data vd
inner join #Data d on vd.data_id = d.Id
)
SELECT * FROM CTE
PIVOT( MAX(col_1_value) FOR Col1 IN ([A-Alpha])) PVT_A
PIVOT( MAX(col_2_value) FOR Col2 IN ([B-Beta])) PVT_B
but it wont work well with the data that i'm using with the joining tables because my database will be dynamic, anyone had a solution with the same case?
I have two tables Customers and Purchases:
Customers table:
+------------+-----------+----------+
| CustomerID | FirstName | Surname |
+------------+-----------+----------+
| 101 | Jeff | Smith |
| 102 | Alex | Jones |
| 103 | Pam | Clark |
| 104 | Zola | Lona |
| 105 | Simphele | Ndima |
| 106 | Andre | Williams |
| 107 | Wayne | Shelton |
| 108 | Bob | Banard |
| 109 | Ken | Davidson |
| 110 | Sally | Ivan |
+------------+-----------+----------+
Purchases table:
+------------+--------------+------------+-----------+
| PurchaseId | PurchaseDate | CustomerID | ProductID |
+------------+--------------+------------+-----------+
| 1 | 2012-08-15 | 105 | a510 |
| 2 | 2012-08-15 | 102 | a510 |
| 3 | 2012-08-15 | 103 | a506 |
| 4 | 2012-08-16 | 105 | a510 |
| 5 | 2012-08-17 | 106 | a507 |
| 6 | 2012-08-17 | 107 | a509 |
| 7 | 2012-08-18 | 108 | a502 |
| 8 | 2012-08-19 | 108 | a510 |
| 9 | 2012-08-19 | 109 | a502 |
| 10 | 2012-08-20 | 110 | a503 |
| 11 | 2012-08-21 | 101 | a510 |
| 12 | 2012-08-22 | 102 | a507 |
+------------+--------------+------------+-----------+
My question (which I have been struggling with for the last 2 days): create a query that will display all the customers who purchased products after five days or more, since their last purchase.
Desired outputs:
+-----------+------------------+
| Firstname | Daysdifference |
+-----------+------------------+
| Alex | 7 |
+-----------+------------------+
select c.FirstName, t.dif as Daysdifference from customer c
inner join
(
select p1.CustomerID,
datediff(day,p1.PurchaseDate,p2.PurchaseDate) as dif
from purchases p1
inner join purchases p2
on p1.CustomerID=p2.CustomerID
where datediff(day,p1.PurchaseDate,p2.PurchaseDate)>=5
) t
on t.CustomerID= c.CustomerID
Here you go:
DECLARE #Customers TABLE (CustomerID INT, FirstName VARCHAR(30), Surname VARCHAR(30));
DECLARE #Purchases TABLE (PurchaseId INT, PurchaseDate DATE, CustomerID INT, ProductID VARCHAR(10) );
/**/
INSERT INTO #Customers VALUES
(101,'Jeff ' , 'Smith '),
(102,'Alex ' , 'Jones '),
(103,'Pam ' , 'Clark '),
(104,'Zola ' , 'Lona '),
(105,'Simphele' , 'Ndima '),
(106,'Andre ' , 'Williams'),
(107,'Wayne ' , 'Shelton '),
(108,'Bob ' , 'Banard '),
(109,'Ken ' , 'Davidson'),
(110,'Sally ' , 'Ivan ');
INSERT INTO #Purchases VALUES
(1, '2012-08-15' ,105, 'a510'),
(2, '2012-08-15' ,102, 'a510'),
(3, '2012-08-15' ,103, 'a506'),
(4, '2012-08-16' ,105, 'a510'),
(5, '2012-08-17' ,106, 'a507'),
(6, '2012-08-17' ,107, 'a509'),
(7, '2012-08-18' ,108, 'a502'),
(8, '2012-08-19' ,108, 'a510'),
(9, '2012-08-19' ,109, 'a502'),
(10,'2012-08-20' ,110, 'a503'),
(11,'2012-08-21' ,101, 'a510'),
(12,'2012-08-22' ,102, 'a507');
--
WITH CTE AS (
SELECT Pur1.CustomerID, DATEDIFF(DAY, Pur1.PurchaseDate, Pur2.PurchaseDate) Daysdifference
FROM #Purchases Pur1 INNER JOIN #Purchases Pur2 ON Pur1.CustomerID = Pur2.CustomerID
)
SELECT Cus.FirstName, CTE.Daysdifference
FROM #Customers Cus INNER JOIN CTE ON Cus.CustomerID = CTE.CustomerID
WHERE CTE.Daysdifference >= 5;
Result:
+-----------+------------------+
| Firstname | Daysdifference |
+-----------+------------------+
| Alex | 7 |
+-----------+------------------+
Demo
You can solve it like this:
Create a ranking based on date desc and partitioned by customer id
Next check date diff between consecutive ranks to find those customers
Query below
; with cte as
(
select
*,
row_number() over(partition by CustomerID order by PurchaseDate desc) r
from
Purchases
)
select
Name= c.FirstName,
Daysdifference =datediff(d,c1.PurchaseDate, c2.PurchaseDate)
from
Customers c join
cte c1
on c.customerid=c1.customerid
join cte c2
on c1.CustomerID=c2.CustomerId
and c1.r-1=c2.r
and datediff(d,c1.PurchaseDate, c2.PurchaseDate) >=5
See working demo
Since SQL Server 2012 and the addition of the LAG & LEAD functions, there is no reason at all to do a self join for something like this...
Note... Ranking function can be extremely efficient compared to other methods BUT they do need the help of a proper index to perform their best (note the additional POC index in the test script).
CREATE TABLE #Customers (
CustomerID INT PRIMARY KEY,
FirstName VARCHAR(30),
Surname VARCHAR(30)
);
CREATE TABLE #Purchases (
PurchaseId INT PRIMARY KEY,
PurchaseDate DATE,
CustomerID INT,
ProductID VARCHAR(10)
);
INSERT INTO #Customers VALUES
(101,'Jeff ' , 'Smith '),
(102,'Alex ' , 'Jones '),
(103,'Pam ' , 'Clark '),
(104,'Zola ' , 'Lona '),
(105,'Simphele' , 'Ndima '),
(106,'Andre ' , 'Williams'),
(107,'Wayne ' , 'Shelton '),
(108,'Bob ' , 'Banard '),
(109,'Ken ' , 'Davidson'),
(110,'Sally ' , 'Ivan ');
INSERT INTO #Purchases VALUES
(1, '2012-08-15' ,105, 'a510'),
(2, '2012-08-15' ,102, 'a510'),
(3, '2012-08-15' ,103, 'a506'),
(4, '2012-08-16' ,105, 'a510'),
(5, '2012-08-17' ,106, 'a507'),
(6, '2012-08-17' ,107, 'a509'),
(7, '2012-08-18' ,108, 'a502'),
(8, '2012-08-19' ,108, 'a510'),
(9, '2012-08-19' ,109, 'a502'),
(10,'2012-08-20' ,110, 'a503'),
(11,'2012-08-21' ,101, 'a510'),
(12,'2012-08-22' ,102, 'a507');
-- add POC index...
CREATE NONCLUSTERED INDEX ix_POC ON #Purchases (CustomerID, PurchaseDate);
--===========================================================
SELECT
c.FirstName,
p2.Daysdifference
FROM
#Customers c
JOIN (
SELECT
p.CustomerID,
Daysdifference = DATEDIFF(DAY, p.PurchaseDate, LEAD(p.PurchaseDate, 1) OVER (PARTITION BY p.CustomerID ORDER BY p.PurchaseDate))
FROM
#Purchases p
) p2
ON c.CustomerID = p2.CustomerID
WHERE
p2.Daysdifference >= 5;
Results...
FirstName Daysdifference
------------------------------ --------------
Alex 7
This question already has answers here:
Convert Rows to columns using 'Pivot' in SQL Server
(9 answers)
Closed 6 years ago.
I have a table like below:
+-----------+------------+--------+
| Col1 | Col2 | Col3 |
+-----------+------------+--------+
| 12345678 | FirstName | John |
| 12345678 | LastName | Smith |
| 987456321 | LastName | Clancy |
| 987456321 | MiddleName | T |
| 987456321 | Height | 176cm |
| 654125878 | FirstName | Tan |
| 654125878 | Weight | 150lb |
+-----------+------------+--------+
How to convert that to:
+-----------+-----------+------------+----------+--------+--------+
| ID | FirstName | MiddleName | LastName | Height | Weight |
+-----------+-----------+------------+----------+--------+--------+
| 12345678 | John | null | Smith | null | null |
| 987456321 | null | T | Clancy | 176cm | null |
| 654125878 | Tan | null | null | null | 150lb |
+-----------+-----------+------------+----------+--------+--------+
I think a conditional aggregation would do the trick here.
Assuming you don't need dynamic
Select ID = Col1
,FirstName = max(case when Col2='FirstName' then Col3 else null end)
,MiddleName = max(case when Col2='MiddleName ' then Col3 else null end)
,LastName = max(case when Col2='LastName ' then Col3 else null end)
,Height = max(case when Col2='Height' then Col3 else null end)
,Weight = max(case when Col2='Weight' then Col3 else null end)
From YourTable
Group By Col1
If you need dynamic
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName([Col2]) From Yourtable Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select [Col1] as ID ,' + #SQL + '
From YourTable
Pivot (max(Col3) For [Col2] in (' + #SQL + ') ) p'
Exec(#SQL);
If it helps, the generated SQL for the dynamic Pivot is as follows:
Select [Col1] as ID ,[FirstName],[Height],[LastName],[MiddleName],[Weight]
From YourTable
Pivot (max(Col3) For [Col2] in ([FirstName],[Height],[LastName],[MiddleName],[Weight]) ) p
This is similar to some other questions on here however not close enough that I have all the info to do it myself. I want to pivot a date range with ability to limit by year. I'm not sure how they want to limit the data at the moment, maybe a year previous to a year forward for now.
I want the start day of the week to be Monday and end to be Sunday. Any quantities to fall between these days to be summed for the week per reftype with the date displaying as that starting Monday.
I have data below.
+---------+---------+------------------+-------------------------+------------------------+
| Itemid | RefType | name | OriginalReqDate | Qty |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 8 | Purchase order | 2016-03-04 00:00:00.000 | 2346.0000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 12 | Production order | 2016-03-04 00:00:00.000 | -1295.4000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 12 | Production order | 2016-03-07 00:00:00.000 | -3651.6000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 8 | Purchase order | 2016-03-11 00:00:00.000 | 4692.0000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 12 | Production order | 2016-03-14 00:00:00.000 | -1397.4000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 12 | Production order | 2016-03-21 00:00:00.000 | -958.8000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 45 | Formula line | 2016-03-28 00:00:00.000 | -696.1700000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 45 | Formula line | 2016-04-03 00:00:00.000 | -527.5500000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 8 | Purchase order | 2016-04-07 00:00:00.000 | 7038.0000000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
| B406227 | 45 | Formula line | 2016-04-07 00:00:00.000 | -1186.5500000000000000 |
+---------+---------+------------------+-------------------------+------------------------+
I would like output as
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| ItemId | RefType | Name | 2016-03-04 | 2016-03-11 | 2016-03-18 | 2016-03-25 | 2016-04-01 |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| B406227 | 1 | On-hand | 470.7600000000000000 | NULL | NULL | NULL | NULL |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| B406227 | 8 | Purchase order | 2346.0000000000000000 | 4692.0000000000000000 | NULL | NULL | NULL |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| B406227 | 12 | Production order | -1295.4000000000000000 | -3651.6000000000000000 | -1397.4000000000000000 | -958.8000000000000000 | NULL |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| B406227 | 33 | Planned purchase order | NULL | NULL | NULL | NULL | NULL |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| B406227 | 45 | Formula line | NULL | NULL | NULL | NULL | -696.1700000000000000 |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
| B406227 | 99 | Total for B406227 | 1992.1200000000000000 | 2561.7600000000000000 | 1164.3600000000000000 | 205.5600000000000000 | -490.6100000000000000 |
+---------+---------+------------------------+------------------------+------------------------+------------------------+-----------------------+-----------------------+
This is my attempt:
IF OBJECT_ID('tt', 'U') IS NOT NULL
DROP TABLE tt;
DECLARE #Columns NVARCHAR(MAX);
DECLARE #SQL NVARCHAR(MAX);
SELECT #Columns = COALESCE(#Columns + ',', '') + QUOTENAME(ReqDate)
FROM ( SELECT DISTINCT
CAST(ReqDate AS DATE) AS ReqDate
FROM SourceTable
) AS A
ORDER BY A.ReqDate;
SET #SQL = 'WITH PivotData AS (SELECT DataAreaId, ItemId, RefType, Name, ReqDate, Qty
FROM SourceTable)
SELECT DataAreaId, ItemId, RefType, Name ' + #Columns + '
INTO tt
FROM PivotData
PIVOT
(SUM(Qty)
FOR ReqDate IN (' + #Columns + ')
) AS PivotResult ORDER BY DataAreaId, ItemId, RefType';
EXEC (#SQL);
IF OBJECT_ID('adhoc.V_tt', 'V') IS NOT NULL
DROP VIEW adhoc.V_tt;
GO
CREATE VIEW adhoc.V_tt
AS
( SELECT *
FROM tt
);
EDIT: Dynamic SQL
With this you would get the column headers dynamically pointing on Sundays...
Attention: Depending on your system's culture you might want to have a look on SET DATEFIRST and ##DATEFIRST...
CREATE TABLE #TestTbl(Itemid VARCHAR(100),RefType INT,name VARCHAR(100),OriginalReqDate DATETIME,Qty DECIMAL(8,2));
INSERT INTO #TestTbl VALUES
('B406227',8,'Purchase order','2016-03-04T00:00:00.000',2346.0000000000000000)
,('B406227',12,'Production order','2016-03-04T00:00:00.000',-1295.4000000000000000)
,('B406227',12,'Production order','2016-03-07T00:00:00.000',-3651.6000000000000000)
,('B406227',8,'Purchase order','2016-03-11T00:00:00.000',4692.0000000000000000)
,('B406227',12,'Production order','2016-03-14T00:00:00.000',-1397.4000000000000000)
,('B406227',12,'Production order','2016-03-21T00:00:00.000',-958.8000000000000000)
,('B406227',45,'Formula line','2016-03-28T00:00:00.000',-696.1700000000000000)
,('B406227',45,'Formula line','2016-04-03T00:00:00.000',-527.5500000000000000)
,('B406227',8,'Purchase order','2016-04-07T00:00:00.000',7038.0000000000000000)
,('B406227',45,'Formula line','2016-04-07T00:00:00.000',-1186.5500000000000000);
DECLARE #colNames VARCHAR(MAX)=
STUFF
(
(
SELECT DISTINCT ',[' + CONVERT(VARCHAR(10),DATEADD(DAY,DATEPART(DW,OriginalReqDate) * (-1),OriginalReqDate),120) + ']'
FROM #TestTbl
FOR XML PATH('')
),1,1,''
);
DECLARE #cmd VARCHAR(MAX)=
'
SELECT p.*
FROM
(
SELECT tt.Itemid
,tt.RefType
,tt.name
,SUM(Qty) AS SumQty
,CONVERT(VARCHAR(10),DATEADD(DAY,DATEPART(DW,OriginalReqDate) * (-1),OriginalReqDate),120) AS ColumName
FROM #TestTbl AS tt
GROUP BY ItemId,RefType,name,CONVERT(VARCHAR(10),DATEADD(DAY,DATEPART(DW,OriginalReqDate) * (-1),OriginalReqDate),120)
) AS tbl
PIVOT
(
SUM(SumQty) FOR ColumName IN(' + #colNames + ')
) AS p
';
EXEC (#cmd);
DROP TABLE #TestTbl;
The result:
Itemid RefType name 2016-02-28 2016-03-06 2016-03-13 2016-03-20 2016-03-27 2016-04-03
B406227 8 Purchase order 2346.00 4692.00 NULL NULL NULL 7038.00
B406227 12 Production order -1295.40 -3651.60 -1397.40 -958.80 NULL NULL
B406227 45 Formula line NULL NULL NULL NULL -1223.72 -1186.55
Previous
This is a hard coded approach for the given sample data. If you want your columns to get a caption like 2016-03-04 you might think about dynamic SQL or you create the columnName (and the IN() list) for the correct output.
CREATE TABLE #TestTbl(Itemid VARCHAR(100),RefType INT,name VARCHAR(100),OriginalReqDate DATETIME,Qty DECIMAL(8,2));
INSERT INTO #TestTbl VALUES
('B406227',8,'Purchase order','2016-03-04T00:00:00.000',2346.0000000000000000)
,('B406227',12,'Production order','2016-03-04T00:00:00.000',-1295.4000000000000000)
,('B406227',12,'Production order','2016-03-07T00:00:00.000',-3651.6000000000000000)
,('B406227',8,'Purchase order','2016-03-11T00:00:00.000',4692.0000000000000000)
,('B406227',12,'Production order','2016-03-14T00:00:00.000',-1397.4000000000000000)
,('B406227',12,'Production order','2016-03-21T00:00:00.000',-958.8000000000000000)
,('B406227',45,'Formula line','2016-03-28T00:00:00.000',-696.1700000000000000)
,('B406227',45,'Formula line','2016-04-03T00:00:00.000',-527.5500000000000000)
,('B406227',8,'Purchase order','2016-04-07T00:00:00.000',7038.0000000000000000)
,('B406227',45,'Formula line','2016-04-07T00:00:00.000',-1186.5500000000000000);
SELECT p.*
FROM
(
SELECT tt.Itemid
,tt.RefType
,tt.name
,SUM(Qty) AS SumQty
,'w' + CAST(DATEPART(WEEK,OriginalReqDate) AS VARCHAR(MAX)) AS ColumName
FROM #TestTbl AS tt
GROUP BY ItemId,RefType,name,DATEPART(WEEK,OriginalReqDate)
) AS tbl
PIVOT
(
SUM(SumQty) FOR ColumName IN(w10,w11,w12,w13,w14,w15)
) AS p
DROP TABLE #TestTbl;
The result:
Itemid RefType name w10 w11 w12 w13 w14 w15
B406227 8 Purchase order 2346.00 4692.00 NULL NULL NULL 7038.00
B406227 12 Production order -1295.40 -3651.60 -1397.40 -958.80 NULL NULL
B406227 45 Formula line NULL NULL NULL NULL -1223.72 -1186.55
This is my attempt...
IF OBJECT_ID('tt', 'U') IS NOT NULL
DROP TABLE tt;
DECLARE #Columns NVARCHAR(MAX);
DECLARE #SQL NVARCHAR(MAX);
SELECT #Columns = COALESCE(#Columns + ',', '') + QUOTENAME(ReqDate)
FROM ( SELECT DISTINCT
CAST(ReqDate AS DATE) AS ReqDate
FROM SourceTable
) AS A
ORDER BY A.ReqDate;
SET #SQL = 'WITH PivotData AS (SELECT DataAreaId, ItemId, RefType, Name, ReqDate, Qty
FROM SourceTable)
SELECT DataAreaId, ItemId, RefType, Name ' + #Columns + '
INTO tt
FROM PivotData
PIVOT
(SUM(Qty)
FOR ReqDate IN (' + #Columns + ')
) AS PivotResult ORDER BY DataAreaId, ItemId, RefType';
EXEC (#SQL);
IF OBJECT_ID('adhoc.V_tt', 'V') IS NOT NULL
DROP VIEW adhoc.V_tt;
GO
CREATE VIEW adhoc.V_tt
AS
( SELECT *
FROM tt
);