Recursive CTE with tree hierarchy SQL Server - sql-server

I need to use recursive with SQL Server, but i don't know how use it with my hierarchy tree.
I need some help for creating my query and know if it's possible with CTE Recursion.
My example :
I have two tables : piece (piece_id) and piece_equivalence(piece1_id, piece2_id)
First, i need to get all the piece from the first table :
SELECT DISTINCT p.record_id FROM piece p
Secondly, i need to check if the piece exists in the second table (piece1_id or piece2_id)
SELECT DISTINCT p.record_id
FROM piece p
inner join piece_equivalence pe
ON (pe.piece1_id = p.record_id OR pe.piece2_id = p.record_id)
Thirdly, if the piece exist, I need to check the piece1_id or piece2_id. This ID can have an equivalence too.
So I will check the second step too with my piece1_id or piece2_id.
Currently I did recursive calling the same function with parameter piece1 or piece2.
Graphical view with nodes :
piece_id
___/ \___
/ \
table : piece_equivalence piece1_id or piece2_id piece1_id or piece2_id
/ \ / \
table : piece_equivalence piece1_id or piece2_id same same same
Graphical with letters :
A
___/ \___ ________
/ \ \
B C D
/ \ / \ / \
D E F B E G
/ /
G H
A : piece
B, C, D, E, F, G, H are equivalences.
WARNING : I need to stock all piece with their equivalence in a temp table. For avoiding duplicate entry or infinity loop we must check this temp table the data exists or not.
EDIT :
I did this :
WITH pieces_CTE
AS
(
SELECT TOP 1 p.record_id as parent,
case when pe.piece1_id <> p.record_id then pe.piece1_id else pe.piece2_id end as enfant,
1 as level
FROM piece p
inner join piece_equivalence pe ON (pe.piece1_id = p.record_id OR pe.piece2_id = p.record_id) AND pe.pertinence = 100
AND pe.piece1_id <> pe.piece2_id
UNION ALL
SELECT c.parent, case when enfant.piece1_id <> c.parent then enfant.piece1_id else enfant.piece2_id end as enfant,
c.level+1
from pieces_CTE c
INNER JOIN piece_equivalence enfant ON (enfant.piece1_id = c.parent OR enfant.piece2_id = c.parent)
WHERE enfant.pertinence = 100
)
SELECT * from pieces_CTE ORDER BY parent,level,enfant
OPTION (MAXRECURSION 32767)
The statement terminated. The maximum recursion 100 has been exhausted
before statement completion.
But I have a large record on it, and my query has to much records, I think it's impossible to use CTE with many redundant cycles...
But why I have the same error with TOP 1 ?

Before you start with the Recursive CTE, You need to Know Few Things
You can't use DISTINCT or UNION
You Can't Use LEFT JOIN in the Recursive part of the CTE
You need to make sure the Recursion does not end in a Dead Lock. Otherwise by the Default Recursion count of 100, the CTE Will Terminate. Please see the below Example :
DECLARE #MyData TABLE (
SeqNo INT IDENTITY(1,1),
FullName VARCHAR(50),
ManagerId INT )
INSERT INTO #MyData (
FullName ) VALUES('CEO')
--Insert Sub Components
INSERT INTO #MyData (
FullName,
ManagerId ) SELECT
'Department Head 1',
ManagerId = SeqNo
FROM #MyData
WHERE FullName = 'CEO' UNION SELECT
'Department Head 2',
ManagerId = SeqNo
FROM #MyData
WHERE FullName = 'CEO' UNION SELECT
'Department Head 3',
ManagerId = SeqNo
FROM #MyData
WHERE FullName = 'CEO'
INSERT INTO #MyData (
FullName,
ManagerId ) SELECT
'Manager 1',
ManagerId = SeqNo
FROM #MyData
WHERE FullName = 'Department Head 1' UNION SELECT
'Manager 2',
ManagerId = SeqNo
FROM #MyData
WHERE FullName = 'Department Head 1' UNION SELECT
'Manager 3',
ManagerId = SeqNo
FROM #MyData
WHERE FullName = 'Department Head 3'
;WITH CTE AS (
SELECT
SeqNo,
FullName,
Manager = ISNULL(FullName,'')
FROM #MyData
WHERE ManagerId IS NULL
UNION ALL
SELECT
MD.SeqNo,
MD.FullName,
Manager = ISNULL(CTE.FullName,'')
FROM CTE
INNER JOIN #MyData MD
ON CTE.SeqNo = MD.ManagerId ) SELECT
*
FROM CTE
SeqNo FullName Manager
----------- -------------------------------------------------- --------------------------------------------------
1 CEO CEO
2 Department Head 1 CEO
3 Department Head 2 CEO
4 Department Head 3 CEO
7 Manager 3 Department Head 3
5 Manager 1 Department Head 1
6 Manager 2 Department Head 1

