Pivoting the employee column - sql-server

I have a table like so.
I need the final result show only one record for each order (essentially combine the suborders). Only sum up the revenue for distinct suborders (for order 0935744, sum up 575.04 + 31.68). An order can have a maximum of 2 employees
Final result should be like this:
order totalrevenue employee1 employee2
0813700 258.57 CREW NULL
0935744 606.72 95liv 95nat
I've tried using row_number and doing some joins but I've had no luck.
Sample code
SELECT N'0813700' AS [OrderNum], N'1077980' AS [SubOrder], N'CREW' AS [employeeid], N'258.57' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1257060' AS [SubOrder], N'95LIV' AS [employeeid], N'575.04' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1342944' AS [SubOrder], N'95LIV' AS [employeeid], N'31.68' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1257060' AS [SubOrder], N'95NAT' AS [employeeid], N'575.04' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1342944' AS [SubOrder], N'95NAT' AS [employeeid], N'31.68' AS [revenue]

this should give the result you are looking for:
create table #temp
(
ordernum int,
suborder int,
employeeid varchar(50),
revenue money
)
insert into #temp values(0813700, 1077980, 'CREW', 258.57)
insert into #temp values(0935744, 1257060, '95LIV', 575.04)
insert into #temp values(0935744, 1342944, '95LIV', 31.68)
insert into #temp values(0935744, 1257060, '95NAT', 575.04)
insert into #temp values(0935744, 1342944, '95NAT', 31.68)
select ordernum
, sum(revenueperorder) as total
, employee1
, case when employee1 = employee2 then null else employee2 end as employee2
from
(
select ordernum
, revenue as revenueperorder
, min(employeeid) as employee1
, max(employeeid) as employee2
from #temp
group by ordernum, revenue
) x
group by ordernum, employee1, employee2
drop table #temp
Results:
813700 258.57 CREW NULL
935744 606.72 95LIV 95NAT

Answers so far would require a hard coded pivot with employee1 & 2 defined as pivot entities, of couse, if your data is dynamic i'd imagine you'll have a varying number of employees (and thus would need a varying number of columns)? If so, i'd suggest you adopt a hybrid dynamic SQL / Pivot example such as:
Pivot Table and Concatenate Columns
or this:
PIVOT in sql 2005

