finding missing dates in a time interval (SQL) - sql

I am a bit of a noob with SQL, so I was searching for some bit of code that might help me find missing date values for a time interval when I stumbled upon this code.
DECLARE #StartDate DATETIME DECLARE #EndDate DATETIME
SET #StartDate ='2014-03-01' SET #EndDate = GETDATE()
;WITH Dates(Date) AS
(
SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, #StartDate)) AS Date
UNION ALL
SELECT DATEADD(day, 1, Date) AS Date
FROM Dates
WHERE Date <= #EndDate
)
SELECT d.Date, r.Value
FROM Dates d
LEFT JOIN Times r ON d.Date = r.Date
Link to the code
It works really well for my problem but I am not able to understand how it increments the date.
I would ask the author but their blog no longer exists and their twitter is inactive too.
Edit: someone said the post is lacking a question. I want to know how this CTE is recursively adding the +1 to each date from #StartDate to #EndDate.

This is a recursive CTE, or Common Table Expression.
The first line of of the CTE SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, #StartDate)) AS Date is the seed, or root portion. The next UNION section takes that date, adds a day, and recurses.
Notice that inside the Dates block you are selecting FROM Dates, so it will continue produce rows with an incremented date until the WHERE clause is satisfied.

Related

How to get last reminder or next reminder date in SQL Server?