Related

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

TSQL Compare tables based on multiple rows same column?

i will try and be as clear as possible on this one, as i have no idea what to do next and would love a kick in the right direction.
Im trying to compare the values within 2 tables. The tables look like this:
Table1:
Table2
INSERT INTO #table1 ([elementName], [elementValue])
VALUES
('t1','Project'),
('p1','test1'),
('n1','value1'),
('t2','Project'),
('p2','test2'),
('n2','value2'),
('t3','Project'),
('p3','test3'),
('n3','value3'),
('t4',''),
('p4',''),
('n4',''),
('t5',''),
('p5',''),
('n5','')
INSERT INTO #table2 ([elementName], [elementValue])
VALUES
('t1','Project'),
('p1',''),
('n1',''),
('t2','Project'),
('p2','test3'),
('n2','value123'),
('t3','Project'),
('p3',''),
('n3',''),
('t4','Package'),
('p4',''),
('n4',''),
('t5','Project'),
('p5','Testtest'),
('n5','valuevalue')
I used this code to fill the testtables. Normally this is an automated process, and the tables are filled from an XML string.
Furthermore, the numbers in the element name are considered "groups" meaning T1 P1 and N1 are together.
I would like to compare T1 and P1 etc from Table1 to any combination of T and P from table2
If they match, i would like to overwrite the value of Table 1 N1 with the value of the matched N on table 2. (in the example, table1 N3 would be replaced with table2 N2
Besides that i also want to keep every group in table 1 that is not in table 2
but also add every group that is in table 2 but not in table 1 on one of the blank spots.
Last but not least, if the T value is filled, but P value is empty, it does not have to overwrite/change anything in table1.
The expected result would be this:
Table1:
i made the changes bold.
I dont really have an idea on where to start on this. Ive tried functions as except and intersect, but did not get even close to what i would like to see.
with t1 as (
select * from (values
('t1','Project'),
('p1','test1'),
('n1','value1'),
('t2','Project'),
('p2','test2'),
('n2','value2'),
('t3','Project'),
('p3','test3'),
('n3','value3'),
('t4',''),
('p4',''),
('n4',''),
('t5',''),
('p5',''),
('n5','')
) v([elementName], [elementValue])
),
t2 as (
select * from (values
('t1','Project'),
('p1',''),
('n1',''),
('t2','Project'),
('p2','test3'),
('n2','value123'),
('t3','Project'),
('p3',''),
('n3',''),
('t4','Package'),
('p4',''),
('n4',''),
('t5','Project'),
('p5','Testtest'),
('n5','valuevalue')
) v([elementName], [elementValue])
),
pivoted_t1 as (
select *
from
(select left([elementName], 1) letter, right([elementName], len([elementName]) - 1) number, [elementValue] as value from t1) t1
pivot(min(value) for letter in ([t], [p], [n])) pvt1
),
pivoted_t2 as (
select *
from
(select left([elementName], 1) letter, right([elementName], len([elementName]) - 1) number, [elementValue] as value from t2) t2
pivot(min(value) for letter in ([t], [p], [n])) pvt2
),
amended_values as (
select
pvt1.number,
coalesce(pvt2.t, pvt1.t) as t,
coalesce(pvt2.p, pvt1.p) as p,
coalesce(pvt2.n, pvt1.n) as n,
count(case when pvt1.t = '' and pvt1.p = '' then 1 end) over(order by pvt1.number rows between unbounded preceding and current row) as empty_row_number
from
pivoted_t1 pvt1
left join pivoted_t2 pvt2 on pvt1.t = pvt2.t and pvt1.p = pvt2.p and pvt1.t <> '' and pvt1.p <> ''
),
added_new_values as (
select
a.number,
coalesce(n.t, a.t) as t,
coalesce(n.p, a.p) as p,
coalesce(n.n, a.n) as n
from
amended_values a
left join (
select number, t, p, n, row_number() over (order by number) as row_number
from pivoted_t2 t2
where
t2.t <> ''
and t2.p <> ''
and not exists (select * from pivoted_t1 t1 where t1.t = t2.t and t1.p = t2.p)
) n on n.row_number = a.empty_row_number
)
select
concat([elementName], number) as [elementName],
[elementValue]
from
added_new_values
unpivot ([elementValue] for [elementName] in ([t], [p], [n])) upvt
;

T-SQL query to show all the past steps, active and future steps

I have 3 tables in SQL Server:
map_table: (workflow map path)
stepId step_name
----------------
1 A
2 B
3 C
4 D
5 E
history_table:
stepId timestamp author
----------------------------
1 9:00am John
2 9:20am Mary
current_stageTable:
Id currentStageId waitingFor
------------------------------------
12345 3 Kat
I would like to write a query to show the map with the workflow status. Like this result here:
step name time author
----------------------------
1 A 9:00am John
2 B 9:20am Mary
3 C waiting Kat
4 D
5 E
I tried left join
select
m.stepId, m.step_name, h.timestamp, h.author
from
map_table m
left join
history_table h on m.stepId = h.stepId
I thought it will list all the records from the map table, since I am using left join, but somehow it only shows 3 records which is from history table..
So I changed to
select
m.stepId, m.step_name, h.timestamp, h.author
from
map_table m
left join
history_table h on m.stepId = h.stepId
union
select
m.stepId, m.step_name, '' as timestamp, '' as author
from
map_table m
where
m.stageId not in (select stageId from history_table)
order by
m.stepId
Then it list the result almost as I expected, but how do I add the 3rd table in to show the current active stage?
Thank you very much for all your help!! Much appreciated.
Looks like it's what you asked:
with map_table as (
select * from (values (1,'A')
,(2,'B')
,(3,'C')
,(4,'D')
,(5,'E')) t(stepId, step_name)
)
, history_table as (
select * from (values
(1,'9:00am','John')
,(2,'9:20am','Mary')) t(stepId, timestamp, author)
)
, current_stapeTable as (
select * from (values (2345, 3, 'Kat')) t(Id, currentStageId, waitingFor)
)
select
m.stepId, m.step_name
, time = coalesce(h.timestamp, case when c.waitingFor is not null then 'waiting' end)
, author = coalesce(h.author, c.waitingFor)
from
map_table m
left join history_table h on m.stepId = h.stepId
left join current_stapeTable c on m.stepId = c.currentStageId
I think a union fits well with the data and avoids the coalescing the values on multiple joins.
with timeline as (
select stepId, "timestamp" as ts, author from history_table
union all
select currentStageId, 'waiting', waitingFor from current_stageTable
)
select step_id, step_name, "timestamp", author
from
map_table as m left outer join timeline as t
on t.stepId = m.stepId

Update records SQL?

First when I started this project seemed very simple. Two tables, field tbl1_USERMASTERID in Table 1 should be update from field tbl2_USERMASTERID Table 2. After I looked deeply in Table 2, there is no unique ID that I can use as a key to join these two tables. Only way to match the records from Table 1 and Table 2 is based on FIRST_NAME, LAST_NAME AND DOB. So I have to find records in Table 1 where:
tbl1_FIRST_NAME equals tbl2_FIRST_NAME
AND
tbl1_LAST_NAME equals tbl2_LAST_NAME
AND
tbl1_DOB equals tbl2_DOB
and then update USERMASTERID field. I was afraid that this can cause some duplicates and some users will end up with USERMASTERID that does not belong to them. So if I find more than one record based on first,last name and dob those records would not be updated. I would like just to skip and leave them blank. That way I wouldn't populate invalid USERMASTERID. I'm not sure what is the best way to approach this problem, should I use SQL or ColdFusion (my server side language)? Also how to detect more than one matching record?
Here is what I have so far:
UPDATE Table1 AS tbl1
LEFT OUTER JOIN Table2 AS tbl2
ON tbl1.dob = tbl2.dob
AND tbl1.fname = tbl2.fname
AND tbl1.lname = tbl2.lname
SET tbl1.usermasterid = tbl2.usermasterid
WHERE LTRIM(RTRIM(tbl1.usermasterid)) = ''
Here is query where I tried to detect duplicates:
SELECT DISTINCT
tbl1.FName,
tbl1.LName,
tbl1.dob,
COUNT(*) AS count
FROM Table1 AS tbl1
LEFT OUTER JOIN Table2 AS tbl2
ON tbl1.dob = tbl2.dob
AND tbl1.FName = tbl2.first
AND tbl1.LName = tbl2.last
WHERE LTRIM(RTRIM(tbl1.usermasterid)) = ''
AND LTRIM(RTRIM(tbl1.first)) <> ''
AND LTRIM(RTRIM(tbl1.last)) <> ''
AND LTRIM(RTRIM(tbl1.dob)) <> ''
GROUP BY tbl1.FName,tbl1.LName,tbl1.dob
Some data after I tested query above:
First Last DOB Count
John Cook 2008-07-11 2
Kate Witt 2013-06-05 1
Deb Ruis 2016-01-22 1
Mike Bennet 2007-01-15 1
Kristy Cruz 1997-10-20 1
Colin Jones 2011-10-13 1
Kevin Smith 2010-02-24 1
Corey Bruce 2008-04-11 1
Shawn Maiers 2016-08-28 1
Alenn Fitchner 1998-05-17 1
If anyone have idea how I can prevent/skip updating duplicate records or how to improve this query please let me know. Thank you.
You could check for and avoid duplicate matches using with common_table_expression (Transact-SQL)
along with row_number()., like so:
with cte as (
select
t.fname
, t.lname
, t.dob
, t.usermasterid
, NewUserMasterId = t2.usermasterid
, rn = row_number() over (partition by t.fname, t.lname, t.dob order by t2.usermasterid)
from table1 as t
inner join table2 as t2 on t.dob = t2.dob
and t.fname = t2.fname
and t.lname = t2.lname
and ltrim(rtrim(t.usermasterid)) = ''
)
--/* confirm these are the rows you want updated
select *
from cte as t
where t.NewUserMasterId != ''
and not exists (
select 1
from cte as i
where t.dob = i.dob
and t.fname = i.fname
and t.lname = i.lname
and i.rn>1
);
--*/
/* update those where only 1 usermasterid matches this record
update t
set t.usermasterid = t.NewUserMasterId
from cte as t
where t.NewUserMasterId != ''
and not exists (
select 1
from cte as i
where t.dob = i.dob
and t.fname = i.fname
and t.lname = i.lname
and i.rn>1
);
--*/
I use the cte to extract out the sub query for readability. Per the documentation, a common table expression (cte):
Specifies a temporary named result set, known as a common table expression (CTE). This is derived from a simple query and defined within the execution scope of a single SELECT, INSERT, UPDATE, or DELETE statement.
Using row_number() to assign a number for each row, starting at 1 for each partition of t.fname, t.lname, t.dob. Having those numbered allows us to check for the existence of duplicates with the not exists() clause with ... and i.rn>1
You could use a CTE to filter out the duplicates from Table1 before joining:
; with CTE as (select *
, count(ID) over (partition by LastName, FirstName, DoB) as IDs
from Table1)
update a
set a.ID = b.ID
from Table2 a
left join CTE b
on a.FirstName = b.FirstName
and a.LastName = b.LastName
and a.Dob = b.Dob
and b.IDs = 1
This will work provided there are no exact duplicates (same demographics and same ID) in table 1. If there are exact duplicates, they will also be excluded from the join, but you can filter them out before the CTE to avoid this.
Please try below SQL:
UPDATE Table1 AS tbl1
INNER JOIN Table2 AS tbl2
ON tbl1.dob = tbl2.dob
AND tbl1.fname = tbl2.fname
AND tbl1.lname = tbl2.lname
LEFT JOIN Table2 AS tbl3
ON tbl3.dob = tbl2.dob
AND tbl3.fname = tbl2.fname
AND tbl3.lname = tbl2.lname
AND tbl3.usermasterid <> tbl2.usermasterid
SET tbl1.usermasterid = tbl2.usermasterid
WHERE LTRIM(RTRIM(tbl1.usermasterid)) = ''
AND tbl3.usermasterid is null

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