DECLARE #SubOrder TABLE
(
OrderNum INT NOT NULL,
SubOrder INT NOT NULL,
EmployeeID NVARCHAR(50) NOT NULL,
Revenue NUMERIC(10, 2) NOT NULL
);
INSERT #SubOrder (OrderNum, SubOrder, EmployeeID, Revenue)
SELECT N'0813700' AS [OrderNum], N'1077980' AS [SubOrder], N'CREW' AS [employeeid], N'258.57' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1257060' AS [SubOrder], N'95LIV' AS [employeeid], N'575.04' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1342944' AS [SubOrder], N'95LIV' AS [employeeid], N'31.68' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1257060' AS [SubOrder], N'95NAT' AS [employeeid], N'575.04' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1342944' AS [SubOrder], N'95NAT' AS [employeeid], N'31.68' AS [revenue];
SELECT pvt.OrderNum,
pvt.TotalRevenue,
pvt.[1] AS Emp1,
pvt.[2] AS Emp2
FROM
(
SELECT dt.OrderNum,
dt.EmployeeID,
DENSE_RANK() OVER(PARTITION BY dt.OrderNum ORDER BY dt.EmployeeID) AS Rnk,
SUM(dt.Revenue) OVER(PARTITION BY dt.OrderNum) AS TotalRevenue
FROM
(
SELECT so.OrderNum,
so.EmployeeID,
ROW_NUMBER() OVER(PARTITION BY so.OrderNum, so.SubOrder ORDER BY ##SPID) AS RowNum,
so.Revenue
FROM #SubOrder so
) dt
WHERE dt.RowNum = 1
) src
PIVOT ( MAX(src.EmployeeID) FOR src.Rnk IN ([1], [2]) ) pvt
Results:
OrderNum TotalRevenue Emp1 Emp2
-------- ------------ ------ -----
813700 258.57 CREW NULL
935744 606.72 95LIV 95NAT
Intermediate results (...) src:
OrderNum EmployeeID Rnk TotalRevenue
-------- ---------- --- ------------
813700 CREW 1 258.57
935744 95LIV 1 606.72
935744 95NAT 2 606.72
Intermediate results (...) dt:
OrderNum EmployeeID RowNum Revenue
-------- ---------- ------ -------
813700 CREW 1 258.57
935744 95LIV 1 575.04
935744 95NAT 2 575.04
935744 95NAT 1 31.68
935744 95LIV 2 31.68

How about this? (Revised after comment from OP)
Assumptions:
No more than two employees per order.
One employee per sub-order (as in the example)
Revenue for a sub order is consistently duplicated on rows for the suborder (per example)
Code Example
select order, sum(revenue) as totalrevenue, max(employee1) as employee1,
case
when max(employee1) = max(employee2) then null
else max(employee2)
end as employee2
from (
select order, suborder, max(revenue) as revenue, max(employeeid)
from orders
group by order, suborder
) SubOrderTotal
group by order
Generally I would not recommend the rigid transform to two employees or the duplication of suborder revenue. Making such rigid assumptions often leads to bugs when dealing with real world data. But, I don't know your data.

Related

Select ID for corresponding max date using GROUP BY

My table structure as below
Category Sex Last Modified Date Id
7 2 2015-01-16 87603
7 1 2014-11-27 87729
7 2 2018-09-06 87135
7 1 2017-12-27 87568
My sql query as below
SELECT
MAX(Id) AS Id
FROM
Table
GROUP BY
Category, Sex
Result as below
87603
87729
But I would like to get Id as Max Last Modified Date. Correct result should be as below
87135
87568
You can use ROW_NUMBER() to find most recent row per group:
SELECT Id, LastModifiedDate
FROM (
SELECT Id, LastModifiedDate, ROW_NUMBER() OVER (PARTITION BY Category, Sex ORDER BY LastModifiedDate DESC) AS rnk
FROM t
) AS cte
WHERE rnk = 1
Use RANK() if you're interested in finding all rows with ties for LastModifiedDate.
You can also get it as
SELECT T.*
FROM
(
SELECT Sex,
MAX([Last Modified Date]) [Last Modified Date],
Category
FROM T
GROUP BY Sex,
Category
) TT INNER JOIN T ON T.[Last Modified Date] = TT.[Last Modified Date]
WHERE T.Sex = TT.Sex
AND
T.Category = TT.Category;
Returns:
+----------+-----+---------------------+-------+
| Category | Sex | Last Modified Date | Id |
+----------+-----+---------------------+-------+
| 7 | 2 | 06/09/2018 00:00:00 | 87135 |
| 7 | 1 | 27/12/2017 00:00:00 | 87568 |
+----------+-----+---------------------+-------+
We can get the solution by joining the same table with its grouped set:
SELECT MIN(T.Id)
FROM Table T
INNER JOIN (SELECT Category,
Sex,
MAX(LastModifiedDate) AS LastModifiedDate
FROM Table
GROUP BY Category, Sex) GT
ON GT.Category = T.Category
AND GT.Sex = T.Sex
AND GT.LastModifiedDate = T.LastModifiedDate
GROUP BY T.Category, T.Sex
Other option is to use correlated subquery :
select t.*
from table t
where t.LastModifiedDate = (select max(t1.LastModifiedDate)
from table t1
where t1.Category = t.Category and t1.Sex = t.Sex
);
Here are a few different approaches... (in no particular order)
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
GO
CREATE TABLE #TestData (
Category TINYINT NOT NULL,
Sex TINYINT NOT NULL,
LastModifiedDate DATE NOT NULL,
Id INT NOT NULL
);
GO
INSERT #TestData(Category, Sex, LastModifiedDate, Id) VALUES
(7, 2, '2015-01-16', 87603),
(7, 1, '2014-11-27', 87729),
(7, 2, '2018-09-06', 87135),
(7, 1, '2017-12-27', 87568);
GO
/* nonclustered index to support the query. */
CREATE UNIQUE NONCLUSTERED INDEX ix_TestData_Category_Sex_LastModifiedDate
ON #TestData (Category ASC, Sex ASC, LastModifiedDate DESC)
INCLUDE (Id);
GO
--====================================================
-- option 1: TOP(n) WITH TIES...
SELECT TOP (1) WITH TIES
td.Id
FROM
#TestData td
ORDER BY
ROW_NUMBER() OVER (PARTITION BY td.Category, td.Sex ORDER BY td.LastModifiedDate DESC);
GO
-----------------------------------------------------
-- option 2: Filter on ROW_NUMBER()...
WITH
cte_AddRN AS (
SELECT
td.Id,
rn = ROW_NUMBER() OVER (PARTITION BY td.Category, td.Sex ORDER BY td.LastModifiedDate DESC)
FROM
#TestData td
)
SELECT
arn.Id
FROM
cte_AddRN arn
WHERE
arn.rn = 1;
GO
-----------------------------------------------------
-- option 3: binary concatination...
SELECT
Id = CONVERT(INT, SUBSTRING(MAX(bv.bin_val), 4, 4))
FROM
#TestData td
CROSS APPLY ( VALUES (CONVERT(BINARY(3), td.LastModifiedDate) + CONVERT(BINARY(4), td.Id)) ) bv (bin_val)
GROUP BY
td.Category,
td.Sex;
GO
--====================================================

