query to retrieve changes in multiple columns in sql - sql

I have the below table with the assignments details such as location, org, job, grade etc.
I want to build a query such that changes in the location, org are fetched for all system_person_type = 'EMP' only.
per_assignments
Person_id locat_id org_id job_id grade_id system_person_type START_DT END_DT
1 Toronto XYZ 1 GR1 EMP 01-JAN-2019 20-JAN-2019
1 US XYZ 1 GR1 EMP 21-JAN-2019 31-DEC-4712
2 Chicago ABC 2 GR1 EX-EMP 01-jul-2017 30-Nov-2017
2 Toronto XYZ 3 GR2 EMP 01-JAN-2019 03-JUL-2019
2 India GFH 3 GR2 EMP 04-JUL-2019 08-SEP-2019
2 India GFH 4 GR2 EMP 09-SEP-2019 31-DEC-4712
so in the above example the output should be :
person_id old_locat_id new_locat_id old_org_id new_org_id old_start_dt new_start_dat
1 Toronto US - - 01-jan-2019 21-jan-2019
2 Toronto India XYZ GFH 01-JAN-2019 04-JUL-2019
I created the below query But from the below query I am getting old_start_dt> new_start_dt and I am not getting all the changes required,
only 1 column change is retrieving. How can the below query be changed to accomadtae the above requirement ?
SELECT DISTINCT paam_change_loc.person_id ,
to_char(paam_change1.start_date,'YYYY-MM-DD') AS old_effective_start_dt ,
to_char(paam_change_loc.start_date,'YYYY-MM-DD') AS new_effective_start_dt ,
paam_change1.location_id AS old_loc_value ,
paam_change_loc.location_id AS new_loc_value
FROM per_assignments paam_change_loc,
per_assignments paam_change1
WHERE paam_change_loc.person_id =paam_change1.person_id
AND (
paam_change_loc.location_id IS NOT NULL
AND paam_change_loc.location_id <> paam_change1.location_id )
AND paam_change_loc.system_person_type = 'EMP'
AND paam_change1.system_person_type = 'EMP'
AND to_char(to_date(paam_change_loc.start_date),'DD-MM-YYYY') BETWEEN ('05-08-2019') AND '05-12-2019'
AND (
to_char(to_date(paam_change_loc.start_date)-1,'DD-MM-YYYY') BETWEEN ('05-08-2019') AND '05-12-2019' )
'05-08-2019' and '05-12-2019' is the transfer dates which will be passed to the query and the dates are to be compared in between these two dates

This query gives expected result:
select person_id, prev_start_dt, start_dt,
case loc_new when loc_old then ' - ' else loc_old end loc_old,
case loc_new when loc_old then ' - ' else loc_new end loc_new,
case org_new when org_old then ' - ' else org_old end org_old,
case org_new when org_old then ' - ' else org_new end org_new
from (
select person_id, locat_id loc_new, org_id org_new, start_dt,
lag(locat_id) over (partition by person_id order by start_dt) loc_old,
lag(org_id) over (partition by person_id order by start_dt) org_old,
lag(start_dt) over (partition by person_id order by start_dt) prev_start_dt,
case start_dt when 1 + lag(end_dt) over (partition by person_id order by start_dt)
then 1 end flag
from per_assignments)
where flag = 1 and (loc_new <> loc_old or org_new <> org_old)
dbfiddle
In the inner query apply filters for system_person_type and dates as needed. At first I used lag() three times and also to mark continuous rows, in column flag. Then only flagged rows are shown where location or organization changed.

I am not sure about data structure of your db. Considering the sample data as table, you can achieve the expected outptut using analytical function:
Select person_id,
Locat_id as old_locat_id,
New_locat_id,
org_id as old_org_id,
New_org_id,
Start_date as old_start_date,
New_start_date
From
(Select t.*,
Lead(org_id) over (partition by person_id order by start_date) as new_org_id,
Lead(start_date) over (partition by person_id order by start_date) as new_start_date,
Lead(locat_id) over (partition by person_id order by start_date) as new_locat_id,
From your_table t where system_person_type = 'EMP')
Where locat_id <> new_locat_id or org_id <> new_org_id;
Cheers!!

