Get most recent salary for the Employee - sql

I have a Employee table which has Empl_id,Hourly rate and effective date.
I want to get the Hourly rate of the employee for a give month if it exists if not give me the most recent Hourly rate for that employee.
select EMPL_ID, HRLY_AMT
from Employee a
where exists (select 1
from Employee b
where b.EMPL_ID = a.EMPL_ID and
b.EFFECT_DT between '2019-10-01' and '2019-10-31'
)
group by EMPL_ID
Data Sample
Empl ID HOUR_AMT EFFECT_DT
1 10 2017-07-01
1 20 2018-10-01
1 40 2019-10-01
2 40 2017-06-01
2 45 2018-09-01
2 60 2019-09-01
Now If I pass Month = 07 & Year = 2017
It should show
Empl ID HOUR_AMT EFFECT_DT
1 10 2017-07-01
2 40 2017-12-01
Now If I pass Month = 11 & Year = 2019
Empl ID HOUR_AMT EFFECT_DT
1 40 2019-10-01
2 60 2019-09-01

You could use exists, but I prefer INNER JOINs. The inner join will remove all results except the one that falls immediately after your #GivenMonth.
SELECT EMPL_ID, HRLY_AMT
FROM Employee AS a
INNER JOIN (
SELECT EMPL_ID, MIN(EFFECT_DT) AS EFFECT_DT
FROM Employee
WHERE EFFECT_DT >= #GivenMonth
GROUP BY EMPL_ID
) AS r ON r.EMPL_ID = a.EMPL_ID AND r.EFFECT_DT = a.EFFECT_DT

Try Following:
For Recent Hourly rate of the employee
select EMPL_ID, HRLY_AMT
from (select ROW_NUMBER () OVER (PARTITION BY EMPL_ID ORDER BY EFFECT_DT AS DESC) RecentRN
from Employee b
where b.EMPL_ID = a.EMPL_ID and
b.EFFECT_DT between '2019-10-01' and '2019-10-31'
)
where RecentRN = 1
For Highest Hourly rate of the employee
select EMPL_ID, HRLY_AMT
from (select ROW_NUMBER () OVER (PARTITION BY EMPL_ID ORDER BY HRLY_AMT AS DESC) HighRate
from Employee b
where b.EMPL_ID = a.EMPL_ID and
b.EFFECT_DT between '2019-10-01' and '2019-10-31'
)
where HighRate = 1