Variable within SQL query

I have this:
SELECT NEWID() as id,
'OwnerReassign' as name,
1 as TypeId,
'MyOrganisation' as OrgName,
'07DA8E53-74BD-459C-AF94-A037897A51E3' as SystemUserId,
0 as StatusId,
GETDATE() as CreatedAt,
'{"EntityName":"account","Ids":["'+CAST(AccountId as varchar(50))+'"],"OwnerId":"0C01C994-1205-E511-988E-26EE4189191B"}' as [Parameters]
FROM Account
WHERE OwnerIdName IN ('John Smith') AND New_AccountType = 1
Within the parameter field is an id (0C01C994-1205-E511-988E-26EE4189191B). Is it possible it could sequentially assign a different id from a list for each row? There are 5 id's in total.
What i'm trying to get to is this result set equally split between the 5 different id's.
Thanks
You can add one more NEWID() in the sub query and handle in the SELECT as below:
SELECT id, [name], TypeId, OrgName, SystemUserId, StatusId, CreatedAt,
'{"EntityName":"account","Ids":["' + AccountId +'"],"OwnerId":"' + ParamId + '"}' as [Parameters]
FROM (
SELECT NEWID() as id,
'OwnerReassign' as name,
1 as TypeId,
'MyOrganisation' as OrgName,
'07DA8E53-74BD-459C-AF94-A037897A51E3' as SystemUserId,
0 as StatusId,
GETDATE() as CreatedAt,
CAST(NEWID() AS VARCHAR (36)) as ParamId,
CAST(AccountId as varchar(50)) as AccountId
FROM Account
WHERE OwnerIdName IN ('John Smith') AND New_AccountType = 1
) A
You can use something like the following. Basically, use a row number for both your IDs and your data table to update, then do a MOD (%) operation with the amount of ID's you want to assign, so your data table to update is split into N groups. Then use that group ID to assign each ID.
IF OBJECT_ID('tempdb..#IDsToAssign') IS NOT NULL
DROP TABLE #IDsToAssign
CREATE TABLE #IDsToAssign (
IDToAssign VARCHAR(100))
-- 3 IDs example
INSERT INTO #IDsToAssign (
IDToAssign)
SELECT IDToAssign = NEWID()
UNION ALL
SELECT IDToAssign = NEWID()
UNION ALL
SELECT IDToAssign = NEWID()
DECLARE #AmountIDsToAssign INT = (SELECT COUNT(1) FROM #IDsToAssign)
IF OBJECT_ID('tempdb..#Account') IS NOT NULL
DROP TABLE #Account
CREATE TABLE #Account (
PrimaryKey INT PRIMARY KEY,
AssignedID VARCHAR(100))
-- 10 Rows example
INSERT INTO #Account (
PrimaryKey)
VALUES
(100),
(200),
(351),
(154),
(194),
(345),
(788),
(127),
(124),
(14)
;WITH DataRowNumber AS
(
SELECT
A.*,
RowNumber = ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM
#Account AS A
),
IDsRowNumbers AS
(
SELECT
D.IDToAssign,
RowNumber = ROW_NUMBER() OVER (ORDER BY D.IDToAssign)
FROM
#IDsToAssign AS D
),
NewIDAssignation AS
(
SELECT
R.*,
IDRowNumberAssignation = (R.RowNumber % #AmountIDsToAssign) + 1
FROM
DataRowNumber AS R
)
UPDATE A SET
AssignedID = R.IDToAssign
FROM
NewIDAssignation AS N
INNER JOIN IDsRowNumbers AS R ON N.IDRowNumberAssignation = R.RowNumber
INNER JOIN #Account AS A ON N.PrimaryKey = A.PrimaryKey
SELECT
*
FROM
#Account AS A
ORDER BY
A.AssignedID
/* Results:
PrimaryKey AssignedID
----------- ------------------------------------
124 1CC7F0F1-7EDE-4F7F-B0A3-739D74A62390
194 1CC7F0F1-7EDE-4F7F-B0A3-739D74A62390
351 1CC7F0F1-7EDE-4F7F-B0A3-739D74A62390
788 2A58A573-EDCB-428E-A87A-6BFCED265A9C
200 2A58A573-EDCB-428E-A87A-6BFCED265A9C
127 2A58A573-EDCB-428E-A87A-6BFCED265A9C
14 2A58A573-EDCB-428E-A87A-6BFCED265A9C
100 FD8036DA-0E15-453E-8A59-FA3C2BDB8FB1
154 FD8036DA-0E15-453E-8A59-FA3C2BDB8FB1
345 FD8036DA-0E15-453E-8A59-FA3C2BDB8FB1
*/
The ordering of the ROW_NUMBER() function will determine how ID's are assigned.
You could potentially do this by using the ROW_NUMBER() field in a subquery; for example:
SELECT NEWID() as id, 'OwnerReassign' as name, 1 as TypeId,
'MyOrganisation' as OrgName,
'07DA8E53-74BD-459C-AF94-A037897A51E3' as SystemUserId,
0 as StatusId, GETDATE() as CreatedAt,
case B / ##ROWCOUNT
when 0 then '0C01C994-1205-E511-988E-26EE4189191B'
when 1 then '12345677-1205-E511-988E-26EE4189191B'
when 2 then '66666666-1205-E511-988E-26EE4189191B'
etc...
end
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY A.Id)
FROM Account A
WHERE OwnerIdName IN ('John Smith') AND New_AccountType = 1
) AS B
If you want the system to pick those values then you could put then in their own temporary table, too.