Related

Creating a new RANK based on delta of previous row

I've been working on an issue for a few days now, and I can't seem to find the right fix. Does anybody have an idea?
Case
We want to create a new a new sequence number whenever an employee has resigned for more than 1 day. We have the delta of the current employment record and the previous, so we can check the sequence. We want to calculate the min(Start) and max(End) of each employment record which isn't separated more than 1 day apart.
Data
Employee
Contract
Unit
Start
End
Delta
John Doe
1
Unit A
2014-01-01
2017-12-31
NULL
John Doe
2
Unit A
2018-02-01
2018-12-31
31
John Doe
3
Unit B
2019-01-01
2020-05-31
1
John Doe
4
Unit A
2020-06-01
NULL
1
With the query it should give back:
Employee
Contract
Unit
Start
End
Delta
Sequence
John Doe
1
Unit A
2014-01-01
2017-12-31
NULL
1
John Doe
2
Unit A
2018-02-01
2018-12-31
31
2
John Doe
3
Unit B
2019-01-01
2020-05-31
1
2
John Doe
4
Unit A
2020-06-01
NULL
1
2
That is because sequence 1 end at 31-12-2017, and a new one starts in February of 2018, so there has been more than 1 day of separation between the records. The following all have a sequence of 2 because it is continuing.
Query
I've tried a few things already with lag() and lead(), but I keep working myself into a corner with the data sample that I have. When I run it on the full set it won't work.
SELECT
Employee,
Start,
End,
DeltaPrevious,
Delta,
DeltaNext,
case
when DeltaPrevious IS NULL AND Delta = 1 then 1
when DeltaPrevious = 1 AND Delta > 1 then min(Contract) OVER (PARTITION BY Employee ORDER BY Contract ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
when DeltaPrevious > 1 AND Delta = 1 then min(Contract) OVER (PARTITION BY Employee ORDER BY Contract ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)
end as Sequence
FROM
Contracts
ORDER BY
Employee, Start ASC
Hope that someone has a great idea.
Thanks,
Basically, you want to use lag() to get the previous date and then do a cumulative sum. This looks like:
select c.*,
sum(case when prev_end >= dateadd(day, -1, start) then 0 else 1
end) over (partition by employee order by start) as ranking
from (select c.*,
lag(end) over (partition by employee order by start) as prev_end
from contracts c
) c;
You mention that you might want to recalculate the new start and end. You would just use the above as a subquery/CTE and aggregate on employee and ranking.
If I understood correctly from the definition of Sequence in your second table, you are more interested in the DeltaNext than in the Delta(Previous). Here an attempt, including the code to create a sample input date with two more employees:
CREATE TABLE #input_table (Employee VARCHAR(255), [Contract] INT, Unit VARCHAR(6), [Start] DATE, [End] DATE)
INSERT INTO #input_table
VALUES
('John Doe', 1, 'Unit A', '2014-01-01', '2017-12-31'),
('John Doe', 2, 'Unit A', '2018-02-01', '2018-12-31'),
('John Doe', 3, 'Unit B', '2019-01-01', '2020-05-31'),
('John Doe', 4, 'Unit A', '2020-06-01', NULL),
('Alice', 1, 'Unit A', '2020-01-01', NULL),
('Bob', 1, 'Unit C', '2020-01-01', '2020-02-20')
First we create the deltas:
SELECT *
, DeltaPrev = DATEDIFF(DAY, LAG([End], 1, NULL) OVER(PARTITION BY Employee
ORDER BY [Start]), [Start]) -- Not relevant (?)
, DeltaNext = DATEDIFF(DAY, [End], LEAD([Start], 1, NULL) OVER(PARTITION BY Employee ORDER BY [Start]))
INTO #cte_delta -- I'll create a CTE at the end
FROM #input_table
Then we define Sequence:
SELECT *
, [Sequence] = CASE WHEN DeltaNext > 1 THEN 1 ELSE 2 END
INTO #cte_sequence
FROM #cte_delta
We then group the same Sequences by assigning a unique ROW_NUMBER for each employee with consecutive/ same Sequences:
SELECT *
, GRP = ROW_NUMBER() OVER(PARTITION BY Employee ORDER BY [Start]) - ROW_NUMBER() OVER(PARTITION BY Employee, [Sequence] ORDER BY [Start])
INTO #cte_grp
FROM #cte_sequence
Finally we calculate the min and max of the contract duration:
SELECT *
, MIN([Start]) OVER(PARTITION BY Employee, GRP) AS ContractStart
, CASE WHEN COUNT(*) OVER(PARTITION BY Employee, GRP) = COUNT([End])
OVER(PARTITION BY Employee, GRP) THEN MAX([End]) OVER(PARTITION BY Employee, GRP) ELSE NULL END AS ContractEnd
FROM cte_grp
The COUNT(*) and COUNT([End]) comparison is necessary or else the ContractEnd would be the max non-NULL value, i.e. 2018-02-01.
The whole code with CTEs here:
WITH cte_delta AS (
SELECT *
, DeltaPrev = DATEDIFF(DAY, LAG([End], 1, NULL) OVER(PARTITION BY Employee ORDER BY [Start]), [Start]) -- Not relevant (?)
, DeltaNext = DATEDIFF(DAY, [End], LEAD([Start], 1, NULL) OVER(PARTITION BY Employee ORDER BY [Start]))
FROM #input_table
)
, cte_sequence AS (
SELECT *
, [Sequence] = CASE WHEN DeltaNext > 1 THEN 1 ELSE 2 END
FROM cte_delta
)
, cte_grp AS (
SELECT *
, GRP = ROW_NUMBER() OVER(PARTITION BY Employee ORDER BY [Start]) - ROW_NUMBER() OVER(PARTITION BY Employee, [Sequence] ORDER BY [Start])
FROM cte_sequence
)
SELECT *
, MIN([Start]) OVER(PARTITION BY Employee, GRP) AS ContractStart
, CASE WHEN COUNT(*) OVER(PARTITION BY Employee, GRP) = COUNT([End]) OVER(PARTITION BY Employee, GRP) THEN MAX([End]) OVER(PARTITION BY Employee, GRP) ELSE NULL END AS ContractEnd
FROM cte_grp
Here the output:
Employee
Contract
Unit
Start
End
DeltaPrev
DeltaNext
Sequence
GRP
ContractStart
ContractEnd
Alice
1
Unit A
2020-01-01
NULL
NULL
NULL
2
0
2020-01-01
NULL
Bob
1
Unit C
2020-01-01
2020-02-20
NULL
NULL
2
0
2020-01-01
2020-02-20
John Doe
1
Unit A
2014-01-01
2017-12-31
NULL
32
1
0
2014-01-01
2017-12-31
John Doe
2
Unit A
2018-02-01
2018-12-31
32
1
2
1
2018-02-01
NULL
John Doe
3
Unit B
2019-01-01
2020-05-31
1
1
2
1
2018-02-01
NULL
John Doe
4
Unit A
2020-06-01
NULL
1
NULL
2
1
2018-02-01
NULL
Feel free to select DISTINCT records according to your needs.

SQL query to get previous and new column detail from a table

I have a table that has both audit data for addresses-
ADDRESS_ID EFFECTIVE_START_DATE EFFECTIVE_END_DATE ADDRESS_LINE_1 ADDRESS_LINE_2 TOWN Person_id
300000009360612 2020-04-01 4712-12-31 Box 14 ADAM SELLERS ST Mirror 157
300000009360612 2000-03-27 2020-03-31 Box 13 Mirror 157
I want the previous value and current value for the columns that change. For example, the output should look like -
ADDRESS_ID Changed Attribute Previous_Value Current_Value
300000009360612 Effective_start_Date 2000-03-27 2020-04-01
300000009360612 Effective_end_Date 2020-03-31 4712-12-31
300000009360612 ADDRESS_LINE_1 Box 13 Box 14
300000009360612 ADDRESS_LINE_2 ADAM SELLERS ST
I have tried -
select * from (
SELECT
address_id ,
'Effective_Start_Date' Changed_Attribute ,
cast(Effective_Start_Date as varchar(40)) current_value
, LAG(cast(effective_start_date as varchar(40)), 1, 0) OVER (partition by address_ID, effective_start_date ORDER BY last_update_date) Previous_Value
FROM fusion.per_addresses_f_
ORDER BY last_update_date DESC)
where current_value <> Previous_Value
The above query is not giving the right output. It is giving me the below output-
address_id Changed_Attribute current_value Previous_Value
300000009360612 Effective_start_Date 2020-04-01
i.e. i am not getting any value in previous_value.
select *
from (SELECT address_id,
'Effective_Start_Date' Changed_Attribute,
to_char(Effective_Start_Date, 'yyyy-mm-dd') current_value,
to_char(LAG(t.effective_start_date, 1) over(order by t.effective_start_date desc) , 'yyyy-mm-dd') Previous_Value
FROM AUDIT_DATA t
union all
SELECT address_id,
'Effective_end_Date' Changed_Attribute,
to_char(t.effective_end_date , 'yyyy-mm-dd') current_value,
to_char(LAG(t.effective_end_date, 1) over(order by t.effective_end_date desc) , 'yyyy-mm-dd') Previous_Value
FROM AUDIT_DATA t
union all
SELECT address_id,
'ADDRESS_LINE_1' Changed_Attribute,
t.address_line_1 current_value,
LAG(t.address_line_1, 1) over(order by t.address_line_1 desc) Previous_Value
FROM AUDIT_DATA t
union all
SELECT address_id,
'ADDRESS_LINE_2' Changed_Attribute,
Nvl(t.address_line_2, ' ') current_value,
LAG(t.address_line_2, 1) over(order by t.address_line_2) Previous_Value
FROM AUDIT_DATA t
) r
where Previous_Value <> current_value;
u can use ROW_NUMBER instead of LAG =
select * from (select address_id ,
'Effective_Start_Date' Changed_Attribute ,
cast(Effective_Start_Date as varchar(40)) current_value,
ROW_NUMBER() over(partition by address_ID order by last_update_date desc) as ROW_NO from fusion.per_addresses_f_ ) Q where q.ROW_NO=1 -- first of each partiotion = Previous_Value
....
You can generate the 6 rows for each ADDRESS and compare it as follows:
WITH CTE AS
(SELECT T.*,
ROW_NUMBER() OVER (PARTITION BY ADDRESS_ID ORDER BY EFFECTIVE_START_DATE DESC) AS RN
FROM YOUR_TABLE T)
SELECT * FROM
(SELECT TNEW.ADDRESS_ID,
DECODE(LVL,1,'Effective_start_Date',2,'Effective_end_Date',3,'ADDRESS_LINE_1',4,'ADDRESS_LINE_2',5,'TOWN') AS CHANGED_aTTRIBUTE,
DECODE(LVL,1,TOLD.Effective_start_Date,2,TOLD.Effective_end_Date,3,TOLD.ADDRESS_LINE_1,4,TOLD.ADDRESS_LINE_2,5,TOLD.TOWN) AS PREV_VALUE,
DECODE(LVL,1,TNEW.Effective_start_Date,2,TNEW.Effective_end_Date,3,TNEW.ADDRESS_LINE_1,4,TNEW.ADDRESS_LINE_2,5,TNEW.TOWN) AS CURRENT_VALUE
FROM CTE TNEW
JOIN CTE TOLD ON TNEW.ADDRESS_ID = TOLD.ADDRESS_ID AND TNEW.RN = 1 AND TOLD.RN=2
cross join (SELECT LEVEL AS LVL FROM DUAL CONNECT BY LEVEL <=5) L)
WHERE PREV_VALUE <> CURRENT_VALUE
OR (PREV_VALUE IS NULL AND CURRENT_VALUE IS NOT NULL)
OR (PREV_VALUE IS NOT NULL AND CURRENT_VALUE IS NULL)
Using the last_update_date to decide which record is the first one...
/*
ADDRESS_ID EFFECTIVE_START_DATE EFFECTIVE_END_DATE ADDRESS_LINE_1 ADDRESS_LINE_2 TOWN Person_id LAST_UPDATE_DATE
300000009360612 2020-04-01 4712-12-31 Box 14 ADAM SELLERS ST Mirror 157 2002-01-01
300000009360612 2000-03-27 2020-03-31 Box 13 Mirror 157 2002-02-02
*/
With a1 as (
select
cast('300000009360612' as varchar2(200)) as address_id
, cast('2020-04-01' as varchar2(200)) as EFFECTIVE_START_DATE
, cast('4712-12-31' as varchar2(200)) as EFFECTIVE_END_DATE
, cast('Box 14' as varchar2(200)) as ADDRESS_LINE_1
, cast('ADAM SELLERS ST' as varchar2(200)) as ADDRESS_LINE_2
, cast('Mirror' as varchar2(200)) as TOWN
, cast(157 as number(10)) as PERSON_ID
, to_date('2020-01-01', 'YYYY-MM-DD') as LAST_UPDATE_DATE
from dual
union all
select
'300000009360612', '2000-03-27', '2020-03-31', 'Box 13', NULL, 'Mirror', 157, to_date('2020-02-02', 'YYYY-MM-DD')
from dual
),
Q0 as (
select
address_id
, effective_start_date, effective_end_date
, address_line_1, address_line_2
, town, person_id
, effective_start_date as current_value
, LAG(effective_start_date, 1, 0) OVER (partition by person_id, address_id ORDER BY last_update_date) Previous_Value
from a1
),
Q1 as (
select
t1.address_id as address_id,
t1.effective_start_date as effective_start_date_t1, t2.effective_start_date as effective_start_date_t2,
t1.effective_end_date as effective_end_date_t1, t2.effective_end_date as effective_end_date_t2,
t1.address_line_1 as address_line_1_t1, t2.address_line_1 as address_line_1_t2,
t1.address_line_2 as address_line_2_t1, t2.address_line_2 as address_line_2_t2,
t1.town as town_t1, t2.town as town_t2,
to_char(t1.person_id) as person_id_t1, to_char(t2.person_id) as person_id_t2
, to_char(t1.last_update_date, 'YYYY-MM-DD') as last_update_date_t1, to_char(t2.last_update_date, 'YYYY-MM-DD') as last_update_date_t2
from a1 t1 inner join a1 t2 on (t1.person_id = t2.person_id and t1.address_id = t2.address_id and t1.LAST_UPDATE_DATE < t2.LAST_UPDATE_DATE)
where 1=1
order by t1.person_id, t1.address_id, t1.last_update_date
)
select
ADDRESS_ID
, COLX
, VAL1
, VAL2
--, case when nvl(VAL1, 'NA') != nvl(VAL2,'NA') THEN 1 ELSE 0 END CHANGED
from q1
unpivot (
(val1, val2)
for colx in (
(EFFECTIVE_START_DATE_T1, EFFECTIVE_START_DATE_T2) as 'EFFECTIVE_START_DATE'
, (EFFECTIVE_END_DATE_T1, EFFECTIVE_END_DATE_T2) as 'EFFECTIVE_END_DATE'
, (ADDRESS_LINE_1_T1, ADDRESS_LINE_1_T2) as 'ADDRESS_LINE_1'
, (ADDRESS_LINE_2_T1, ADDRESS_LINE_2_T2) as 'ADDRESS_LINE_2'
, (TOWN_T1, TOWN_T2) as 'TOWN'
, (PERSON_ID_T1, PERSON_ID_T2) as 'PERSON_ID'
, (LAST_UPDATE_DATE_T1, LAST_UPDATE_DATE_T2) as 'LAST_UPDATE_DATE'
)
)
where 1=1
and case when nvl(VAL1, 'NA') != nvl(VAL2,'NA') THEN 1 ELSE 0 END != 0
;
gives
ADDRESS_ID COLX VAL1 VAL2
300000009360612 EFFECTIVE_START_DATE 2020-04-01 2000-03-27
300000009360612 EFFECTIVE_END_DATE 4712-12-31 2020-03-31
300000009360612 ADDRESS_LINE_1 Box 14 Box 13
300000009360612 ADDRESS_LINE_2 ADAM SELLERS ST
300000009360612 LAST_UPDATE_DATE 2020-01-01 2020-02-02
NOTE: VAL1 = Previous Value and VAL2 = Current Value (based on LAST_UPDATE_DATE)

Oracle SQL overlap between begin date and end date in 2 or more records

Database my_table:
id seq start_date end_date
1 1 01-01-2017 02-01-2017
1 2 07-01-2017 09-01-2017
1 3 11-01-2017 11-01-2017
2 1 20-01-2017 20-01-2017
3 1 01-02-2017 02-02-2017
3 2 03-02-2017 04-02-2017
3 3 08-01-2017 09-02-2017
3 4 09-01-2017 10-02-2017
3 5 10-01-2017 12-02-2017
My requirement is to get the first date (normally seq 1 start date) and end date (normally last seq end date) and the number of dates occurred during all seq for each unique ID.
Date occurred:
id 1 2 3
01-01-2017 20-01-2017 01-02-2017
02-01-2017 02-02-2017
07-01-2017 03-02-2017
08-01-2017 04-02-2017
09-01-2017 08-02-2017
11-01-2017 09-02-2017
10-02-2017
11-02-2017
12-02-2017
total 6 1 9
Here is the result I want:
id start_date end_date num_date
1 01-01-2017 11-01-2017 6
2 20-01-2017 20-01-2017 1
3 01-02-2017 12-02-2017 9
I have tried
SELECT id
, MIN(start_date)
, MAX(end_date)
, SUM(end_date - start_date + 1)
FROM my_table
GROUP BY id
and this SQL statement work fine in id 1 and 2 since there is no overlap date between begin date and end date. But for id 3, the result num_date is 11. Could you please suggest the SQL statement to solve this problem? Thank you.
One more question: The date in database is in datetime format. How do I convert it to date. I tried to use TRUNC function but it sometimes convert date to yesterday instead.
You need to count how many times an end_date equals the following start_date. For this you need to use the lag() or the lead() analytic function. You can use a case expression for the comparison, but alas you can't wrap the case expression within a COUNT or SUM in the same query; you need a subquery and an outer query.
Something like this; not tested, since you didn't provide CREATE TABLE and INSERT statements to recreate your sample data.
select id, min(start_date) as start_date, max(end_date) as end_date,
sum(end_date - start_date + 1 - flag) as num_days
from ( select id, start_date, end_date,
case when start_date = lag(end_date)
over (partition by id order by end_date) then 1
else 0 end as flag
from my_table
)
group by id;
SELECT id,
MIN( start_date ) AS start_date,
MAX( end_date ) AS end_date,
SUM( end_date - start_date + 1 ) AS num_days
FROM (
SELECT id,
GREATEST(
start_date,
COALESCE(
LAG( end_date ) OVER ( PARTITION BY id ORDER BY seq ) + 1,
start_date
)
) AS start_date,
end_date
FROM your_table
)
WHERE start_date <= end_date
GROUP BY id;

using min and max in group by clause

I want below output in oracle sql.
I have data in table as below :
id start_date end_date assignment number
1 2.02.2014 15.02.2014 10
2 25.02.2014 30.02.2014 20
3 26.03.2014 04.05.2014 30
4 06.06.2014 31.12.4712 10
I need output using group by
assignment_number start_date end_date
10 02.02.2014 15.02.2014
10 06.06.2014 31.12.4712
20 25.02.2014 30.02.2014
30 26.03.2014 04.05.2014
I tried using min(start_date) and max(end_date) for assignment 10 ia was getting output as
assignment_number start_date end_date
10 02.02.2014 31.12.4712
But I want as :-
assignment_number start_date end_date
10 02.02.2014 15.02.2014
10 06.06.2014 31.12.4712
Please help
I think you'd have to calculate the min and max separately, then union them. Try something like this:
SELECT
assignment_number
, start_date
, end_date
FROM
(SELECT
assignment_number
, start_date
, end_date
FROM TABLE
GROUP BY assignment_number
HAVING MIN(start_date)
UNION
SELECT
assignment_number
, start_date
, end_date
FROM TABLE
GROUP BY assignment_number
HAVING MAX(end_date)
)
ORDER BY
1 ASC
, 2 ASC
, 3 ASC
;
sql fiddle
select id, to_char(start_date,'dd.mm.yyyy') start_date, to_char(end_date,'dd.mm.yyyy') end_date,ASSIGNMENT_NUMBER from sof1 s
where not exists
(select 1 from sof1 s2
where s2.assignment_number=s.assignment_number
and s2.start_date<s.start_date
)
or not exists
(select 1 from sof1 s2
where s2.assignment_number=s.assignment_number
and s2.end_date>s.end_date
)
order by ASSIGNMENT_NUMBER
With analytic function:
sql fiddle
select id, to_char(start_date,'dd.mm.yyyy') start_date, to_char(end_date,'dd.mm.yyyy') end_date,ASSIGNMENT_NUMBER from
(select s.*
, min (start_date) over (partition by ASSIGNMENT_NUMBER) sd
, max (end_date) over (partition by ASSIGNMENT_NUMBER) ed
from sof1 s
)
where start_date=sd or end_date=ed
order by ASSIGNMENT_NUMBER, start_date

SQL server full join query

I have a full join sql query and i am retrieving the data from the same table.the problem is i am getting the null value where i am expecting the column name.
Example:
I am having a table where there are two columns typeOfPost,dob.
DOB TypeOfPost
--------- --------------
20/11/1998 Manager
1/1/2000 Sales
13/6/1999 Manager
20/1/1987 Manager
1/11/1985 Sales
Now when I am writing a join query like
select DATENAME(month,dob) as Red,count(TypeOfPost)
from tablename
where TypeOfPost='Manager'
group by DATENAME(month,dob) as A
full join
select DATENAME(month,dob) as Green,count(TypeOfPost)
from tablename
where TypeOfPost='Sales'
group by DATENAME(month,dob) as B on B.Green = A.Red
Output-- Expected Output--
--------------------- ---------------------
Month Man Sal Month Man Sal
-------- ----- ------ -------- ----- ------
January 1 1 January 1 1
NULL 1 NULL June 1 NULL
November 1 1 November 1 1
Now here the problem rise, I want 'June' in the column Month instead of NULL value.
So is there any way to get that??
Help me out.
Thanks.
One option is to
use a CASE statement in a subselect
Determine for given record if it is a manager or sales
substitute with 1 or 0 accordingly
SELECT and GROUP from this subselect the final results.
SQL Statement
SELECT Month
, SUM(Man) AS Man
, SUM(Sal) AS Sal
FROM (
SELECT DATENAME(MONTH, DOB) AS Month
, CASE WHEN TypeOfPost = 'Manager' THEN 1 ELSE 0 END AS Man
, CASE WHEN TypeOfPost = 'Sales' THEN 1 ELSE 0 END AS Sal
FROM tableName
) g
GROUP BY
Month
or
SELECT Month
, SUM(Man)
, SUM(Sal)
FROM (
SELECT DATENAME(MONTH, DOB) AS Month
, COUNT(TypeOfPost) AS Man
, 0 AS Sal
FROM tableName
WHERE TypeOfPost = 'Manager'
GROUP BY
DATENAME(MONTH, DOB)
UNION ALL
SELECT DATENAME(MONTH, DOB) AS Month
, 0 AS Man
, COUNT(TypeOfPost) AS Sal
FROM tableName
WHERE TypeOfPost = 'Sales'
GROUP BY
DATENAME(MONTH, DOB)
) g
GROUP BY
Month