Finding Members between a Date Range - sql

I need to find Membership between two dates ( 7-01-14 and 6-30-15) who have been members at least 4 months or more during that time frame.There is a START_DATE and an END_DATE column. Any advice would be appreciated. Thank you.

This will work for sqlserver 2012. If you are using an earlier version, you can replace the where clause:
DECLARE #t table(START_DATE date, END_DATE date)
INSERT #t values
('2015-01-01','2015-08-30'),('2015-01-01','2015-12-30'),
('2015-08-01','2015-12-30'),('2015-11-01','2017-12-30'),
('2016-01-01','2017-12-30'),('2017-01-01','2017-12-30')
DECLARE #from date='2015-7-01'
DECLARE #to date='2016-03-01'
SELECT *
FROM #t
WHERE
DATEADD(month,4, IIF(#from<START_DATE, START_DATE, #FROM)) <=
IIF(#to>END_DATE, END_DATE, #to)
/*
--this is for sqlserver 2008
DATEADD(month,4, CASE WHEN #from<START_DATE THEN START_DATE ELSE #FROM END) <=
CASE WHEN #to>END_DATE THEN END_DATE ELSE #to END
*/

You can use this code:
select *
from YourTable
where DateColumn between '2014/01/7' and '2006/05/30' and
start_date >= Dateadd(Month, Datediff(Month, 0, DATEADD(m, -4, current_timestamp)), 0)

Lets call your table Enrollment.
First.. we want all people who were enrolled between 7-01-14 and 6-30-15
Select *
From Enrollment
Where Start_Date Between '7-01-14' and '6-30-15'
And End_Date Between '7-01-14' and '6-30-15'
Also, the membership has to be at least found months long in that timespan.
Select *
From Enrollment
Where Start_Date Between '7-01-14' and '6-30-15'
And End_Date Between '7-01-14' and '6-30-15'
And DateDiff (month, Start_Date, End_Date) > 4
But consider these scenarios:
What happens if somebody signs up on '6-25-15' (5 days before end cutoff) and is active for 4 months?
What happens if somebody signs up on '4-01-14' (3 months before beginning cutoff) and is active for 4 months?
You will have to make appropriate changes to the query in that case to take care of these scenarios if you need them.
For example, if the Start_Date OR End_Date can be between the cutoff dates specified, but the membership still needs to be 4 months long
Select *
From Enrollment
Where
(
Start_Date Between '7-01-14' and '6-30-15'
OR End_Date Between '7-01-14' and '6-30-15'
)
And DateDiff (month, Start_Date, End_Date) >= 4
But what if the Start_Date is smaller than beginning of the cutff, and the End_Date is larger than the end of cutoff, and the membership still needs to be 4 months long
Select *
From Enrollment
Where
(
(Start_Date Between '7-01-14' and '6-30-15' OR End_Date Between '7-01-14' and '6-30-15')
OR
(Start_Date < '7-01-14' and End_Date > '6-30-15')
)
And DateDiff (month, Start_Date, End_Date) >= 4

Related

How to get all info about start date to end date within a given date range?

Recently I've been developing a leave management system. In this application I need a report like in a month wise employee leave statement.
So here's my sample table:
Employee Id application Date Start Date End Date
20130002 14-Mar-2016 16-Mar-2016 17-Mar-2016
20130012 15-Mar-2016 29-Mar-2016 2-Apr-2016
20130003 14-Mar-2016 15-Mar-2016 16-Mar-2016
20130005 10-Mar-2016 24-Mar-2016 24-Mar-2016
20130002 10-Mar-2016 20-Mar-2016 25-Mar-2016
20130006 13-Mar-2016 8-Mar-2016 17-Mar-2016
20130001 14-Mar-2016 4-Apr-2016 24-Apr-2016
20130003 15-Mar-2016 16-May-2016 18-May-2016
20130011 10-Mar-2016 7-Jun-2016 7-Jun-2016
Now I need a report where I can get month wise this report. Suppose I need only March's data, like this:
<pre>
Employee Id application Date Start Date End Date
20130002 14-Mar-2016 16-Mar-2016 17-Mar-2016
20130012 15-Mar-2016 29-Mar-2016 31-Mar-2016
20130003 14-Mar-2016 15-Mar-2016 16-Mar-2016
20130005 10-Mar-2016 24-Mar-2016 24-Mar-2016
20130002 10-Mar-2016 20-Mar-2016 25-Mar-2016
20130006 13-Mar-2016 8-Mar-2016 17-Mar-2016
</pre>
How can I achieve this - by PL/SQL or any SQL language?
Assuming that the time component of the dates is set to 00:00:00 then:
SELECT EmployeeId,
application_date,
GREATEST( start_date, DATE '2016-03-01' ) AS start_date,
LEAST( end_date, DATE '2016-03-31' ) AS end_date
FROM table_name
WHERE Start_date <= DATE '2016-03-31'
AND end_date >= DATE '2016-03-01'
You can use a bind variable to replace the hard-coded dates like this:
SELECT EmployeeId,
application_date,
GREATEST( start_date, :month_start ) AS start_date,
LEAST( end_date, LAST_DAY( :month_start ) ) AS end_date
FROM table_name
WHERE Start_date <= LAST_DAY( :month_start )
AND end_date >= :month_start
If you have time components then:
SELECT EmployeeId,
application_date,
GREATEST( start_date, :month_start ) AS start_date,
LEAST( end_date, :month_start + INTERVAL '1' MONTH - INTERVAL '1' SECOND )
AS end_date
FROM table_name
WHERE Start_date < :month_start + INTERVAL '1' MONTH
AND end_date >= :month_start
This is for SQL Server
SELECT *
FROM Leaves
WHERE MONTH(StartDate) <= 4 and Month(EndDate) >= 4
For Oracle
SELECT *
FROM Leaves
WHERE EXTRACT(month FROM StartDate) <= 4 and EXTRACT(month FROM EndDate) >= 4
The condition should be
WHERE StartDate>='01-Mar-2016' and EndDate <'01-Apr-2016'
You could use a parameter for the required month in format 'YYYYMM' (for March 2016: '201603')
then the where-clause would be:
where parameter = to_char(start_date,'yyyymm')
or parameter = to_char(end_date,'yyyymm')
if a leave can be longer than a month then you also need to check if the parameter is between start and end date.
..
or parameter between to_char(start_date,'yyyymm') and to_char(end_date,'yyyymm')
to display the correct date in the report you can use a CASE statement combined with the first_day and last_day function to display the first and the last day of the month.
(here the parameter_date is the required month as a DATE, not a VARCHAR2)
CASE
WHEN start_date < first_day(parameter_date) THEN first_day(parameter_date)
ELSE start_date
END as start_Date,
CASE
WHEN end_date > last_day(parameter_date) THEN last_day(parameter_date)
ELSE end_date
END as end_Date

Calculating days of therapy in a sql query

I'm attempting to calculate days of therapy by month from an oracle database. The (vastly simplified) data is as follows:
Therapies
+-----------+-----------+----------+
| Rx Number | StartDate | StopDate |
|-----------+-----------+----------|
| 1 | 12-29-14 | 1-10-15 |
| 2 | 1-2-15 | 1-14-15 |
| 3 | 1-29-15 | 2-15-15 |
+-----------+-----------+----------+
For the purposes of this example, all times are assumed to be midnight. The total days of therapy in this table is (10-1 + 32-29) + (14-2) + (15-1 + 32-29) = 41. The total days of therapy in January in this table is (10-1) + (14-2) + (32-29) = 24.
If I wanted to calculate days of therapy for the month of January , my best effort is the following query:
SELECT SUM(stopdate - startdate)
FROM therapies
WHERE startdate > to_date('01-JAN-15')
AND stopdate < to_date ('01-FEB-15');
However, rx's 1 and 3 are not captured at all. I could try the following instead:
SELECT SUM(stopdate - startdate)
FROM therapies
WHERE stopdate > to_date('01-JAN-15')
AND startdate < to_date ('01-FEB-15');
But that would include the full duration of the first and third therapies, not just the portion in January. To make the matter more complex, I need these monthly summaries over a period of two years. So my questions are:
How do I include overhanging therapies such that only the portion within the target time period is included, and
How do I automatically generate these monthly summaries over a two year period?
How do I include overhanging therapies such that only the portion
within the target time period is included?
select sum(
greatest(least(stopdate, date '2015-01-31' + 1)
- greatest(startdate, date '2015-01-01'), 0)) suma
from therapies
How do I automatically generate these monthly summaries over a two
year period?
with period as (select date '2014-01-01' d1, date '2015-12-31' d2 from dual),
months as (select trunc(add_months(d1, level-1), 'Month') dt
from period connect by add_months(d1, level-1)<d2)
select to_char(dt, 'yyyy-mm') mth,
sum(greatest(least(stopdate, add_months(dt, 1)) - greatest(startdate, dt), 0)) suma
from therapies, months
group by to_char(dt, 'yyyy-mm') order by mth
Above queries produced desired output. Please insert your dates in proper places to change analyzed periods.
In second SQL inner subquery months gives 24 dates, one for each month. The rest is only maneuvering
with functions greatest(),least() and some math.
Use a case statement to set the start date and stop date. Like the below:
select sum(
Stopdate -
(case Startdate
when startdate < to_date(#YourBeginingDate) then To_date(#YourBeginingDate)
else startdate
end)
FROM therapies
WHERE stopdate > to_date(#YourBeginingDate)
AND StartDate < to_date(#YourEndingDate)
I would do something like the following:
WITH t1 AS (
SELECT 1 AS rx, DATE'2014-12-29' AS start_date
, DATE'2015-01-10' AS stop_date
FROM dual
UNION ALL
SELECT 2, DATE'2015-01-02', DATE'2015-01-14'
FROM dual
UNION ALL
SELECT 3, DATE'2015-01-29', DATE'2015-02-15'
FROM dual
)
SELECT TRUNC(rx_dt, 'MONTH') AS rx_month, SUM(rx_cnt) AS rx_day_cnt
FROM (
SELECT rx_dt, COUNT(*) AS rx_cnt
FROM (
SELECT rx, start_date + LEVEL - 1 AS rx_dt
FROM t1
CONNECT BY start_date + LEVEL - 1 < stop_date
AND PRIOR rx = rx
AND PRIOR DBMS_RANDOM.VALUE IS NOT NULL
) GROUP BY rx_dt
) GROUP BY TRUNC(rx_dt, 'MONTH')
ORDER BY rx_month
Results:
12/1/2014 12:00:00 AM 2
1/1/2015 12:00:00 AM 24
2/1/2015 12:00:00 AM 15
See SQL Fiddle here.
What I am doing is using LEVEL and CONNECT BY to get all the days of therapy based on start_date and stop_date (not inclusive). I then GROUP BY the therapy date (rx_dt) to handle the overlapping therapies. Then I GROUP BY the month of the therapy using the TRUNC() function.
This should work just fine over a two-year period (or more); just add that filter before the last GROUP BY:
WHERE rx_dt >= DATE'2014-01-01'
AND rx_dt < DATE'2016-01-01'
GROUP BY TRUNC(rx_dt, 'MONTH')
Note that if your primary key is composite, you should include all the columns in the CONNECT BY clause:
CONNECT BY start_date + LEVEL - 1 < stop_date
AND PRIOR rx = rx
AND PRIOR patient_id = patient_id
--etc.
This is a bit tricky, as you need to capture days from sessions that:
Begin before the month and end after the month
Begin before the month and end during the month
Begin during the month and end after the month
Begin during the month and end during the month
To get those sessions, you can use a WHERE statement like this (the # symbol means that those are variables being passed in):
*examples are in TSQL, PLSQL might have somewhat different syntax
WHERE startdate < #endDate AND stopdate > #startDate
That should capture all four of those scenarios that I listed.
Then you only need to capture days that occurred during the month. I do this with a query that replaces the startdate/enddate with the date range limits if they exceed the range, like this:
SELECT
CASE WHEN enddate > #endDate then #endDate ELSE enddate END -
CASE WHEN startdate < #startDate THEN #startDate ELSE startdate END
So your whole query should look like this:
SELECT
SUM(
CASE WHEN enddate > #endDate then #endDate ELSE enddate END -
CASE WHEN startdate < #startDate THEN #startDate ELSE startdate END
)
FROM therapies
WHERE startdate < #endDate AND stopdate > #startDate
If you want to run that for two years, toss that code in a function that accepts #startDate and #endDate parameters, then call it from a query that gives you two years worth of months, like this:
WITH dateCTE AS (
SELECT
GETDATE() AS StartDate,
DATEADD(Month, 1, GETDATE()) AS EndDate
UNION ALL
SELECT
DATEADD(MONTH, -1, StartDate),
DATEADD(MONTH, -1, EndDate)
FROM dateCTE
WHERE StartDate > DATEADD(YEAR, -2, GETDATE())
)
SELECT
StartDate,
EndDate,
SomeFunction(StartDate, EndDate)
FROM dateCTE

Select data from date range between two dates

I have a table Named Product_Sales and it holds data like this
Product_ID | Sold_by | Qty | From_date | To_date
-----------+---------+-----+------------+-----------
3 | 12 | 7 | 2013-01-05 | 2013-01-07
6 | 22 | 14 | 2013-01-06 | 2013-01-10
8 | 11 | 9 | 2013-02-05 | 2013-02-11
Now what is the query if I want to select sales data between two dates from a date range?
For example, I want to select sales data from 2013-01-03 to 2013-01-09.
As you can see, there are two ways to get things done:
enlist all acceptable options
exclude all wrong options
Obviously, second way is much more simple (only two cases against four).
Your SQL will look like:
SELECT * FROM Product_sales
WHERE NOT (From_date > #RangeTill OR To_date < #RangeFrom)
SELECT * from Product_sales where
(From_date BETWEEN '2013-01-03'AND '2013-01-09') OR
(To_date BETWEEN '2013-01-03' AND '2013-01-09') OR
(From_date <= '2013-01-03' AND To_date >= '2013-01-09')
You have to cover all possibilities. From_Date or To_Date could be between your date range or the record dates could cover the whole range.
If one of From_date or To_date is between the dates, or From_date is less than start date and To_date is greater than the end date; then this row should be returned.
Try following query to get dates between the range:
SELECT *
FROM Product_sales
WHERE From_date >= '2013-01-03' AND
To_date <= '2013-01-09'
SELECT * FROM Product_sales
WHERE From_date between '2013-01-03'
AND '2013-01-09'
This covers all conditions that you are looking for.
SELECT * from Product_sales where (From_date <= '2013-01-09' AND To_date >= '2013-01-01')
SELECT *
FROM Product_sales
WHERE (
From_date >= '2013-08-19'
AND To_date <= '2013-08-23'
)
OR (
To_date >= '2013-08-19'
AND From_date <= '2013-08-23'
)
Please try:
DECLARE #FrmDt DATETIME, #ToDt DATETIME
SELECT #FrmDt='2013-01-03', #ToDt='2013-01-09'
SELECT *
FROM Product_sales
WHERE (#FrmDt BETWEEN From_date AND To_date) OR
(#ToDt BETWEEN From_date AND To_date)
This is easy, use this query to find select data from date range between two dates
select * from tabblename WHERE (datecolumn BETWEEN '2018-04-01' AND '2018-04-5')
Just my 2 cents, I find using the "dd-MMM-yyyy" format safest as the db server will know what you want regardless of the regional settings on the server. Otherwise you could potentially run into issues on a server that has its date regional settings as yyyy-dd-mm (for whatsoever reason)
Thus:
SELECT * FROM Product_sales
WHERE From_date >= '03-Jan-2013'
AND To_date <= '09-Jan-2013'
It's always worked well for me ;-)
select *
from table
where
( (table.EndDate > '2013-01-05') and (table.StartDate < '2013-01-07' ) )
Check this query, i created this query to check whether the check in date over lap with any reservation dates
SELECT * FROM tbl_ReservedRooms
WHERE NOT ('#checkindate' NOT BETWEEN fromdate AND todate
AND '#checkoutdate' NOT BETWEEN fromdate AND todate)
this will retrun the details which are overlaping , to get the not overlaping details then remove the 'NOT' from the query
This query will help you:
select *
from XXXX
where datepart(YYYY,create_date)>=2013
and DATEPART(YYYY,create_date)<=2014
This working on SQL_Server_2008 R2
Select *
from Product_sales
where From_date
between '2013-01-03' and '2013-01-09'
SELECT NULL
FROM HRMTable hm(NOLOCK)
WHERE hm.EmployeeID = 123
AND (
(
CAST(#Fromdate AS date) BETWEEN CAST(hm.FromDate AS date)
AND CAST(hm.ToDate AS date)
)
OR (
CAST(#Todate AS date) BETWEEN CAST(hm.FromDate AS date)
AND CAST(hm.ToDate AS date)
)
)
)
You can also try using following fragments:
select * from Product_sales
where From_date >= '2013-01-03' and game_date <= '2013-01-09'
Here is a query to find all product sales that were running during the month of August
Find Product_sales there were active during the month of August
Include anything that started before the end of August
Exclude anything that ended before August 1st
Also adds a case statement to validate the query
SELECT start_date,
end_date,
CASE
WHEN start_date <= '2015-08-31' THEN 'true'
ELSE 'false'
END AS started_before_end_of_month,
CASE
WHEN NOT end_date <= '2015-08-01' THEN 'true'
ELSE 'false'
END AS did_not_end_before_begining_of_month
FROM product_sales
WHERE start_date <= '2015-08-31'
AND end_date >= '2015-08-01'
ORDER BY start_date;
DECLARE #monthfrom int=null,
#yearfrom int=null,
#monthto int=null,
#yearto int=null,
#firstdate DATE=null,
#lastdate DATE=null
SELECT #firstdate=DATEADD(month,#monthfrom-1,DATEADD(year,#yearfrom-1900,0)) /*Setting First Date using From Month & Year*/
SELECT #lastdate= DATEADD(day,-1,DATEADD(month,#monthto,DATEADD(year,#yearto-1900,0)))/*Setting Last Date using From Month & Year*/
SELECT * FROM tbl_Record
WHERE (DATEADD(yy, Year - 1900, DATEADD(m, Month - 1, 1 - 1)) BETWEEN CONVERT(DATETIME, #firstdate, 102) AND
CONVERT(DATETIME, #lastdate, 102))
You should compare dates in sql just like you compare number values,
SELECT * FROM Product_sales
WHERE From_date >= '2013-01-01' AND To_date <= '2013-01-20'
this is easy, use this query to find what you want.
select * from Product_Sales where From_date<='2018-04-11' and To_date>='2018-04-11'

How can I count and group by between two date?

For example,
the start date = '20100530' and
the end date = '20100602'
How can I write a SQL to display the below result?
month: may, 2 days
month: June, 2 days
Use a recursive CTE to generate all of the dates between the start and end, and then do a simple group and count (caution, not tested, but should be close if not exactly right):
with dates (the_date) as (
select #start_date
UNION ALL
select dateadd(dd, 1, the_date) from dates where the_date <= #end_date
)
select
datepart(mm, the_date) month,
count(*) num_days
from
dates
group by
datepart(mm, the_date)
TBH, you really need to provide more schema and information about the source data. However, given what little we know, you should be able to write:
Select DateName('month', [Date]) As Month
, Cast(DateDiff(d, #StartDate, #EndDate) As varchar(10) - 1) + ' days'
From Table
Where [Date] Between #StartDate And #EndDate
What we'd need to know to refine our solutions is exactly how 2 days is supposed to be calculated. Is it the days between the start and end date? Is it the day of the second parameter?

Date split-up based on Fiscal Year

Given a From Date, To Date and a Fiscal Year system, I want to get all the split-up duration within the
given From & To Date based on the Fiscal Year system. Explained below with examples:
Example 1:
Fiscal Year system: Apr to Mar
From Date: Jan-05-2008
To Date: May-15-2008
Based on Fiscal Year system, duration should be splitted into:
Jan-05-2008 to Mar-31-2008
Apr-01-2008 to May-15-2008
Example 2:
Fiscal Year system: Apr to Mar
From Date: Jan-17-2008
To Date: May-20-2009
Based on Fiscal Year system, duration should be splitted into:
Jan-17-2008 to Mar-31-2008
Apr-01-2008 to Mar-31-2009
Apr-01-2009 to May-20-2009
Am looking for approach/algorithm to solve this in PostgreSQL 8.2.
Regards,
Gnanam
I actually favor Andomar's solution (with the addition of a processes that automatically fills the Periods table), but for fun here's a solution that doesn't require it.
CREATE TABLE your_table (start_date date, end_date date);
INSERT INTO your_table VALUES ('Jan-17-2008', 'May-20-2009');
SELECT
GREATEST(start_date, ('04-01-'||series.year)::date) AS year_start,
LEAST(end_date, ('03-31-'||series.year + 1)::date) AS year_end
FROM
(SELECT
start_date,
end_date,
generate_series(
date_part('year', your_table.start_date - INTERVAL '3 months')::int,
date_part('year', your_table.end_date - INTERVAL '3 months')::int)
FROM your_table) AS series(start_date, end_date, year)
ORDER BY
start_date;
You could create a table containing the start and end of all fiscal years, f.e.
Periods (PeriodStartDt, PeriodEndDt)
Then you can join the tables together if they at least partly overlap. Use a case statement to select the end of the period or the end of the row, depending on which is later. For example (not tested):
select case when yt.StartDt < p.PeriodStartDt then p.PeriodStartDt
else yt.StartDt
end as SplitStart
, case when yt.EndDt > p.PeriodEndDt then p.PeriodEndDt
else yt.EndDt
end as SplitEnd
, yt.*
from YourTable yt
inner join Periods p
on yt.StartDt < p.PeriodEndDate
and yt.EndDt >= p.PeriodStartDate
CREATE TABLE your_table (start_date date, end_date date);
INSERT INTO your_table VALUES (CONVERT (date, GETDATE()),CONVERT (date, DATEADD(year, -1, GETDATE())) );
WITH mycte AS
(
SELECT 1 as id
UNION ALL
SELECT id + 1
FROM mycte
WHERE id + 1 < = 12
),
cte_distribution as
(
SELECT *,
DATEPART (month,DATEADD(month, mycte.id - 1, your_table.start_date)) as month_number ,
DATEPART (YEAR,DATEADD(month, mycte.id - 1, your_table.start_date)) as cal_year,
12000/12 as cash
FROM your_table
CROSS JOIN mycte
)
select
*,
(CASE WHEN month_number between 1 and 3 THEN '1st quarter' WHEN month_number between 4 and 6 THEN '2nd quarter' WHEN month_number between 7 and 9 THEN '3rd quarter' WHEN month_number between 9 and 12 THEN '4th quarter' END) as Quarter,
CASE WHEN month_number between 1 and 6 THEN CAST(CAST((cal_year - 1) as CHAR(4)) + '-' + CAST(cal_year as CHAR(4)) AS CHAR(9)) WHEN month_number between 6 and 12 THEN CAST(CAST((cal_year) as CHAR(4)) + '-' + CAST((cal_year + 1) as CHAR(4)) AS CHAR(9)) ELSE NULL END as fin_year
from cte_distribution;