TSQL - Display the date which exist in all Model

MODEL DateCreated
----------------------
MODEL_1 2017-07-20
MODEL_1 2017-07-19
MODEL_1 2017-06-10
MODEL_1 2017-06-02
MODEL_2 2017-07-18
MODEL_2 2017-07-17
MODEL_2 2017-06-10
MODEL_2 2017-06-02
MODEL_3 2017-07-20
MODEL_3 2017-07-12
MODEL_3 2017-06-10
MODEL_3 2017-06-02
MODEL_3 2017-05-16
Expected result
DateCreated_Exist_In_All_Model
------------------------------
2017-06-10
2017-06-02
This means, only those DateCreated exist in all model will be displayed
Thanks to everyone who willing to help me.
One approach would be to aggregate on the creation date and then compare the count of distinct models appearing on each date against the total number of models appearing in the table.
SELECT
DateCreated AS DateCreated_Exist_In_All_Model
FROM [yourTable]
GROUP BY DateCreated
HAVING COUNT(DISTINCT MODEL) = (SELECT COUNT(DISTINCT MODEL) FROM yourTable)
Note that even though there appear to be only 3 models in your data set, we can make the query more flexible by using a subquery which counts the number of distinct models in the entire table.
DECLARE #Temp table(Model varchar(1000), DateCreated date)
INSERT INTO #Temp
SELECT 'MODEL_1' , '2017-07-20'
UNION ALL
SELECT 'MODEL_1', '2017-07-19'
UNION ALL
SELECT 'MODEL_1', '2017-06-10'
UNION ALL
SELECT 'MODEL_1', '2017-06-02'
UNION ALL
SELECT 'MODEL_2', '2017-07-17'
UNION ALL
SELECT 'MODEL_2', '2017-06-10'
UNION ALL
SELECT 'MODEL_2', '2017-06-02'
UNION ALL
SELECT 'MODEL_3', '2017-07-12'
UNION ALL
SELECT 'MODEL_3 ', '2017-06-10'
UNION ALL
SELECT 'MODEL_3 ', '2017-06-02'
UNION ALL
SELECT 'MODEL_3' , '2017-05-16'
DECLARE #ModelCount int
SELECT #ModelCount = COUNT(distinct Model) from #Temp
SELECT DISTINCT DateCreated FROM (
SELECT
COUNT(MODEL) OVER(PARTITION BY DateCreated) AS Counts, Model,
DateCreated
FROM #Temp) AS D
WHERE D.Counts = #ModelCount
Or
SELECT DateCreated FROM (
SELECT
ROW_NUMBER() OVER(PARTITION BY DateCreated ORDER BY DateCreated) as Rownum,
COUNT(MODEL) OVER(PARTITION BY DateCreated) AS Counts, Model,
DateCreated
FROM #Temp) AS D
WHERE D.Counts = #ModelCount and D.Rownum = 1

Trying to pivot event dates in t-sql without using a cursor

I have the following table:
What I want is to get to this:
EventTypeId 1 and 3 are valid start events and EventTypeId of 2 is the only valid end event.
I have tried to do a pivot, but I don't believe a pivot will get me the multiple events for a person in the result set.
SELECT PersonId, [1],[3],[2]
FROM
(
SELECT PersonId, EventTypeId, EventDate
from #PersonEvent
) as SourceTable
PIVOT
(
count(EventDate) FOR EventTypeId
IN ([1],[3],[2])
) as PivotTable
Select PersonID,
Min(Case WHEN EventTypeId IN (1,3) THEN EventDate END) as StartDate,
Min(Case WHEN EventTypeId IN (2) THEN EventDate END) as EndDate
FROM #PersonEvent
group by personid
I can do a cursor, but my original table is over 90,000 rows, and this is to be for a report, so I don't think I can use that option. Any other thoughts that I might be missing?
Assuming the table is called [dbo].[PersonEventRecords] this will work...
With StartEvents As
(
Select *
From [dbo].[PersonEventRecords]
Where EventTypeId In (1,3)
), EndEvents As
(
Select *
From [dbo].[PersonEventRecords]
Where EventTypeId In (2)
)
Select IsNull(se.PersonId,ee.PersonId) As PersonId,
se.EventTypeId As StartEventTypeId,
se.EventDate As StartEventDate,
ee.EventTypeId As EndEventTypeId,
ee.EventDate As EndEventDate
From StartEvents se
Full Outer Join EndEvents ee
On se.PersonId = ee.PersonId
And se.EventSequence = ee.EventSequence - 1
Order By IsNull(se.PersonId,ee.PersonId),
IsNull(se.EventDate,ee.EventDate);
/**** TEST DATA ****/
If Object_ID('[dbo].[PersonEventRecords]') Is Not Null
Drop Table [dbo].[PersonEventRecords];
Create Table [dbo].[PersonEventRecords]
(
PersonId Int,
EventTypeId Int,
EventDate Date,
EventSequence Int
);
Insert [dbo].[PersonEventRecords]
Select 1,1,'2012-10-13',1
Union All
Select 1,2,'2012-10-20',2
Union All
Select 1,1,'2012-11-01',3
Union All
Select 1,2,'2012-11-13',4
Union All
Select 2,1,'2012-05-07',1
Union All
Select 2,2,'2012-06-01',2
Union All
Select 2,3,'2012-07-01',3
Union All
Select 2,2,'2012-08-30',4
Union All
Select 3,2,'2012-04-05',1
Union All
Select 3,1,'2012-05-04',2
Union All
Select 3,2,'2012-05-24',3
Union All
Select 4,1,'2013-01-03',1
Union All
Select 4,1,'2013-02-20',2
Union All
Select 4,2,'2013-03-20',3;
Try this
SELECT E1.PersonId, E1.EventTypeId, E1.EventDate, E2.EventTypeId, E2.EventDate
FROM PersonEvent AS E1
OUTER APPLY(
SELECT TOP 1 PersonEvent.EventTypeId, PersonEvent.EventDate
FROM PersonEvent
WHERE PersonEvent.PersonId = E1.PersonId
AND PersonEvent.EventSequence = E1.EventSequence + 1
AND PersonEvent.EventTypeId = 2
) AS E2
WHERE E1.EventTypeId = 1 OR E1.EventTypeId = 3
UNION
SELECT E3.PersonId, NULL, NULL, E3.EventTypeId, E3.EventDate
FROM PersonEvent E3
WHERE E3.EventTypeId = 2
AND NOT EXISTS(
SELECT *
FROM PersonEvent
WHERE PersonEvent.PersonId = E3.PersonId
AND PersonEvent.EventSequence = E3.EventSequence - 1)
It is not completely clear how do you want the result to be ordered – add order as needed.

How to use Row_Number to group a resultset

i'm stuck with a query and i don't want to use a while loop or another nasty method to do this.
Here's the situation:
I've got a query that gets some data, and i need to calculate a column based on 2 other columns.
My results are as follow:
Type | Customer | Cycle | Amount | Expiration | Row_Number (Partition By Customer, Cycle)
So, my row_number column needs to "group" customers and cycles, here's a Fiddle to better understand it
Here's an example:
As you can see, iteration column is correctly applied as far as i know what row_number does.
But i need to do this:
Is there a way to do this with Row_Number ?
or should i need store the data in a temp table, loop through it and update this ITERATION column?
Maybe a CTE?
Any help on this will be highly appreciated. Thanks!
just run this as new query, replace what you need in your query...
WITH T(StyleID, ID)
AS (SELECT 1,1 UNION ALL
SELECT 1,1 UNION ALL
SELECT 1,1 UNION ALL
SELECT 1,2)
SELECT *,
RANK() OVER(PARTITION BY StyleID ORDER BY ID) AS 'RANK',
ROW_NUMBER() OVER(PARTITION BY StyleID ORDER BY ID) AS 'ROW_NUMBER',
DENSE_RANK() OVER(PARTITION BY StyleID ORDER BY ID) AS 'DENSE_RANK'
FROM T
regards,
Valentin
You could use DENSE_RANK function instead of ROW_NUMBER.
DECLARE #MyTable TABLE
(
Customer NVARCHAR(100) NOT NULL,
[Cycle] SMALLINT NOT NULL
);
INSERT #MyTable VALUES ('C1', 2010);
INSERT #MyTable VALUES ('C1', 2010);
INSERT #MyTable VALUES ('C1', 2011);
INSERT #MyTable VALUES ('C1', 2012);
INSERT #MyTable VALUES ('C1', 2012);
INSERT #MyTable VALUES ('C1', 2012);
INSERT #MyTable VALUES ('C2', 2010);
INSERT #MyTable VALUES ('C2', 2010);
SELECT t.Customer, t.[Cycle],
DENSE_RANK() OVER(PARTITION BY t.Customer ORDER BY t.[Cycle]) AS Rnk
FROM #MyTable t
ORDER BY Customer, [Cycle];
Results:
Customer Cycle Rnk
-------- ------ ---
C1 2010 1
C1 2010 1
C1 2011 2
C1 2012 3
C1 2012 3
C1 2012 3
C2 2010 1
C2 2010 1
SQL Fiddle

Resources