I am trying to getting the last reminder date and the next reminder date from the SQL Server.
I have a start date and interval time
startdate = '1-Jan-20'
interval = 3 (months)
I want to get the last reminder date and next reminder by the SQL function
I want output as
lastreminderdate = '1-apr-20'
nextreminderdate = '1-jul-20'
Please assist me in how to do this
Thanks
A fun way to solve this uses recursive CTEs. The CTE generates all the dates up to the first one following the current date. Then the most recent two values are returned:
declare #startdate date = '2020-01-01';
declare #interval_months int = 3;
with cte as (
select #startdate as dte
union all
select dateadd(month, #interval_months, dte)
from cte
where dte <= getdate()
)
select top (2) dte
from cte
order by dte desc;
Here is a db<>fiddle.
If these series require more than 100 iteration, you need to add option (maxrecursion 0).
I am suggesting this because it is a very "SQL"-y approach to the problem, and a nice introduction to recursive CTEs. The more efficient methods would use a lot of date arithmetic (prone to error). This is also pretty easy to modify for different time frames -- such support requires a case expression but isn't that complicated.
declare #startDate Date = dateadd(month, -1, CAST('01-Jan-2020' as Date))
declare #interval_months int = 3
Select dateadd(month, #interval_months, #startDate), dateadd(month, #interval_months*2, #startDate)
Output
lastreminderdate nextreminderdate
-------------------------------------
2020-03-01 2020-06-01

SQL- Weekly report seven days (Sunday to Saturday )

I have created the following query to reoccur every seven days starting including Saturday and Sunday. This report will be run every seven days. However, the problem I am facing on the days where there were files received in our SFTP folder (inbound) report should have an entry for the missing Null= 0. The primary goal to make this an automated process that will be executed every Sunday through Sunday every seven days
Example:
SELECT SubmitterID,SubmitterName,convert(varchar(15), DateReceived, 101) DateReceived,sum(ClaimCount) as TotalCount
FROM FalloutClaimReport
WHERE DateReceived BETWEEN '2019-06-01' AND '2019-06-07'
--ORDER BY COUNT(submitterID) DESC;
GROUP BY submitterid, SubmitterName, convert(varchar(15), DateReceived, 101)
DECLARE #StartDate AS DATETIME
DECLARE #EndDate AS DATETIME
DECLARE #CurrentDate AS DATETIME
SET #StartDate = '2019-06-01' --AND '2019-06-10'
SET #StartDate = '2019-06-07'
SET #EndDate = GETDATE()
SET #CurrentDate = #StartDate
I'm a little uncertain what you're looking for but I think the general approach is you're trying to get a week's worth of data.
Date calculations
Let's start with some queries (and these presume a US install as the default day is Monday.
SELECT
DATEADD(WEEK, -1, CAST(DATEADD(WEEK, DATEDIFF(WEEK, 0, CAST(GETDATE() AS date)), -1) AS date)) AS TheLastSundayOfTheFullWeek
, DATEADD(WEEK, -1, CAST(DATEADD(WEEK, DATEDIFF(WEEK, 0, CAST(GETDATE() AS date)), +5) AS date)) AS TheLastSaturdayOfTheFullWeek
, CAST(DATEADD(WEEK, DATEDIFF(WEEK, 0, CAST(GETDATE() AS date)), -1) AS date) AS SundayOfTheCurrentWeek
, CAST(DATEADD(WEEK, DATEDIFF(WEEK, 0, CAST(GETDATE() AS date)), +5) AS date) AS SaturdayOfTheCurrentWeek;
These queries generate the following dates
TheLastSundayOfTheFullWeek TheLastSaturdayOfTheFullWeek SundayOfTheCurrentWeek SaturdayOfTheCurrentWeek
2019-06-30 2019-07-06 2019-07-07 2019-07-13
The last full week would run 6/30 to 7/06. The current week would be defined as 7/7 to 7/13.
Depending on which week definition you need, choose the appropriate pair of columns.
Dealing with the unknowns
In situations like this, I build out a virtual table with all expected dates (or elements) my report should have. I then use that to drive a connection to the actual data table. Since we don't know that we'll find any rows for a given date, I connect the tables with a LEFT JOIN
SELECT
FCR.SubmitterID
, FCR.SubmitterName
, CONVERT(varchar(15), ED.DateReceived, 101) AS DateReceived
, SUM(FCR.ClaimCount) AS TotalCount
FROM
(
-- This logic builds out a list of all the dates that must exist on the report
-- I used the logic for TheLastSundayOfTheFullWeek
SELECT
DATEADD(DAY, D.DayOffset, DATEADD(WEEK, -1, CAST(DATEADD(WEEK, DATEDIFF(WEEK, 0, CAST(GETDATE() AS date)), -1) AS date))) AS DateReceived
FROM
(
-- Generate a series of 7 numbers from 0 t 6
SELECT TOP 7
-1 + ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn
FROM
sys.all_columns AS AC
) D(DayOffset)
) AS ED
LEFT OUTER JOIN
dbo.FalloutClaimReport AS FCR
ON FCR.DateReceived = ED.DateReceived
GROUP BY
CONVERT(varchar(15), ED.DateReceived, 101)
, FCR.SubmitterID
, FCR.SubmitterName;
That generates a result set like
We didn't have data on the the 30th or the 5th but there are still records on the query. If you need default values in there, wrap the column with ISNULL/COALESCE calls.
DBFiddle version to provide a testing sandbox
Audrey,
I would suggest two possible solutions. Assuming by SRS you meant, SSRS....
1) I would set your SSIS job to run through a SQL Agent every 7 days. It would call a stored procedure (SP) that then ran and wrote into a table... when that SP would be called it would run:
SELECT
SubmitterID,
SubmitterName,
convert(varchar(15), DateReceived, 101) DateReceived,
sum(ClaimCount) as TotalCount
FROM FalloutClaimReport
WHERE cast(DateReceived as date) BETWEEN dateadd(d,-7,cast(getdate() as date)) AND dateadd(d,-1,cast(getdate() as date))
GROUP BY
submitterid,
SubmitterName,
convert(varchar(15), DateReceived, 101)
2) If you decide to go the SSRS route, you should make a report subscription that sends automatically to the users you need that calls the stored procedure above and sends what you need to whoever needs it. (The code above should be enough for that assuming it's selecting what you need)

How to select data's from this today,week and month sperately?

I have a problem that is I am unable to resolve as of now.
I need to get the data of
this day, this week and this month
I have a table reminder where I want to select reminders according to
following parameters.
1. Today
2. This Week
3. This Month
The column rdate having the date format in dd-mm-yyyy which is stored as nvarchar
For example
If I execute this weeks query I should get data starting from this week i.e.
If it is Friday I should get data from starting from Sunday to Saturday of that week
How can I get the data as mentioned above. I have searched a lot on internet but I didn't get the solution?
This is the query I have been trying
SELECT
*
FROM
reminder
WHERE
date > DATE_SUB(GETDATE(), INTERVAL 1 DAY)
ORDER BY
rdate DESC;
Where I'm converting nvarchar to date format.
If it's not possible to change the [date] column's data type to DATE, then you will incur a massive performance penalty when trying to filter by date.
Add computed column to table
We can add a computed column that will store the date in the correct format, and then index it for quick searchiing:
ALTER TABLE reminder
ADD Date_Value AS (CONVERT(DATE, '12-05-2016', 105)) PERSISTED;
-- This should yield superior performance
CREATE NONCLUSTERED INDEX IX_Date_Value ON reminder (Date_Value);
Table-valued function to calculate date range
Now, let's create an inline table-valued function to generate the date range for specific period types:
CREATE FUNCTION [dbo].[tvfn_Get_Date_Range](
#Period_Type VARCHAR(100)
)
RETURNS
TABLE
AS RETURN
(
WITH date_range AS(
SELECT CAST(GETDATE() AS DATE) d
-- This line works correctly if your week starts on Sunday
,CAST(DATEADD(WEEK, DATEDIFF(WEEK, '19050101', GETDATE()), '19050101') AS DATE) AS week_start
,CAST(DATEADD(DAY, - DAY(GETDATE()) + 1, GETDATE()) AS DATE) AS month_start
,CAST(DATEADD(MONTH, 1, DATEADD(DAY, - DAY(GETDATE()), GETDATE())) AS DATE) AS month_end
)
SELECT d AS From_Date
,d AS To_Date
FROM date_range
WHERE #Period_Type = 'DAY'
UNION ALL
SELECT week_start
,DATEADD(DAY, 7, week_start)
FROM date_range
WHERE #Period_Type = 'WEEK'
UNION ALL
SELECT month_start
,month_end
FROM date_range
WHERE #Period_Type = 'MONTH'
)
In the above function, week starts on Sunday. If you need this to be configurable, then take a look at the answer to SET DATEFIRST in FUNCTION.
Fast, simple querying now possible
You can now use the two together using a simple query:
SET #Range VARCHAR(100) = 'WEEK'
SELECT *
FROM reminder
CROSS APPLY [dbo].[tvfn_Get_Date_Range](#Range) dr
WHERE Date_Value BETWEEN dr.Date_From AND dr.Date_To
If you can't change the columns data type to Date (or DateTime), you must convert it to date in the query.
Here is one way to get the data for today, this week and this month:
Get records from today:
SELECT *
FROM reminder
WHERE CONVERT(Date, [date], 105) = CAST(GETDATE() as date)
ORDER BY rdate DESC;
Get records from this week:
SELECT *
FROM reminder
WHERE DATEPART(WEEK, CONVERT(Date, [date], 105)) = DATEPART(WEEK, GETDATE())
AND DATEPART(YEAR, CONVERT(Date, [date], 105)) = DATEPART(YEAR, GETDATE())
ORDER BY rdate DESC;
Get records from this Month:
SELECT *
FROM reminder
WHERE DATEPART(MONTH, CONVERT(Date, [date], 105)) = DATEPART(MONTH, GETDATE())
AND DATEPART(YEAR, CONVERT(Date, [date], 105)) = DATEPART(YEAR, GETDATE())
ORDER BY rdate DESC;
To my knowledge, SQL server internally deals with date format as MM/dd/yyyy.
Usually I prefer to save date as string in SQL table since it's easier for inserting and retrieving.
For example, suppose that the column rdate is defined as follows in your table reminder:
[rdate] nvarchar NULL
Then you can customize the select statement for a week as follows:
"Select R.* From reminder R Where CAST(R.rdate as datetime) between
'03/04/2011' AND '03/11/2011'"
And for 10 days as follows:
"Select R.* From reminder R Where CAST(R.rdate as datetime) between
'03/04/2011' AND '03/14/2011'"
And so on. If this is not what you want, please provide more details about your requirements.

Getting the date for each day within a date range in SQL

I'm looking for a way to get a list of instances a certain day appears between two date periods in SQL.
I have a range:
DECLARE #ViewStartDate DATETIME
DECLARE #ViewEndDate DATETIME
SET #ViewStartDate = '2014-09-08 00:00:00.000';
SET #ViewEndDate = '2014-09-30 00:00:00.000';
And need to get e.g. every Monday (date) within that list. I have tried looking all over for an answer as specific as this and can't seem to find anything relevant.
The reason is that it will be used in a logistics program to calculate delivery dates between a date range where the required delivery day is every Monday.
You can use the following query.
The first part in the cte is to find the next monday after the startdate. Then keep adding one week until the end date.
;WITH Dates([Date])
AS
(
--If the start date is sunday or monday
SELECT CASE WHEN DATEPART(WEEKDAY,#ViewStartDate) <=2
--Then the monday of that week
THEN DATEADD(WEEK, DATEDIFF(WEEK, 1, #ViewStartDate), 0)
ELSE
-- Mondy of the next week
DATEADD(WEEK,1,DATEADD(WEEK, DATEDIFF(WEEK, 0, #ViewStartDate), 0))
END
UNION ALL
-- Loop by joining the CTE Dates, until the date < #ViewEndDate
SELECT DATEADD(WEEK, 1,Date)
FROM Dates
WHERE DATEADD(WEEK, 1,Date) <= #ViewEndDate
)
SELECT [Date]
FROM Dates;

how to get last 9 months data?

I am working on a stored proc and need last 9 months data. Need a syntax Which will automatically deletes the oldest data when new data will be added to the table(Latest 9 months data).
That syntax is gonna be used in Select Syntax.
I have used
select * from tablename t
left outer join calendartable r on
t.fiscal_month=r.fiscal_month
where t.date > dateadd(m,-9,date)
I know it is wrong. Could you guys please help me with this.
Thanks
You probably want GETDATE to calculate the nine month boundary from now:
where t.date >= dateadd(m,-9, GETDATE())
Beware that if t.date is a date and time field not just date you'll see odd behaviour on the nine-month boundary unless you also round away the time before the comparison.
Or if you're comparing it against another value e.g. the date of the inserted record in your trigger then what you've got is probably OK, e.g. something like
declare #latest date
select #latest = inserted.date
delete from ... where t.date < dateadd(m, -9, #latest)
although I suggest you actually archive off the data, not delete it.
Since you've clarified you want whole months, i.e. 9 months from the end of last month, you could use
declare #today date;
declare #firstOfMonth date;
declare #nineMonthsAgo date;
set #today = GETDATE();
set #firstOfMonth = DATEADD(d, 1-DAY(#today), #today);
set #nineMonthsAgo = DATEADD(m, -9, #firstOfMonth);
... WHERE date >= #nineMonthsAgo AND date < #firstOfMonth
seems like it's pretty close. Do you need 9 months from right now, or 9 months from a given date?
how about:
declare #date datetime
set #date = dateadd(m, -9, getdate()) -- 9 months from right now
select *
from tablename t
left outer join calendartable r
on t.fiscal_month=r.fiscal_month
where t.date > #date
If You need to delete the data when new data is added, you will need to do it in a trigger. And the sintax inside the trigger would be like this.
DELETE t
FROM
tablename t
left outer join calendartable r on
t.fiscal_month=r.fiscal_month
where datediff(month, t.date, GETDATE()) > 9
And the select to retrieve your data will be similar.
select * from tablename t
left outer join calendartable r on
t.fiscal_month=r.fiscal_month
where datediff(month, t.date, GETDATE()) < 9