if you have entry of two values "month and Year" your query should be
SELECT Empl_ID ,
HOUR_AMT ,
EFFECT_DT
FROM (
SELECT Empl_ID ,
HOUR_AMT ,
EFFECT_DT ,
A.CurDiff,
ROW_NUMBER() OVER ( PARTITION BY A.Empl_ID ORDER BY ABS(A.CurDiff) ) Row_Id
FROM ( SELECT Empl_ID ,
HOUR_AMT ,
EFFECT_DT ,
DATEDIFF(DAY, CONVERT(DATE, #Year + '/' + #Month + '/01'), EFFECT_DT) CurDiff
FROM #tbl
) A
) Final
WHERE Final.Row_Id = 1
To view the demo for the output of this query Click Here

Related

Overlapping between first record enddate and next record start date in SQL Server

I have the below kind of data and I need below kind of output.
Input:
id startdate enddate
1 21/01/2019 23/01/2019
1 23/01/2019 24/01/2019
1 24/01/2029 27/01/2019
1 29/01/2019 02/02/2019
Output:
id startdate enddate
1 21/01/2019 27/01/2019
1 29/01/2019 02/02/2019
We need to use the logic of matching the first record enddate and nth record startdate.
This is a gaps-and-islands problem, where you want to group together "adjacent" dates. Here is one approach using window functions: the idea is to compare the current start date to the end date of the "previous" row, and use a window sum to define the groups:
select id, min(startdate) startdate, max(enddate) enddate
from (
select t.*,
sum(case when startdate = lag_enddate then 0 else 1 end) over(partition by id order by startdate) grp
from (
select t.*,
lag(enddate) over(partition by id order by startdate) lag_enddate
from mytable t
) t
) t
group by id, grp
Demo on DB Fiddle - with credits to Sander for creating the DDL statements in the first place:
id | startdate | enddate
-: | :--------- | :---------
1 | 2019-01-21 | 2019-01-27
1 | 2019-01-29 | 2019-02-02
have a look at
NEXT VALUE FOR method, works 2016 and later
Use a CTE or subquery (works in 2008) where you join on your own table using the previous value as a join. Here a sample script I use showing backup growth
declare #backupType char(1)
, #DatabaseName sysname
set #DatabaseName = db_name() --> Name of current database, null for all databaseson server
set #backupType ='D' /* valid options are:
D = Database
I = Database Differential
L = Log
F = File or Filegroup
G = File Differential
P = Partial
Q = Partial Differential
*/
select backup_start_date
, backup_finish_date
, DurationSec
, database_name,backup_size
, PreviouseBackupSize
, backup_size-PreviouseBackupSize as growth
,KbSec= format(KbSec,'N2')
FROM (
select backup_start_date
, backup_finish_date
, datediff(second,backup_start_date,b.backup_finish_date) as DurationSec
, b.database_name
, b.backup_size/1024./1024. as backup_size
,case when datediff(second,backup_start_date,b.backup_finish_date) >0
then ( b.backup_size/1024.)/datediff(second,backup_start_date,b.backup_finish_date)
else 0 end as KbSec
-- , b.compressed_backup_size
, (
select top (1) p.backup_size/1024./1024.
from msdb.dbo.backupset p
where p.database_name = b.database_name
and p.database_backup_lsn< b.database_backup_lsn
and type=#backupType
order by p.database_backup_lsn desc
) as PreviouseBackupSize
from msdb.dbo.backupset as b
where #DatabaseName IS NULL OR database_name =#DatabaseName
and type=#backupType
)as A
order by backup_start_date desc
using a "cursor local fast_forward" to loop over the data on a row-by-row and use a temporary table where you store & compaire prev value
Here is a solution with common table expressions that could work.
Sample data
create table data
(
id int,
startdate date,
enddate date
);
insert into data (id, startdate, enddate) values
(1, '2019-01-21', '2019-01-23'),
(1, '2019-01-23', '2019-01-24'),
(1, '2019-01-24', '2019-01-27'),
(1, '2019-01-29', '2019-02-02');
Solution
-- determine start dates
with cte_start as
(
select s.id,
s.startdate
from data s
where not exists ( select 'x'
from data e
where e.id = s.id
and e.enddate = s.startdate )
),
-- determine date boundaries
cte_startnext as
(
select s.id,
s.startdate,
lead(s.startdate) over (partition by s.id order by s.startdate) as startdate_next
from cte_start s
)
-- determine periods
select sn.id,
sn.startdate,
e.enddate
from cte_startnext sn
cross apply ( select top 1 e.enddate
from data e
where e.id = sn.id
and e.startdate >= sn.startdate
and (e.startdate < sn.startdate_next or sn.startdate_next is null)
order by e.enddate desc ) e
order by sn.id,
sn.startdate;
Result
id startdate enddate
-- ---------- ----------
1 2019-01-21 2019-01-27
1 2019-01-29 2019-02-02
Fiddle to see build up of solution and intermediate CTE results.

Oracle SQL calculate average/opening/closing balances from discrete data

I have account balances like this
acc_no balance balance_date
account1 5000 2020-01-01
account1 6000 2020-01-05
account2 3000 2020-01-01
account1 3500 2020-01-08
account2 7500 2020-01-15
the effective balance for any day without a balance entry is equal to to the last balance. eg account1 balance on 2,3,4 Jan is 5000 etc.
I would like to produce the daily average, opening and closing balance from this data for any period. I came up with the following query and it works but it takes half an hour when I run it against the full data set. Is my approach correct or there is a more efficient method?
WITH cte_period
AS (
SELECT '2020-01-01' date_from
,'2020-01-31' date_to
FROM dual
)
,cte_calendar
AS (
SELECT rownum
,(
SELECT to_date(date_from, 'YYYY-MM-DD')
FROM cte_period
) + rownum - 1 AS balance_day
FROM dual connect BY rownum <= (
SELECT to_date(date_to, 'YYYY-MM-DD')
FROM cte_period
) - (
SELECT to_date(date_from, 'YYYY-MM-DD')
FROM cte_period
) + 1
)
,cte_balances
AS (
SELECT 'account1' acc_no
,5000 balance
,to_date('2020-01-01', 'YYYY-MM-DD') sys_date
FROM dual
UNION ALL
SELECT 'account1'
,6000
,to_date('2020-01-05', 'YYYY-MM-DD')
FROM dual
UNION ALL
SELECT 'account2'
,3000
,to_date('2020-01-01', 'YYYY-MM-DD')
FROM dual
UNION ALL
SELECT 'account1'
,3500
,to_date('2020-01-08', 'YYYY-MM-DD')
FROM dual
UNION ALL
SELECT 'account2'
,7500
,to_date('2020-01-15', 'YYYY-MM-DD')
FROM dual
)
,cte_accounts
AS (
SELECT DISTINCT acc_no
FROM cte_balances
)
SELECT t.acc_no
,(
SELECT eff_bal
FROM (
SELECT cal.balance_day
,acc_nos.acc_no
,(
SELECT balance
FROM cte_balances bal
WHERE bal.sys_date <= cal.balance_day
AND acc_nos.acc_no = bal.acc_no
ORDER BY bal.sys_date DESC FETCH first 1 row ONLY
) eff_bal
FROM cte_calendar cal
CROSS JOIN cte_accounts acc_nos
) t1
WHERE balance_day = (
SELECT to_date(date_from, 'YYYY-MM-DD')
FROM cte_period
)
AND t.acc_no = t1.acc_no
) opening_bal
,(
SELECT eff_bal
FROM (
SELECT cal.balance_day
,acc_nos.acc_no
,(
SELECT balance
FROM cte_balances bal
WHERE bal.sys_date <= cal.balance_day
AND acc_nos.acc_no = bal.acc_no
ORDER BY bal.sys_date DESC FETCH first 1 row ONLY
) eff_bal
FROM cte_calendar cal
CROSS JOIN cte_accounts acc_nos
) t1
WHERE balance_day = (
SELECT to_date(date_to, 'YYYY-MM-DD')
FROM cte_period
)
AND t.acc_no = t1.acc_no
) closing_bal
,round(avg(eff_bal), 2) avg_bal
FROM (
SELECT cal.balance_day
,acc_nos.acc_no
,(
SELECT balance
FROM cte_balances bal
WHERE bal.sys_date <= cal.balance_day
AND acc_nos.acc_no = bal.acc_no
ORDER BY bal.sys_date DESC FETCH first 1 row ONLY
) eff_bal
FROM cte_calendar cal
CROSS JOIN cte_accounts acc_nos
) t
GROUP BY acc_no
order by acc_no
The expected result
ACC_NO OPENING_BAL CLOSING_BAL AVG_BAL
account1 5000 3500 3935.48
account2 3000 7500 5467.74
Yes. You are unnecesary selecting from same table many times. Produce calendar as you did, join with your data partitioned by account and use analytic functions for computations:
select acc_no, round(avg(bal), 2) av_bal,
max(bal) keep (dense_rank first order by day) op_bal,
max(bal) keep (dense_rank last order by day) cl_bal
from (
select acc_no, day,
nvl(balance, lag(balance) ignore nulls over (partition by acc_no order by day)) bal
from (
select date_from + level - 1 as day
from (select date '2020-01-01' date_from, date '2020-01-31' date_to from dual)
connect by date_from + level - 1 <= date_to)
left join cte_balances partition by (acc_no) on day = sys_date)
group by acc_no
dbfiddle
Edit:
sometimes the first day of the month has no balance entry, it should
take form the last available
We have to treat first row in special way. It's done in subquery data, where in case of first row and null balance I run correlated subquery which looks for balance from max previous date.
with
cte_calendar as (
select level lvl, date_from + level - 1 as day
from (select date '2020-01-01' date_from, date '2020-01-31' date_to from dual)
connect by date_from + level - 1 <= date_to),
data as (
select lvl, day, acc_no,
case when balance is null and lvl = 1
then (select max(balance) keep (dense_rank last order by sys_date)
from cte_balances a
where a.acc_no = b.acc_no and a.sys_date <= day)
else balance
end bal
from cte_calendar
left join cte_balances b partition by (acc_no) on day = sys_date)
select acc_no,
max(bal) keep (dense_rank first order by day) op_bal,
max(bal) keep (dense_rank last order by day) cl_bal,
round(avg(bal), 2)
from (
select acc_no, day,
nvl(bal, lag(bal) ignore nulls over (partition by acc_no order by day)) bal
from data)
group by acc_no
dbfiddle
although I don't understand it yet
There are thre things, which are not obvoius here and you should know to understand query:
partitioned outer join. It's main part of the solution which produces whole period for each account. You can read about them here for instance,
lag() ignore nulls - fills null balance values, take them from previous not null,
max(bal) keep (dense_rank first order by day) takes balance value from first date for opening balance. last - from last row for closing balance.
If you can afford using first_value, last_value analytic functions, then this, based on my understanding of your description, may help:
with data as (
select 'account1' as acc, 5000 as balance, to_date('2020-01-01', 'YYYY-MM-DD') as d from dual
union all select 'account1' as acc, 6000 as balance, to_date('2020-01-05', 'YYYY-MM-DD') as d from dual
union all select 'account2' as acc, 3000 as balance, to_date('2020-01-01', 'YYYY-MM-DD') as d from dual
union all select 'account1' as acc, 3500 as balance, to_date('2020-01-08', 'YYYY-MM-DD') as d from dual
union all select 'account1' as acc, 7500 as balance, to_date('2020-01-15', 'YYYY-MM-DD') as d from dual
)
select acc, avg(balance) over (partition by acc order by balance) as average,
first_value(balance) over(partition by acc order by balance asc rows unbounded preceding) as first,
last_value(balance) over(partition by acc order by balance asc rows unbounded preceding) as last
from data
where d between to_date('2020-01-01', 'YYYY-MM-DD') and to_date('2020-01-06', 'YYYY-MM-DD')
order by acc
ACC | AVERAGE | FIRST | LAST
:------- | ------: | ----: | ---:
account1 | 5000 | 5000 | 5000
account1 | 5500 | 5000 | 6000
account2 | 3000 | 3000 | 3000
db<>fiddle here

SQL - Find if column dates include at least partially a date range

I need to create a report and I am struggling with the SQL script.
The table I want to query is a company_status_history table which has entries like the following (the ones that I can't figure out)
Table company_status_history
Columns:
| id | company_id | status_id | effective_date |
Data:
| 1 | 10 | 1 | 2016-12-30 00:00:00.000 |
| 2 | 10 | 5 | 2017-02-04 00:00:00.000 |
| 3 | 11 | 5 | 2017-06-05 00:00:00.000 |
| 4 | 11 | 1 | 2018-04-30 00:00:00.000 |
I want to answer to the question "Get all companies that have been at least for some point in status 1 inside the time period 01/01/2017 - 31/12/2017"
Above are the cases that I don't know how to handle since I need to add some logic of type :
"If this row is status 1 and it's date is before the date range check the next row if it has a date inside the date range."
"If this row is status 1 and it's date is after the date range check the row before if it has a date inside the date range."
I think this can be handled as a gaps and islands problem. Consider the following input data: (same as sample data of OP plus two additional rows)
id company_id status_id effective_date
-------------------------------------------
1 10 1 2016-12-15
2 10 1 2016-12-30
3 10 5 2017-02-04
4 10 4 2017-02-08
5 11 5 2017-06-05
6 11 1 2018-04-30
You can use the following query:
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
ORDER BY company_id, effective_date
to get:
id company_id status_id effective_date grp
-----------------------------------------------
1 10 1 2016-12-15 0
2 10 1 2016-12-30 1
3 10 5 2017-02-04 2
4 10 4 2017-02-08 2
5 11 5 2017-06-05 0
6 11 1 2018-04-30 0
Now you can identify status = 1 islands using:
;WITH CTE AS
(
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
)
SELECT id, company_id, status_id, effective_date,
ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) -
cnt AS grp
FROM CTE
Output:
id company_id status_id effective_date grp
-----------------------------------------------
1 10 1 2016-12-15 1
2 10 1 2016-12-30 1
3 10 5 2017-02-04 1
4 10 4 2017-02-08 2
5 11 5 2017-06-05 1
6 11 1 2018-04-30 2
Calculated field grp will help us identify those islands:
;WITH CTE AS
(
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
), CTE2 AS
(
SELECT id, company_id, status_id, effective_date,
ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) -
cnt AS grp
FROM CTE
)
SELECT company_id,
MIN(effective_date) AS start_date,
CASE
WHEN COUNT(*) > 1 THEN DATEADD(DAY, -1, MAX(effective_date))
ELSE MIN(effective_date)
END AS end_date
FROM CTE2
GROUP BY company_id, grp
HAVING COUNT(CASE WHEN status_id = 1 THEN 1 END) > 0
Output:
company_id start_date end_date
-----------------------------------
10 2016-12-15 2017-02-03
11 2018-04-30 2018-04-30
All you want know is those records from above that overlap with the specified interval.
Demo here with somewhat more complicated use case.
Maybe this is what you are looking for? For these kind of questions, you need to join two instance of your table, in this case I am just joining with next record by Id, which probably is not totally correct. To do it better, you can create a new Id using a windowed function like row_number, ordering the table by your requirement criteria
If this row is status 1 and it's date is before the date range check
the next row if it has a date inside the date range
declare #range_st date = '2017-01-01'
declare #range_en date = '2017-12-31'
select
case
when csh1.status_id=1 and csh1.effective_date<#range_st
then
case
when csh2.effective_date between #range_st and #range_en then true
else false
end
else NULL
end
from company_status_history csh1
left join company_status_history csh2
on csh1.id=csh2.id+1
Implementing second criteria:
"If this row is status 1 and it's date is after the date range check
the row before if it has a date inside the date range."
declare #range_st date = '2017-01-01'
declare #range_en date = '2017-12-31'
select
case
when csh1.status_id=1 and csh1.effective_date<#range_st
then
case
when csh2.effective_date between #range_st and #range_en then true
else false
end
when csh1.status_id=1 and csh1.effective_date>#range_en
then
case
when csh3.effective_date between #range_st and #range_en then true
else false
end
else null -- ¿?
end
from company_status_history csh1
left join company_status_history csh2
on csh1.id=csh2.id+1
left join company_status_history csh3
on csh1.id=csh3.id-1
I would suggest the use of a cte and the window functions ROW_NUMBER. With this you can find the desired records. An example:
DECLARE #t TABLE(
id INT
,company_id INT
,status_id INT
,effective_date DATETIME
)
INSERT INTO #t VALUES
(1, 10, 1, '2016-12-30 00:00:00.000')
,(2, 10, 5, '2017-02-04 00:00:00.000')
,(3, 11, 5, '2017-06-05 00:00:00.000')
,(4, 11, 1, '2018-04-30 00:00:00.000')
DECLARE #StartDate DATETIME = '2017-01-01';
DECLARE #EndDate DATETIME = '2017-12-31';
WITH cte AS(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) AS rn
FROM #t
),
cteLeadLag AS(
SELECT c.*, ISNULL(c2.effective_date, c.effective_date) LagEffective, ISNULL(c3.effective_date, c.effective_date)LeadEffective
FROM cte c
LEFT JOIN cte c2 ON c2.company_id = c.company_id AND c2.rn = c.rn-1
LEFT JOIN cte c3 ON c3.company_id = c.company_id AND c3.rn = c.rn+1
)
SELECT 'Included' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date BETWEEN #StartDate AND #EndDate
UNION ALL
SELECT 'Following' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date > #EndDate
AND LagEffective BETWEEN #StartDate AND #EndDate
UNION ALL
SELECT 'Trailing' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date < #EndDate
AND LeadEffective BETWEEN #StartDate AND #EndDate
I first select all records with their leading and lagging Dates and then I perform your checks on the inclusion in the desired timespan.
Try with this, self-explanatory. Responds to this part of your question:
I want to answer to the question "Get all companies that have been at
least for some point in status 1 inside the time period 01/01/2017 -
31/12/2017"
Case that you want to find those id's that have been in any moment in status 1 and have records in the period requested:
SELECT *
FROM company_status_history
WHERE id IN
( SELECT Id
FROM company_status_history
WHERE status_id=1 )
AND effective_date BETWEEN '2017-01-01' AND '2017-12-31'
Case that you want to find id's in status 1 and inside the period:
SELECT *
FROM company_status_history
WHERE status_id=1
AND effective_date BETWEEN '2017-01-01' AND '2017-12-31'

