Tally Table to insert missing dates between two dates? SQL
Another option: (updated [again])
Create and populate sample table (Please save us this step in your next question)
DECLARE @T as TABLE
(
Staff_ID int,
[Date] date,
[Year] int,
Mon int,
[Day] int,
First_Name varchar(10),
Last_Name varchar(10),
Section varchar(10),
Time_Worked datetime
)
INSERT INTO @T VALUES
(1001, '2016-06-01', 2016, 6, 1, 'Bill', 'Price', 'Level 1', '2016-06-01 8:30:00.000'),
(1001, '2016-06-05', 2016, 6, 5, 'Bill', 'Price', 'Level 1', '2016-06-05 8:30:00.000'),
(1001, '2016-06-09', 2016, 6, 9, 'Bill', 'Price', 'Level 1', '2016-06-09 8:30:00.000'),
(1001, '2016-07-05', 2016, 7, 5, 'Bill', 'Price', 'Level 2', '2016-06-12 8:30:00.000'), -- Different month
(1002, '2016-06-01', 2016, 6, 1, 'Mary', 'Somers', 'Level 1', '2016-06-01 8:30:00.000'),
(1002, '2016-06-05', 2016, 6, 5, 'Mary', 'Somers', 'Level 1', '2016-06-05 8:30:00.000'),
(1002, '2016-06-08', 2016, 6, 8, 'Mary', 'Somers', 'Level 1', '2016-06-08 8:30:00.000'),
(1003, '2016-06-03', 2016, 6, 3, 'Mark', 'Jones', 'Level 1', '2016-06-03 8:30:00.000'),
(1003, '2016-06-04', 2016, 6, 4, 'Mark', 'Jones', 'Level 1', '2016-06-05 8:30:00.000')
Declare and populate @StartDate
and @EndDate
:
DECLARE @StartDate datetime = '2016-06-01',
@EndDate datetime = '2016-08-01'
The INSERT...SELECT
statement: (I've chosen to use a cte so I wouldn't have to write the dateadd(DD,ID-1,@StartDate)
so many times)
;WITH Calendar AS
(
SELECT dateadd(DD,ID-1,@StartDate) as [Date]
FROM dbo.Tally
WHERE dateadd(DD,ID-1,@StartDate) < @EndDate
)
INSERT INTO @T (Staff_ID, [Date], [Year], Mon, [Day], First_Name, Last_Name, Section)
SELECT DISTINCT Staff_ID, C.[Date], Year(C.[Date]), MONTH(C.[Date]), DAY(C.[Date]), First_Name, Last_Name, Section
FROM @T T
CROSS APPLY
(
SELECT Cal.[Date]
FROM Calendar Cal
WHERE MONTH(Cal.[Date]) = MONTH(T.[Date])
AND YEAR(Cal.[Date]) = YEAR(T.[Date])
AND NOT EXISTS
(
SELECT 1
FROM @T T2
WHERE T.Staff_ID = T2.Staff_ID
AND T2.[Date] = Cal.[Date]
)
) C
Verify inserts:
SELECT Staff_ID, [Date], [Year], Mon, [Day], First_Name, Last_Name, Section, Time_Worked
FROM @T
ORDER BY Staff_ID, [Date]
Results:
Staff_ID Date Year Mon Day First_Name Last_Name Section Time_Worked
----------- ---------- ----------- ----------- ----------- ---------- ---------- ---------- -----------------------
1001 2016-06-01 2016 6 1 Bill Price Level 1 2016-06-01 08:30:00.000
1001 2016-06-02 2016 6 2 Bill Price Level 1 NULL
1001 2016-06-03 2016 6 3 Bill Price Level 1 NULL
1001 2016-06-04 2016 6 4 Bill Price Level 1 NULL
1001 2016-06-05 2016 6 5 Bill Price Level 1 2016-06-05 08:30:00.000
1001 2016-06-06 2016 6 6 Bill Price Level 1 NULL
1001 2016-06-07 2016 6 7 Bill Price Level 1 NULL
1001 2016-06-08 2016 6 8 Bill Price Level 1 NULL
1001 2016-06-09 2016 6 9 Bill Price Level 1 2016-06-09 08:30:00.000
1001 2016-06-10 2016 6 10 Bill Price Level 1 NULL
1001 2016-06-11 2016 6 11 Bill Price Level 1 NULL
1001 2016-06-12 2016 6 12 Bill Price Level 1 NULL
1001 2016-06-13 2016 6 13 Bill Price Level 1 NULL
1001 2016-06-14 2016 6 14 Bill Price Level 1 NULL
1001 2016-06-15 2016 6 15 Bill Price Level 1 NULL
1001 2016-06-16 2016 6 16 Bill Price Level 1 NULL
1001 2016-06-17 2016 6 17 Bill Price Level 1 NULL
1001 2016-06-18 2016 6 18 Bill Price Level 1 NULL
1001 2016-06-19 2016 6 19 Bill Price Level 1 NULL
1001 2016-06-20 2016 6 20 Bill Price Level 1 NULL
1001 2016-06-21 2016 6 21 Bill Price Level 1 NULL
1001 2016-06-22 2016 6 22 Bill Price Level 1 NULL
1001 2016-06-23 2016 6 23 Bill Price Level 1 NULL
1001 2016-06-24 2016 6 24 Bill Price Level 1 NULL
1001 2016-06-25 2016 6 25 Bill Price Level 1 NULL
1001 2016-06-26 2016 6 26 Bill Price Level 1 NULL
1001 2016-06-27 2016 6 27 Bill Price Level 1 NULL
1001 2016-06-28 2016 6 28 Bill Price Level 1 NULL
1001 2016-06-29 2016 6 29 Bill Price Level 1 NULL
1001 2016-06-30 2016 6 30 Bill Price Level 1 NULL
1001 2016-07-01 2016 7 1 Bill Price Level 2 NULL
1001 2016-07-02 2016 7 2 Bill Price Level 2 NULL
1001 2016-07-03 2016 7 3 Bill Price Level 2 NULL
1001 2016-07-04 2016 7 4 Bill Price Level 2 NULL
1001 2016-07-05 2016 7 5 Bill Price Level 2 2016-06-12 08:30:00.000
1001 2016-07-06 2016 7 6 Bill Price Level 2 NULL
1001 2016-07-07 2016 7 7 Bill Price Level 2 NULL
1001 2016-07-08 2016 7 8 Bill Price Level 2 NULL
1001 2016-07-09 2016 7 9 Bill Price Level 2 NULL
1001 2016-07-10 2016 7 10 Bill Price Level 2 NULL
1001 2016-07-11 2016 7 11 Bill Price Level 2 NULL
1001 2016-07-12 2016 7 12 Bill Price Level 2 NULL
1001 2016-07-13 2016 7 13 Bill Price Level 2 NULL
1001 2016-07-14 2016 7 14 Bill Price Level 2 NULL
1001 2016-07-15 2016 7 15 Bill Price Level 2 NULL
1001 2016-07-16 2016 7 16 Bill Price Level 2 NULL
1001 2016-07-17 2016 7 17 Bill Price Level 2 NULL
1001 2016-07-18 2016 7 18 Bill Price Level 2 NULL
1001 2016-07-19 2016 7 19 Bill Price Level 2 NULL
1001 2016-07-20 2016 7 20 Bill Price Level 2 NULL
1001 2016-07-21 2016 7 21 Bill Price Level 2 NULL
1001 2016-07-22 2016 7 22 Bill Price Level 2 NULL
1001 2016-07-23 2016 7 23 Bill Price Level 2 NULL
1001 2016-07-24 2016 7 24 Bill Price Level 2 NULL
1001 2016-07-25 2016 7 25 Bill Price Level 2 NULL
1001 2016-07-26 2016 7 26 Bill Price Level 2 NULL
1001 2016-07-27 2016 7 27 Bill Price Level 2 NULL
1001 2016-07-28 2016 7 28 Bill Price Level 2 NULL
1001 2016-07-29 2016 7 29 Bill Price Level 2 NULL
1001 2016-07-30 2016 7 30 Bill Price Level 2 NULL
1001 2016-07-31 2016 7 31 Bill Price Level 2 NULL
1002 2016-06-01 2016 6 1 Mary Somers Level 1 2016-06-01 08:30:00.000
1002 2016-06-02 2016 6 2 Mary Somers Level 1 NULL
1002 2016-06-03 2016 6 3 Mary Somers Level 1 NULL
1002 2016-06-04 2016 6 4 Mary Somers Level 1 NULL
1002 2016-06-05 2016 6 5 Mary Somers Level 1 2016-06-05 08:30:00.000
1002 2016-06-06 2016 6 6 Mary Somers Level 1 NULL
1002 2016-06-07 2016 6 7 Mary Somers Level 1 NULL
1002 2016-06-08 2016 6 8 Mary Somers Level 1 2016-06-08 08:30:00.000
1002 2016-06-09 2016 6 9 Mary Somers Level 1 NULL
1002 2016-06-10 2016 6 10 Mary Somers Level 1 NULL
1002 2016-06-11 2016 6 11 Mary Somers Level 1 NULL
1002 2016-06-12 2016 6 12 Mary Somers Level 1 NULL
1002 2016-06-13 2016 6 13 Mary Somers Level 1 NULL
1002 2016-06-14 2016 6 14 Mary Somers Level 1 NULL
1002 2016-06-15 2016 6 15 Mary Somers Level 1 NULL
1002 2016-06-16 2016 6 16 Mary Somers Level 1 NULL
1002 2016-06-17 2016 6 17 Mary Somers Level 1 NULL
1002 2016-06-18 2016 6 18 Mary Somers Level 1 NULL
1002 2016-06-19 2016 6 19 Mary Somers Level 1 NULL
1002 2016-06-20 2016 6 20 Mary Somers Level 1 NULL
1002 2016-06-21 2016 6 21 Mary Somers Level 1 NULL
1002 2016-06-22 2016 6 22 Mary Somers Level 1 NULL
1002 2016-06-23 2016 6 23 Mary Somers Level 1 NULL
1002 2016-06-24 2016 6 24 Mary Somers Level 1 NULL
1002 2016-06-25 2016 6 25 Mary Somers Level 1 NULL
1002 2016-06-26 2016 6 26 Mary Somers Level 1 NULL
1002 2016-06-27 2016 6 27 Mary Somers Level 1 NULL
1002 2016-06-28 2016 6 28 Mary Somers Level 1 NULL
1002 2016-06-29 2016 6 29 Mary Somers Level 1 NULL
1002 2016-06-30 2016 6 30 Mary Somers Level 1 NULL
1003 2016-06-01 2016 6 1 Mark Jones Level 1 NULL
1003 2016-06-02 2016 6 2 Mark Jones Level 1 NULL
1003 2016-06-03 2016 6 3 Mark Jones Level 1 2016-06-03 08:30:00.000
1003 2016-06-04 2016 6 4 Mark Jones Level 1 2016-06-05 08:30:00.000
1003 2016-06-05 2016 6 5 Mark Jones Level 1 NULL
1003 2016-06-06 2016 6 6 Mark Jones Level 1 NULL
1003 2016-06-07 2016 6 7 Mark Jones Level 1 NULL
1003 2016-06-08 2016 6 8 Mark Jones Level 1 NULL
1003 2016-06-09 2016 6 9 Mark Jones Level 1 NULL
1003 2016-06-10 2016 6 10 Mark Jones Level 1 NULL
1003 2016-06-11 2016 6 11 Mark Jones Level 1 NULL
1003 2016-06-12 2016 6 12 Mark Jones Level 1 NULL
1003 2016-06-13 2016 6 13 Mark Jones Level 1 NULL
1003 2016-06-14 2016 6 14 Mark Jones Level 1 NULL
1003 2016-06-15 2016 6 15 Mark Jones Level 1 NULL
1003 2016-06-16 2016 6 16 Mark Jones Level 1 NULL
1003 2016-06-17 2016 6 17 Mark Jones Level 1 NULL
1003 2016-06-18 2016 6 18 Mark Jones Level 1 NULL
1003 2016-06-19 2016 6 19 Mark Jones Level 1 NULL
1003 2016-06-20 2016 6 20 Mark Jones Level 1 NULL
1003 2016-06-21 2016 6 21 Mark Jones Level 1 NULL
1003 2016-06-22 2016 6 22 Mark Jones Level 1 NULL
1003 2016-06-23 2016 6 23 Mark Jones Level 1 NULL
1003 2016-06-24 2016 6 24 Mark Jones Level 1 NULL
1003 2016-06-25 2016 6 25 Mark Jones Level 1 NULL
1003 2016-06-26 2016 6 26 Mark Jones Level 1 NULL
1003 2016-06-27 2016 6 27 Mark Jones Level 1 NULL
1003 2016-06-28 2016 6 28 Mark Jones Level 1 NULL
1003 2016-06-29 2016 6 29 Mark Jones Level 1 NULL
1003 2016-06-30 2016 6 30 Mark Jones Level 1 NULL
Find missing date ranges between two dates
A note, I am assuming here that the expected results for your final expected results is wrong here as it doesn't match the other 2. The last and first rows in the expected results for the last set both have a value for ID
that isn't 0
, but no explanation of why they do is given. I therefore assume the value should be 0
like the row in the "middle".
To do this, I use a Tally to get all the dates between the date range you need; the Tally is limited to 1,000 rows, a little shy of 3 years, but you can cross join to N
more if you need more rows. I then use that tally to create an inline calendar table. Next I LEFT JOIN
that calendar to your data, and use a gaps and island method to put the values into groups. Finally I then aggregate on those groups, getting the MIN
and MAX
date in each one:
USE Sandbox;
GO
CREATE TABLE dbo.YourTable (ID int,
FromDate date,
ToDate date);
INSERT INTO dbo.YourTable
VALUES(1,'20210505','20210510'),
(2,'20210517','20210525');
GO
DECLARE @StartDate date = '20210501',
@EndDate date = '20210528';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT 0 AS I
UNION ALL
SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3), --1000 days
Dates AS(
SELECT DATEADD(DAY, T.I, @StartDate) AS [Date],
T.I
FROM Tally T),
Grps AS(
SELECT D.[Date],
YT.ID,
D.I - ROW_NUMBER() OVER (PARTITION BY ID ORDER BY D.[Date]) AS Grp
FROM Dates D
LEFT JOIN dbo.YourTable YT ON D.[Date] >= YT.FromDate AND D.[Date] <= YT.ToDate)
SELECT ISNULL(MAX(G.ID),0) AS ID,
MIN(G.[Date]) AS FromDate,
MAX(G.[Date]) AS ToDate
FROM Grps G
GROUP BY G.Grp
ORDER BY FromDate ASC;
GO
DROP TABLE dbo.YourTable;
db<>fiddle
Fill Missing Dates In a Date-Sequenced in SQL using Tally Table
You could do this using a Tally Table.
Basically, you use the Tally Table
to generate sequence of dates from @startDate
to @endDate
and CROSS JOIN
it to DISTINCT Item
to generate all Date
-Item
combination. Then, the result will be LEFT-JOIN
ed to tblSales
to achieve the desired output.
SQL Fiddle
DECLARE
@startDate DATE = '20140101',
@endDate DATE = '20140105';
WITH E1(N) AS(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
,E2(N) AS(SELECT 1 FROM E1 a, E1 b)
,E4(N) AS(SELECT 1 FROM E2 a, E2 b)
,Tally(N) AS(
SELECT TOP (DATEDIFF(DAY, @startDate, @endDate) + 1)
ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
FROM E4
)
,CteAllDates(Item, dt) AS(
SELECT x.Item, DATEADD(DAY, N - 1, @startDate)
FROM Tally
CROSS JOIN(
SELECT DISTINCT Item
FROM tblSales
WHERE [Date] BETWEEN @startDate AND @endDate
) AS x
)
SELECT d.*, ts.Sales
FROM CteAllDates d
LEFT JOIN tblSales ts
ON ts.Item = d.Item
AND ts.Date = d.dt
WHERE
ts.[Date] BETWEEN @startDate AND @endDate
ORDER BY d.Item, d.dt
Here is an alternative. Instead of the cascading CTE
s, use sys.columns
to generate the Tally Table
.:
DECLARE
@startDate DATE = '20140101',
@endDate DATE = '20140105';
WITH Tally(N) AS(
SELECT TOP (DATEDIFF(DAY, @startDate, @endDate) + 1)
ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
FROM sys.columns a, sys.columns b
)
,CteAllDates(Item, dt) AS(
SELECT x.Item, DATEADD(DAY, N - 1, @startDate)
FROM Tally
CROSS JOIN(
SELECT DISTINCT Item
FROM tblSales
WHERE [Date] BETWEEN @startDate AND @endDate
) AS x
)
SELECT d.*, ts.Sales
FROM CteAllDates d
LEFT JOIN tblSales ts
ON ts.Item = d.Item
AND ts.Date = d.dt
WHERE
ts.[Date] BETWEEN @startDate AND @endDate
ORDER BY d.Item, d.dt
Result
| Item | dt | Sales |
|---------|------------|--------|
| tenant1 | 2014-01-01 | 100 |
| tenant1 | 2014-01-02 | 100 |
| tenant1 | 2014-01-03 | 100 |
| tenant1 | 2014-01-04 | NULL |
| tenant1 | 2014-01-05 | 100 |
| tenant2 | 2014-01-01 | 100 |
| tenant2 | 2014-01-02 | NULL |
| tenant2 | 2014-01-03 | NULL |
| tenant2 | 2014-01-04 | 100 |
| tenant2 | 2014-01-05 | NULL |
| tenant3 | 2014-01-01 | 100 |
| tenant3 | 2014-01-02 | NULL |
| tenant3 | 2014-01-03 | 100 |
| tenant3 | 2014-01-04 | NULL |
| tenant3 | 2014-01-05 | 100 |
SQL Finding Missing Dates Between Ranges
Try this:
WITH
a AS (
SELECT
SourceSystem, FromDate, ToDate,
LEAD(FromDate) OVER(
PARTITION BY SourceSystem
ORDER BY RunDate
) AS NextDate
FROM #temptable
)
SELECT
SourceSystem,
DATEADD(DAY, 1, ToDate) AS GapBeg,
DATEADD(DAY, -1, NextDate) AS GapFin
FROM a
WHERE
NextDate IS NOT NULL AND
DATEADD(DAY, -2, NextDate) >= ToDate;
Result:
+--------------+------------+------------+
| SourceSystem | GapBeg | GapFin |
+--------------+------------+------------+
| AAP | 2021-06-03 | 2021-06-03 |
| ILG | 2021-06-02 | 2021-06-04 |
+--------------+------------+------------+
db-fiddle
finding missing dates in a time interval (SQL)
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.
SQL - SHOW ALL DATES between two dates
Similar to @DhruvJoshi's answer but using a recursive CTE to generate the dates instead:
DECLARE @MinDate DATE = '20170424',
@MaxDate DATE = '20170430';
WITH allDates AS
(
SELECT @MinDate AS dates
UNION ALL
SELECT DATEADD(DAY, 1, ad.[dates] )
FROM allDates AS ad
WHERE ad.[dates] < @MaxDate
)
SELECT
ISNULL([MessageType].[Name],0) AS [Channel],
dates AS [Time],
COUNT([MessageType].[Name]) AS [Count]
FROM
(
SELECT dates
FROM allDates
) AS T
LEFT JOIN
@table1 ON T.dates=CONVERT(VARCHAR(11), @table1.[OccuredAtUtc], 106)
LEFT JOIN @table2 ON ... = ...
GROUP BY dates,
[MessageType].[Name]
ORDER BY [Time] ASC
How to get missing dates with 0 value in SQL Server?
To see a particular value, the value must come from a row. So to see dates that don't exist on your login table, you must generated them as rows somewhere.
You can use a simple recursive CTE to generate 1 row per day between a particular interval, then use a LEFT JOIN
to join logins that match on that particular day. The ones that don't match will still be displayed, since we are using LEFT JOIN
.
DECLARE @GeneratingDateFrom DATE = DATEADD(DAY, -7, GETDATE())
DECLARE @GeneratingDateTo DATE = GETDATE()
;WITH GeneratedDates AS
(
SELECT
GeneratedDate = @GeneratingDateFrom
UNION ALL
SELECT
GeneratedDate = DATEADD(DAY, 1, G.GeneratedDate)
FROM
GeneratedDates AS G
WHERE
DATEADD(DAY, 1, G.GeneratedDate) < @GeneratingDateTo
)
SELECT
G.GeneratedDate,
count(distinct L.LoginID) as UserCount
FROM
GeneratedDates AS G
LEFT JOIN [Login] AS L ON G.GeneratedDate = CONVERT(date, L.LoginTime)
GROUP BY
G.GeneratedDate
ORDER BY
G.GeneratedDate desc
Related Topics
Get the Number of Affected Rows in a MySQL Update Statement
Insert Data from One Server to Another
Paging with Oracle and SQL Server and Generic Paging Method
SQL Syntax to Pivot Multiple Tables
Enforce a Foreign-Key Constraint to Columns of Same Table
Sqlite3 (Or General SQL) Retrieve Nth Row of a Query Result
Multiple SQL Update Statements in Single Query
Using Different Order by with Union
Moving a Point Along a Path in SQL Server 2008
MySQL Join/Group_Concat Second Table
Checking Whether an Item Does Not Exist in Another Table
Differencebetween Prepared Statements and SQL or Pl/Pgsql Functions, in Terms of Their Purpose
Which SQL Command How to Use to See the Structure of a Table on SQL Server
Find Start and Stop Date for Contiguous Dates in Multiple Rows