UNPIVOT in SQL Server - sql-server

I have a table as below:
I need to convert this table's rows into columns
Expected output:
depotCode | containerYardCode | chargeCode | 20"GP | 40"GP |YAC 20"GP|YAC 40"GP
-------------------------------------------------------------------------------
HQ |CT1 KCT | DGC | 25.000 |25.000 | NULL |NULL
HQ |HARBOUR YARD | DGC |21.000 |32.000 |18.000 |28.000

Sample data:
declare #t table (depotCode varchar(2), containerYardCode varchar(25), chargeCode varchar(3), cargoTypeCode varchar(10), effectiveDate datetime, rate numeric(5,3), yardActualCost numeric(5,3), includeGST bit)
insert into #t values ('HQ', 'CT1 KCT', 'DGC', '20"GP', '6/1/2015', 25, null, 0)
insert into #t values ('HQ', 'CT1 KCT', 'DGC', '40"GP', '6/1/2015', 25, null, 0)
insert into #t values ('HQ', 'HARBOUR YARD', 'DGC', '20"GP', '1/1/2017', 21, 18, 0)
insert into #t values ('HQ', 'HARBOUR YARD', 'DGC', '40"GP', '1/1/2017', 32, 28, 0)
I think you need to join 2 pivot queries together, like this:
SELECT sq1.depotCode, sq1.containerYardCode, sq1.chargeCode, [20"GP], [40"GP], [YAC 20"GP], [YAC 40"GP]
FROM
(
SELECT depotCode, containerYardCode, chargeCode, [20"GP], [40"GP]
FROM (SELECT depotCode, containerYardCode, chargeCode, cargoTypeCode, rate FROM #t)t
PIVOT (sum(rate) for cargoTypeCode in ([20"GP], [40"GP]))p
)sq1
INNER JOIN
(
SELECT depotCode, containerYardCode, chargeCode, [20"GP] as [YAC 20"GP], [40"GP] as [YAC 40"GP]
FROM (SELECT depotCode, containerYardCode, chargeCode, cargoTypeCode, yardActualCost FROM #t)t
PIVOT (sum(yardActualCost) for cargoTypeCode in ([20"GP], [40"GP]))p
)sq2 ON sq1.depotCode = sq2.depotCode AND sq1.containerYardCode = sq2.containerYardCode AND sq1.chargeCode = sq2.chargeCode
And here's the output:
depotCode containerYardCode chargeCode 20"GP 40"GP YAC 20"GP YAC 40"GP
HQ CT1 KCT DGC 25.000 25.000 NULL NULL
HQ HARBOUR YARD DGC 21.000 32.000 18.000 28.000
The key is in the FROM clauses of the subqueries - only include the columns necessary for each individual pivot, and then join the subqueries on the common columns (e.g. depotCode, containerYardCode, chargeCode).

Related

SQL unpivot of multiple columns

I would like the following wide table to be unpivotted but only where a user has a true value against the field, along with the appropriate date.
Current State:
CUSTOMER_ID
First_Party_Email
Third_Party_Email
First_Party_Email_Date
Third_Party_Email_Date
40011111
1
1
2021-01-22 04:38:00.000
2021-01-17 06:38:00.000
50022222
NULL
1
NULL
2021-01-18 04:38:00.000
80066666
1
NULL
2021-01-24 05:38:00.000
NULL
_______________
_______________________
_______________________
_______________________________
_______________________________
Required State:
Customer_ID
Type
Value
Date
40011111
First_Party_Email
1
22/01/2021 04:38
40011111
Third_Party_Email
1
17/01/2021 06:38
50022222
Third_Party_Email
1
18/01/2021 04:38
80066666
First_Party_Email
1
24/01/2021 05:38
_______________________________________________________________________
Associated query to create table and my attempt that doesn't work:
create table Permissions_Obtained
(Customer_ID bigint
,First_Party_Email bit
,Third_Party_Email bit
,First_Party_Email_Date datetime
,Third_Party_Email_Date datetime
)
insert into Permissions_Obtained
(Customer_ID
,First_Party_Email
,Third_Party_Email
,First_Party_Email_Date
,Third_Party_Email_Date
)
VALUES
(40011111, 1, 1, '2021-01-22 04:38', '2021-01-17 06:38'),
(50022222, NULL, 1, NULL, '2021-01-18 04:38'),
(80066666, 1, NULL, '2021-01-24 05:38', null)
select *
from Permissions_Obtained
select
customer_id, Permission
from Permissions_Obtained
unpivot
(
GivenPermission
for Permission in (
First_Party_Email, Third_Party_Email
)
) unpiv1,
unpivot
(
GivenPermissionDate
for PermissionDate in (
First_Party_Email_Date, Third_Party_Email_Date
)
) unpiv2
where GivenPermission = 1
--drop table Permissions_Obtained
Any help would be massively appreciated. TIA
You cannot have multiple unpivots at the same time. Instead you can use Cross Apply or Inner join or union, union all or kind of joins depending on your requirement. I have added a sample answer for this using join and unpivot.
SELECT
unpvt.Customer_ID
, [Type]
, ISNULL(po.First_Party_Email ,po.Third_Party_Email) AS [Value]
,CASE WHEN unpvt.Type = 'First_Party_Email' THEN po.First_Party_Email_Date
ELSE po.Third_Party_Email_Date
END AS [Date]
FROM
(
SELECT
Customer_ID, First_Party_Email , Third_Party_Email
FROM Permissions_Obtained
) p
UNPIVOT
( [Value] FOR [Type] IN
(First_Party_Email , Third_Party_Email )
)AS unpvt
INNER JOIN Permissions_Obtained [po]
on [po].Customer_ID = unpvt.Customer_ID
When un-pivoting multiple columns, CROSS APPLY (VALUES is often the easiest and most effective solution.
It creates a virtual table per-row of the previous table, and therefore un-pivots it into separate rows.
SELECT
p.Customer_Id,
v.[Type],
v.Value,
v.Date
FROM Permissions_Obtained p
CROSS APPLY (VALUES
('First_Party_Email', p.First_Party_Email, p.First_Party_Email_Date),
('Third_Party_Email', p.Third_Party_Email, p.Third_Party_Email_Date)
) v([Type], Value, Date)
where v.Value IS NOT NULL;

How to use a Left Join with a bunch of other joins

I am trying to join two tables based on EquipWorkOrderID. Tables(EquipWorkOrder and EquipWorkOrderHrs)
With the query I have below it duplicates the row based on ID if there is two UserNm's for the Same ID. I want the two UserNm's and the Hrs if the ID match's in the same role if possible.
example of what my results give me now
EquipWorkOrderID/Equip/Description/Resolution/UserNm/Hrs
---------------------------------------------------------
1 / ForkLift / Bad /Fixed/John Doe / 2
1 /Forklift / Bad /Fixed/Jane Doe /2
What I would Like to see
EquipWorkOrderID/Equip/Description/Resolution/UserNm1/Hrs1/UserNm2/Hrs2
---------------------------------------------------------
1 / ForkLift / Bad /Fixed/John Doe / 2 / Jane Doe / 2
Select * From
(
Select
a.EquipWorkOrderID,
c.UserNm,
b.Hrs
From
EquipWorkOrder a
Left Join EquipWorkOrderHrs b
On a.EquipWorkOrderID = b.EquipWorkOrderID
Left Join AppUser c
On c.UserID = b.UserID
) t
Pivot (
Count(Hrs)
For UserNm IN (
[Tech1],
[Tech2],
[Tech3],
[Tech4],
[Tech5])
) AS pivot_table
I have placed the result of your query in a table (selection) and retrieved the data from it in a common table expression (cte). Replace the content of the CTE with your query and add the two new columns I created (UsrNum and HrsNum).
My solution uses a double pivot (one for the UserNm column and one for the Hrs column) followed by a grouping. This may not be ideal, but it gets the job done.
Here is a fiddle to show how I built up the solution.
Sample data
This just recreates the results of your current query.
create table selection
(
EquipWorkOrderID int,
Equip nvarchar(10),
Description nvarchar(10),
Resolution nvarchar(10),
UserNm nvarchar(10),
Hrs int
);
insert into selection (EquipWorkOrderID,Equip,Description,Resolution,UserNm,Hrs) values
(1, 'ForkLift', 'Bad', 'Fixed', 'John Doe', 2),
(1, 'Forklift', 'Bad', 'Fixed', 'Jane Doe', 2);
Solution
Replace the first part of the CTE with your query and add the two new columns.
with cte as
(
select EquipWorkOrderID,Equip,Description,Resolution,UserNm,Hrs,
'Usr' + convert(nvarchar(10),row_number() over(partition by Equip order by UserNm)) as 'UsrNum',
'Hrs' + convert(nvarchar(10),row_number() over(partition by Equip order by UserNm)) as 'HrsNum'
from selection
)
select ph.EquipWorkOrderId, ph.Equip, ph.Description, ph.Resolution,
max(ph.Usr1) as 'UserNm1',
max(ph.Hrs1) as 'Hrs1',
max(ph.Usr2) as 'UserNm2',
max(ph.Hrs2) as 'Hrs2'
from cte c
pivot (max(c.UserNm) for c.UsrNum in ([Usr1], [Usr2])) pu
pivot (max(pu.Hrs) for pu.HrsNum in ([Hrs1], [Hrs2])) ph
group by ph.EquipWorkOrderId, ph.Equip, ph.Description, ph.Resolution;
Result
Outcome looks like this. Jane Doe is UserNm1 because that is how the new UserNum column was constructed (order by UserNm). Adjust the order by if you need John Doe to remain first.
EquipWorkOrderId Equip Description Resolution UserNm1 Hrs1 UserNm2 Hrs2
----------------- --------- ------------ ----------- --------- ----- -------- -----
1 ForkLift Bad Fixed Jane Doe 2 John Doe 2
Edit: solution merged with original query (untested)
with cte as
(
SELECT TOP 1000
--Start original selection field list
ewo.EquipWorkOrderID,
ewo.DateTm,
equ.Equip,
equ.AccountCode,
equ.Descr,
ewo.Description,
ewo.Resolution,
sta.Status,
au.UserNm,
ewoh.Hrs,
cat.Category,
ml.MaintLoc,
equt.EquipType,
cre.Crew,
ewo.MeterReading,
typ.Type,
--Added two new fields
'Usr' + convert(nvarchar(10),row_number() over(partition by Equip order by UserNm)) as 'UsrNum',
'Hrs' + convert(nvarchar(10),row_number() over(partition by Equip order by UserNm)) as 'HrsNum'
FROM EquipWorkOrder ewo
JOIN EquipWorkOrderHrs ewoh
ON ewo.EquipWorkOrderID = ewoh.EquipWorkOrderID
JOIN AppUser au
ON au.UserID = ewoh.UserID
JOIN Category cat
ON cat.CategoryID = ewo.CategoryID
JOIN Crew cre
ON cre.CrewID = ewo.CrewID
JOIN Equipment equ
ON equ.EquipmentID = ewo.EquipmentID
JOIN Status sta
ON sta.StatusID = ewo.StatusID
JOIN PlantLoc pll
ON pll.PlantLocID = ewo.PlantLocID
JOIN MaintLocation ml
ON ml.MaintLocationID = ewo.MaintLocationID
JOIN EquipType equt
ON equt.EquipTypeID = ewo.EquipTypeID
JOIN Type typ
ON typ.TypeID = equ.TypeID
ORDER BY ewo.DateTm DESC
)
select ph.EquipWorkOrderId, ph.Equip, ph.Description, ph.Resolution,
max(ph.Usr1) as 'UserNm1',
max(ph.Hrs1) as 'Hrs1',
max(ph.Usr2) as 'UserNm2',
max(ph.Hrs2) as 'Hrs2'
from cte c
pivot (max(c.UserNm) for c.UsrNum in ([Usr1], [Usr2])) pu
pivot (max(pu.Hrs) for pu.HrsNum in ([Hrs1], [Hrs2])) ph
group by ph.EquipWorkOrderId, ph.Equip, ph.Description, ph.Resolution;
Edit2: how to use pivot...
Select pivot_table.*
From
(
Select a.EquipWorkOrderID,
b.Hrs,
c.UserNm,
'Tech' + convert(nvarchar(10), row_number() over(order by c.UserNm)) -- construct _generic_ names
From EquipWorkOrder a
Left Join EquipWorkOrderHrs b
On a.EquipWorkOrderID = b.EquipWorkOrderID
Left Join AppUser c
On c.UserID = b.UserID
) t
/*
Pivot (Count(Hrs) For UserNm IN ([Tech1], [Tech2], [Tech3], [Tech4], [Tech5])) AS pivot_table -- UserNm does not contain values like "Tech1" or "Tech2"
*/
Pivot (Count(Hrs) For GenUserNm IN ([Tech1], [Tech2], [Tech3], [Tech4], [Tech5])) AS pivot_table -- pivot over the _generic_ names

T-SQL AVG of multiple columns in a row

I'm trying to select the average sales per person per territory out of the AdventureWorks database.
Since this is aggregating multiple columns in a row instead of multiple rows in a column, it seems like I'd need a sub-query, temp table, maybe a CTE, but I'm not sure how to identify which direction to take or how to write it.
Desired result:
| SalesTerritory | SalesPeople | 2011 | 2012 | 2013 | 2014 | AvgSales
+----------------+-------------+--------+---------+---------+---------+----------
| Australia | 1 | NULL | NULL | 184105 | 1237705 | [avg]
| Canada | 2 | 115360 | 3426082 | 2568323 | etc... | [avg]
Code:
SELECT
pvt.SalesTerritory,
COUNT(pvt.SalesPersonID) AS SalesPeople,
SUM(pvt.[2011]),
SUM(pvt.[2012]),
SUM(pvt.[2013]),
SUM(pvt.[2014])
--What's the best way to AVG the sales by year by sales person for each territory here?
FROM
(SELECT
st.[Name] AS [SalesTerritory],
soh.[SalesPersonID],
soh.[SubTotal],
YEAR(DATEADD(m, 6, soh.[OrderDate])) AS [FiscalYear]
FROM
[Sales].[SalesPerson] sp
INNER JOIN
[Sales].[SalesOrderHeader] soh ON sp.[BusinessEntityID] = soh.[SalesPersonID]
INNER JOIN
[Sales].[SalesTerritory] st ON sp.[TerritoryID] = st.[TerritoryID]
INNER JOIN
[HumanResources].[Employee] e ON soh.[SalesPersonID] = e.[BusinessEntityID]
INNER JOIN
[Person].[Person] p ON p.[BusinessEntityID] = sp.[BusinessEntityID]) AS soh
PIVOT
(SUM([SubTotal]) FOR [FiscalYear] IN ([2011], [2012], [2013], [2014])) AS pvt
GROUP BY
pvt.SalesTerritory
You have several options:
1) Use cross apply. Query would look like:
select
*
from
(
--put your query here
) t
cross apply (select AvgSales = avg(v) from (values ([2011]), ([2012]), ([2013]), ([2014])) q(v)) q
2) Count average by yourself
SELECT
pvt.SalesTerritory,
COUNT(pvt.SalesPersonID) AS SalesPeople,
SUM(pvt.[2011]),
SUM(pvt.[2012]),
SUM(pvt.[2013]),
SUM(pvt.[2014]),
ISNULL(SUM(pvt.[2011]), 0) + ISNULL(SUM(pvt.[2012]), 0)
+ ISNULL(SUM(pvt.[2013]), 0) + ISNULL(SUM(pvt.[2014]), 0)
/ CASE WHEN SUM(pvt.[2011]) > 0 THEN 1 ELSE 0 END
+ CASE WHEN SUM(pvt.[2012]) > 0 THEN 1 ELSE 0 END
+ CASE WHEN SUM(pvt.[2013]) > 0 THEN 1 ELSE 0 END
+ CASE WHEN SUM(pvt.[2014]) > 0 THEN 1 ELSE 0 END
FROM
...
GROUP BY
pvt.SalesTerritory
As I understand your question, you need the average of the yearly sales per salesperson, for each territory. Uzi's answer provides the average of the yearly sales for all salespersons together, for each territory. You could divide that result by the number of salespersons, or use a query like this:
SELECT pvt.SalesTerritory, COUNT(pvt.SalesPersonID) AS SalesPeople,
SUM(pvt.[2011]) AS [2011], SUM(pvt.[2012]) AS [2012],
SUM(pvt.[2013]) AS [2013], SUM(pvt.[2014]) AS [2014],
AVG(pvt.AvgSubTotal) AS AvgSubTotal
FROM (
SELECT y.SalesTerritory, y.SalesPersonID, y.FiscalYear, y.SubTotal,
AVG(y.SubTotal) OVER (PARTITION BY y.SalesTerritory) AS AvgSubTotal
FROM (
SELECT x.SalesTerritory, x.SalesPersonID, x.FiscalYear, SUM(x.SubTotal) AS SubTotal
FROM (
SELECT st.Name AS SalesTerritory, soh.SalesPersonID, soh.SubTotal,
YEAR(DATEADD(m, 6, soh.OrderDate)) AS FiscalYear
FROM Sales.SalesPerson sp
INNER JOIN Sales.SalesOrderHeader soh ON sp.BusinessEntityID = soh.SalesPersonID
INNER JOIN Sales.SalesTerritory st ON sp.TerritoryID = st.TerritoryID
INNER JOIN HumanResources.Employee e ON soh.SalesPersonID = e.BusinessEntityID
INNER JOIN Person.Person p ON p.BusinessEntityID = sp.BusinessEntityID
) x
GROUP BY x.SalesTerritory, x.SalesPersonID, x.FiscalYear
) y
) AS soh
PIVOT (SUM(SubTotal) FOR FiscalYear IN ([2011], [2012], [2013], [2014])) AS pvt
GROUP BY pvt.SalesTerritory;
The result is different for the Northwest territory, but I'm not sure which result you want.

Multiple value IF statement, to show in select

Any help appreciated,
the code below is from a database which someone made, every time a receipt is made a unique receipt id is issued. same when a reversal is made a new receipt is issued. what links the two are the pay in number. If a reverse is issued, the reverse flag changes on the old receipt to Y and the new one says N. i have my query that select Minimum date and Max data, for receipts returned will have a much later date that when it was first created. The issue is when there is no reverse, it still pulls information as the min and max date are the same. I am fully aware that i need an if statement, but have no idea how to do it since i am new to Databases.
Select distinct r.receipt_date, r.receipt_no, r.doc_no as Payin_No,r.trans_amt,l.location_desc, ct.charge_type_desc,
(select un.first_name + ' ' + un.last_name)as cashier,
r.payee, r.comments, r.reverse_flag, ret1.returned_by, ret1.return_date
from Cashier..receipts as r
inner join Cashier..location as l on r.location_id=l.location_id
inner join [Cashier].[dbo].[charge_type] as ct on ct.charge_type_no=r.charge_type_no
inner join Cashier..user_name as un on un.user_name=(UPPER(r.created_by))
inner join (
select receipt_no as Return_Receipt ,
(select un2.first_name + ' ' + un2.last_name) as returned_by,
created_date as return_date, doc_no as Payin_no,
r1.reverse_flag
from Cashier..receipts as r1
inner join Cashier..user_name as un2 on un2.user_name=(UPPER(r1.created_by))
where doc_no = r1.doc_no
and created_date = (select MAX(created_date)
from Cashier..receipts where doc_no = r1.doc_no)) as ret1
on (ret1.Payin_no=r.doc_no)
where r.receipt_date = (select MIN(r1.receipt_date) from Cashier..receipts as r1 where r1.receipt_no = r.receipt_no )
Issue i am having, the return by is the same as created
Desired result
Is this basically what you're trying to do?
-- test data creation, for me
create table receipts (receipt_no int, receipt_date datetime, doc_no int, reverse_flag char(1), returned_by varchar(10), create_date datetime, created_date datetime)
insert into receipts values (1, '1/1/2016', 12345, 'Y', 'John', null, '1/1/2016')
insert into receipts values (2, '2/15/2016', 12345, 'N', null, '2/15/2016', '2/15/2016')
SELECT r.receipt_date, r.receipt_no, r.doc_no, r.reverse_flag, ret1.return_date
FROM receipts r
INNER JOIN (
SELECT doc_no, create_date as return_date
FROM receipts
WHERE reverse_flag = 'N')ret1 on r.doc_no = ret1.doc_no and ret1.return_date > r.receipt_date
WHERE r.reverse_flag = 'Y' and r.doc_no = 12345
If that's your goal, I think you just tack this on to the very end of your query:
and r.receipt_date < ret1.return_date
Edit: Based on your update, I think tack this onto the end:
and convert(date, r.receipt_date) < convert(date, ret1.return_date)
I'm still really not sure what you want.
select *
from ( select doc_no, min(receipt_date) as min_receipt_date
from receipts group by doc_no ) as min_receipt
left outer join ( select doc_no, max(receipt_date) as max_receipt_date
from receipts group by doc_no) as max_receipt
on min_receipt.doc_no = max_receipt.doc_no and
min_receipt.min_receipt_date <> max_receipt.max_receipt_date
for
insert into receipts values (1, '2016-01-01', 12345, 'Y', 'John', null, '2016-01-01');
insert into receipts values (2, '2016-03-15', 12345, 'N', null, '2016-03-15', '2016-03-15');
insert into receipts values (3, '2016-03-15', 123667, 'N', null, '2016-03-15', '2016-03-15');
yields
doc_no min_receipt_date doc_no max_receipt_date
12345 January, 01 2016 00:00:00 12345 March, 15 2016 00:00:00
123667 March, 15 2016 00:00:00 (null) (null)
but it assumes that the return date will never be the same as the original receipt date. I also left off the Y and N flags because I don't see how it's necessary if the min date is always the original purchase date. The other answer here uses the created_date but in your screenshot of the table data, you only show one date so I wrote it assuming only one date (the receipt date). I tested that on MySQL because SQLFiddle was hating on my SQL Server syntax for inserts and I don't have another way to test right now.
This is what i was looking for, figured it out. Thank you Max for opening insights on how i could tackle the problem.
Select distinct r.receipt_date, r.receipt_no, r.doc_no as Payin_No,r.trans_amt,l.location_desc, ct.charge_type_desc,
(select un.first_name + ' ' + un.last_name)as cashier,
r.payee, r.comments, r.cost_centre, r.item, r.programme, r.activity, r.btl_sof, r.reverse_flag, --, ret1.returned_by, ret1.return_date
(case when r.reverse_flag = 'Y' then (select (un2.first_name + ' ' + un2.last_name)from Cashier..receipts as r1
inner join Cashier..user_name as un2 on un2.user_name=(UPPER(r1.created_by)) where created_date = (select MAX(created_date)
from Cashier..receipts where doc_no = r.doc_no)) end ) as returned_by ,
(case when r.reverse_flag = 'Y' then (select created_date from Cashier..receipts as r1
inner join Cashier..user_name as un2 on un2.user_name=(UPPER(r1.created_by)) where created_date = (select MAX(created_date)
from Cashier..receipts where doc_no = r.doc_no)) end ) as returned_date
from Cashier..receipts as r
inner join Cashier..location as l on r.location_id=l.location_id
inner join [Cashier].[dbo].[charge_type] as ct on ct.charge_type_no=r.charge_type_no
inner join Cashier..user_name as un on un.user_name=(UPPER(r.created_by))
where r.receipt_date = (select MIN(r1.receipt_date) from Cashier..receipts as r1 where r1.receipt_no = r.receipt_no )

Using Table valued function the code

<pre>
I have 3 tables and one table valued function: EmpHistory,EmpRank,Emp and fnEmpRank.
The sample data are given as follows:
EmpHistory
EmpHistID EmpID RankID MonitorDate RankName
1 aba JPR 2008-10-06 Junior Prof
2 aba JPR 2009-11-07 Junior Prof
3 aba TERM 2012-2-08 Termoinated Prof
4 aba ASST 2012-6-22 lab Assistant
5 aba ASST 2012-7-2 Lab Assistant
6 aba TSST 2012-8-4 Terminated Assistant
EmpRank
RankID RankName
JPR Junior Professor
SPR Senior Professor
ASST Junior Assistant
SASST Senior Assistant
PL Principal
Employee
EmpID EmpStartDate
aba 2008-10-06
abc01 2007-09-23
sdh 2009-7-26
sbs 2012-2-8
The fnEmpRank function takes the emproleID and gives the employee history same as the empHistory Table. There is also empoyeerole table which has employeeroleid column.
Now my problem is: I want the second last professor rank of the employee i.e in this case I want Junior Professor row(i.e) 2nd row from emphistory table). Currntly my code is using emphistory table but now intead of that table I want to use fnEmpRank as it gives the same data. I am also giving the sample code.
select
a.EmpID,
a.StartDate,
J.RankID,
c.MonitorDate,
from dbo.vwEmployee A(nolock)
INNER join dbo.EmpHistory c(nolock) on c.Empid = a.EmpID
and c.EmpHistoryID = (select max(c1.EmpHistoryID)
from dbo.EmpHistory c1(nolock)
where c1.Empid = c.EmpID
and c1.MonitorDate =
(
I have 3 tables and one table valued function: EmpHistory,EmpRank,Emp and fnEmpRank.
The sample data are given as follows:
create table EmpHistory(
EmpHistID int,
EmpID varchar,
RankID varchar,
Monitordate Date,
Rankname varchar)
insert into EmpHistory
select 1,'aba','JPR','2008-10-6','Junior Professor'
insert into EmpHistory
select 2,'aba','JPR','2009-11-7','Junior Professor'
insert into EmpHistory
select 3,'aba','TERM','2012-2-8','Terminated Prof'
insert into EmpHistory
select 4,'aba','ASST','2012-6-22','Lab Assistant'
insert into EmpHistory
select 5,'aba','ASST','2012-7-2','Lab Assistant'
insert into EmpHistory
select 1,'aba','JPR','2012-8-4','Terminated Assistant'
create table EmpRank(
RankID varchar,
RankName varchar
)
insert into EmpRank
select 'JPR','Junior Professor'
insert into EmpRank
select 'SPR','Senior Professor'
insert into EmpRank
select 'ASST','Junior Assistant'
insert into EmpRank
select 'SASST','Senior Assistant'
insert into EmpRank
select 'PL','Principal'
create table Employee(
EmpID varchar,
EmpStartDate date
)
insert into Employee
select 'aba','2008-10-06'
insert into Employee
select 'abc01','2007-9-23'
insert into Employee
select 'sdh','2009-7-26'
insert into Employee
select 'sbs','2012-2-8'
The fnEmpRank function takes the emproleID and gives the employee history same as the empHistory Table. There is also empoyeerole table which has employeeroleid column.
Now my problem is: I want the second last professor rank of the employee i.e in this case I want Junior Professor row(i.e) 2nd row from emphistory table). Currntly my code is using emphistory table but now intead of that table I want to use fnEmpRank as it gives the same data. I am also giving the sample code.
select
a.EmpID,
a.StartDate,
J.RankID,
c.MonitorDate,
from dbo.vwEmployee A(nolock)
INNER join dbo.EmpHistory c(nolock) on c.Empid = a.EmpID
and c.EmpHistoryID = (select max(c1.EmpHistoryID)
from dbo.EmpHistory c1(nolock)
where c1.Empid = c.EmpID
and c1.MonitorDate =
(
SELECT MAX(C2.MonitorDate)
FROM dbo.EmpHistory C2
WHERE C2.EmpID = C1.EmpID
)
)
join dbo.EmpRank d(nolock) on d.RankID = a.RankID
left join dbo.EmpHistory f(nolock) on f.EmpID = a.EmpID
and f.EmpHistoryID = (select max(g.EmpHistoryID)
from dbo.EmpHistory g(nolock)
where g.EmpID = a.EmpID
AND G.RankID not like 'T%'
and g.EmpHistoryID < c.EmpHistoryID)
left join dbo.EmpRank h(nolock) on h.RankID = f.RankID
LEFT JOIN dbo.EmpHistory J(NOLOCK) ON J.EmpID = A.EmpID
AND J.EmpHistoryID = (
SELECT max(K.EmpHistoryID )
FROM dbo.EmpHistory K(NOLOCK)
WHERE K.EmpID = J.EmpID
AND K.AgentRankID NOT LIKE 'T%'
AND K.MonitorDate = (
SELECT max(M.MonitorDate )
FROM dbo.EmpHistory M(NOLOCK)
WHERE M.EmpID = J.EmpID
AND M.RankID NOT LIKE 'T%'
)
)
where
A.Prof=1
c.RankID like 'T%'
AND c.RankID <>'TSST'
AND A.StartDate is not null
Here there is one more problem: Even if the Employee is terminated from professor to Assitant, A.Prof values is still 1 and basically Assistant dont have the start dates but when professor are transformed to Assitant, they still contain the start date. How can I handle this in the code. Basically this code assumes that that if emp has the start date then he is the professor. Can any one help me?
SELECT MAX(C2.MonitorDate)
FROM dbo.EmpHistory C2
WHERE C2.EmpID = C1.EmpID
)
)
join dbo.EmpRank d(nolock) on d.RankID = a.RankID
left join dbo.EmpHistory f(nolock) on f.EmpID = a.EmpID
and f.EmpHistoryID = (select max(g.EmpHistoryID)
from dbo.EmpHistory g(nolock)
where g.EmpID = a.EmpID
AND G.RankID not like 'T%'
and g.EmpHistoryID < c.EmpHistoryID)
left join dbo.EmpRank h(nolock) on h.RankID = f.RankID
LEFT JOIN dbo.EmpHistory J(NOLOCK) ON J.EmpID = A.EmpID
AND J.EmpHistoryID = (
SELECT max(K.EmpHistoryID )
FROM dbo.EmpHistory K(NOLOCK)
WHERE K.EmpID = J.EmpID
AND K.AgentRankID NOT LIKE 'T%'
AND K.MonitorDate = (
SELECT max(M.MonitorDate )
FROM dbo.EmpHistory M(NOLOCK)
WHERE M.EmpID = J.EmpID
AND M.RankID NOT LIKE 'T%'
)
)
where
A.Prof=1
c.RankID like 'T%'
AND c.RankID <>'TSST'
AND A.StartDate is not null
Here there is one more problem: Even if the Employee is terminated from professor to Assitant, A.Prof values is still 1 and basically Assistant dont have the start dates but when professor are transformed to Assitant, they still contain the start date. How can I handle this in the code. Basically this code assumes that that if emp has the start date then he is the professor. Can any one help me?
</pre>
create fnemprank(#inputrankid int)
returns #result table(
EmpHistID int,
EmpID nvarchar,
RankID nvarchar,
MonitorDate date,
RankName nvarchar)
as
begin
insert into #result (EmpHistID,EmpID,RankID,MonitorDate,RankName)
select top 1 * from emphistory eh where eh.rankid=#inputrankid
order by eh.monitordate desc
return
end

Resources