SQL Join two tables by unrelated date

I’m looking to join two tables that do not have a common data point, but common value (date). I want a table that lists the date and total number of hired/terminated employees on that day. Example is below:
Table 1
Hire Date Employee Number Employee Name
--------------------------------------------
5/5/2018 10078 Joe
5/5/2018 10077 Adam
5/5/2018 10078 Steve
5/8/2018 10079 Jane
5/8/2018 10080 Mary
Table 2
Termination Date Employee Number Employee Name
----------------------------------------------------
5/5/2018 10010 Tony
5/6/2018 10025 Jonathan
5/6/2018 10035 Mark
5/8/2018 10052 Chris
5/9/2018 10037 Sam
Desired result:
Date Total Hired Total Terminated
--------------------------------------
5/5/2018 3 1
5/6/2018 0 2
5/7/2018 0 0
5/8/2018 2 1
5/9/2018 0 1
Getting the total count is easy, just unsure as the best approach from the standpoint of "adding" a date column
If you need all dates within some window then you need to join the data to a calendar. You can then left join and sum flags for data points.
DECLARE #StartDate DATETIME = (SELECT MIN(ActionDate) FROM(SELECT ActionDate = MIN(HireDate) FROM Table1 UNION SELECT ActionDate = MIN(TerminationDate) FROM Table2)AS X)
DECLARE #EndDate DATETIME = (SELECT MAX(ActionDate) FROM(SELECT ActionDate = MAX(HireDate) FROM Table1 UNION SELECT ActionDate = MAX(TerminationDate) FROM Table2)AS X)
;WITH AllDates AS
(
SELECT CalendarDate=#StartDate
UNION ALL
SELECT DATEADD(DAY, 1, CalendarDate)
FROM AllDates
WHERE DATEADD(DAY, 1, CalendarDate) <= #EndDate
)
SELECT
CalendarDate,
TotalHired = SUM(CASE WHEN H.HireDate IS NULL THEN NULL ELSE 1 END),
TotalTerminated = SUM(CASE WHEN T.TerminationDate IS NULL THEN NULL ELSE 1 END)
FROM
AllDates D
LEFT OUTER JOIN Table1 H ON H.HireDate = D.CalendarDate
LEFT OUTER JOIN Table2 T ON T.TerminationDate = D.CalendarDate
/* If you only want dates with data points then uncomment out the where clause
WHERE
NOT (H.HireDate IS NULL AND T.TerminationDate IS NULL)
*/
GROUP BY
CalendarDate
I would do this with a union all and aggregations:
select dte, sum(is_hired) as num_hired, sum(is_termed) as num_termed
from (select hiredate as dte, 1 as is_hired, 0 as is_termed from table1
union all
select terminationdate, 0 as is_hired, 1 as is_termed from table2
) ht
group by dte
order by dte;
This does not include the "missing" dates. If you want those, a calendar or recursive CTE works. For instance:
with ht as (
select dte, sum(is_hired) as num_hired, sum(is_termed) as num_termed
from (select hiredate as dte, 1 as is_hired, 0 as is_termed from table1
union all
select terminationdate, 0 as is_hired, 1 as is_termed from table2
) ht
group by dte
),
d as (
select min(dte) as dte, max(dte) as max_dte)
from ht
union all
select dateadd(day, 1, dte), max_dte
from d
where dte < max_dte
)
select d.dte, coalesce(ht.num_hired, 0) as num_hired, coalesce(ht.num_termed) as num_termed
from d left join
ht
on d.dte = ht.dte
order by dte;
Try this one
SELECT ISNULL(a.THE_DATE, b.THE_DATE) as Date,
ISNULL(a.Total_Hire,0) as Total_Hire,
ISNULL (b.Total_Terminate,0) as Total_terminate
FROM (SELECT Hire_date as the_date, COUNT(1) as Total_Hire
FROM TABLE_HIRE GROUP BY HIRE_DATE) a
FULL OUTER JOIN (SELECT Termination_Date as the_date, COUNT(1) as Total_Terminate
FROM TABLE_TERMINATE GROUP BY HIRE_DATE) a
ON a.the_date = b.the_date

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