Get all the missing dates in a sequence of dates - sql

I am trying to get all the missing dates in a sequence of dates which are in an ascending order. How can I do it using a simple sql without using any functions or udfs.
Input :-
2016-09-01
2016-09-02
2016-09-05
2016-09-10
Output :-
2016-09-03
2016-09-04
2016-09-06
2016-09-07
2016-09-08
2016-09-09
What I have tried?
select start, stop
from
(
select m.x + 1 as start,
(select min(x) - 1 from X as x where x.x > m.x) as stop
from X as m
left outer join X as r
on m.x = r.x - 1
where r.x is null
) as x
where stop is not null;

create a new table and insert 365 rows numbered 1-365
(alternately use a table that already has more than 365 rows and use rownum or similar construct to get unique integers)
Convert your dates to integers (in Oracle use something like TO_CHAR( mydate, 'ddd' ))
join these two list together in your query to find the appropriate set (overlap, missing etc.)
convert back to dates

Related

Dynamically generating date range starts in SQL

Imagine you have a set of dates. You want any date which is within X days of the lowest date to be "merged" into that date. Then you want to repeat until you have merged all date points.
For example:
ID
DatePoints
1
2023-01-01
2
2023-01-02
3
2023-01-12
4
2023-01-21
5
2023-02-01
6
2023-02-02
7
2023-03-01
If you applied this rule to this data using 10 days as your X, you would end up with this output:
DateRangeStarts
2023-01-01
2023-01-12
2023-02-01
2023-03-01
IDs 1 and 2 into range 1, IDs 3 and 4 into range 2, IDs 5 and 6 into range 3, and ID 7 into range 4.
Is there any way to do this without a loop? Answer can work in SQL Server or BigQuery. Thanks
You could consider something like the following. It's not pretty and I'm not at all confident it is the best solution, but I do think it works. Maybe it's a good starting point for you to work from.
WITH cte AS
(
SELECT min(datepoint) datepoint
FROM test
UNION ALL
SELECT min(t.datepoint) OVER() datepoint
FROM test t CROSS APPLY (SELECT max(cte.datepoint) OVER() md FROM cte) c
WHERE t.datepoint > DATEADD(DAY, 10, c.md)
)
SELECT distinct datepoint
FROM cte
ORDER BY datepoint
(You might want to change the > to a >=, depending on what counts as within X days.)
The basic idea is to get the minimum date from your table into the cte, then recursively get the minimum date from your table that is bigger than the current maximum date in the cte + X days.
It gets messy because of the limitations SQL Server places on recursive CTEs. They can't be used in subqueries, with normal OUTER JOINs, or with aggregate functions. Therefore, I use CROSS APPLY and the window versions of min/max. This gets the correct result, but multiple times, so I'm forced to use DISTINCT to clean it up afterward.
Depending on your data, it might be better to do a loop anyway, but I think this is an option to consider.
Here's a Fiddle of it working.

Is there a way to auto-fill dates in SQL

My first problem is that I have been adding dates into the database by hand-creating them and it is labor-intensive. I am looking for a way to automatically add dates like you can do in Excel.
I have 3 columns in use.
Date (YYYY-MM-DD HH:mm:ss)
TenantID (Name)
Count (Count of tenant ID)
This is currently what I have been using to add the dates into the database.
INSERT INTO [dbo].[acms_data]
([Time]
,[TenantId]
,[CallingService]
,[PolicyList]
,[PolicyInstanceList])
VALUES
('2019-11-22 00:00:00'
,'4754F795-2FB9-4647-B28F-2CF2412F0BA2'
,'s1'
,'p1,p2,p3,p4'
,'pi1,pi2,pi3,pi4')
I would like to know a way where instead of copy pasting each date manually, if there is a way to instead add the consecutive dates all the way to a certain range.
Example:
11/22/01 05:30:20
.
.
.
.
11/30/01 05:30:20
I am new to SQL and I do not know what I am doing but I am willing to try all suggestion
If I understand correctly!
If you want to get the specific dates between rage (N) then you can use the below query.
DECLARE #TEST DATE
SET #TEST = '2019-11-22' -- your date
DECLARE #NoOfDay INT = 9 -- No of days you want
;WITH N(N)
AS
(
SELECT 1
FROM (VALUES(1),(1),(1),(1),(1),(1)) M(N)
), demo(N) AS (SELECT ROW_NUMBER()OVER(ORDER BY N.N)FROM N,N a)
SELECT TOP(day(EOMONTH(#TEST)))
N day, dateadd(d,N-1, #TEST) date
FROM demo
WHERE N <= #NoOfDay
Output:
day date
1 2019-11-22
2 2019-11-23
3 2019-11-24
4 2019-11-25
5 2019-11-26
6 2019-11-27
7 2019-11-28
8 2019-11-29
9 2019-11-30

Find list of dates in a table closest to specific date from different table.

I have a list of unique ID's in one table that has a date column. Example:
TABLE1
ID Date
0 2018-01-01
1 2018-01-05
2 2018-01-15
3 2018-01-06
4 2018-01-09
5 2018-01-12
6 2018-01-15
7 2018-01-02
8 2018-01-04
9 2018-02-25
Then in another table I have a list of different values that appear multiple times for each ID with various dates.
TABLE 2
ID Value Date
0 18 2017-11-28
0 24 2017-12-29
0 28 2018-01-06
1 455 2018-01-03
1 468 2018-01-16
2 55 2018-01-03
3 100 2017-12-27
3 110 2018-01-04
3 119 2018-01-10
3 128 2018-01-30
4 223 2018-01-01
4 250 2018-01-09
4 258 2018-01-11
etc
I want to find the value in table 2 that is closest to the unique date in table 1.
Sometimes table 2 does contain a value that matches the date exactly and I have had no problem in pulling through those values. But I can't work out the code to pull through the value closest to the date requested from table 1.
My desired result based on the examples above would be
ID Value Date
0 24 2017-12-29
1 455 2018-01-03
2 55 2018-01-03
3 110 2018-01-04
4 250 2018-01-09
Since I can easily find the ID's with an exact match, one thing I have tried is taking the ID's that don't have an exact date match and placing them with their corresponding values into a temporary table. Then trying to find the values where I need the closest possible match, but it's here that I'm not sure where to begin on the coding of that.
Apologies if I'm missing a basic function or clause for this, I'm still learning!
The below would be one method:
WITH Table1 AS(
SELECT ID, CONVERT(date, datecolumn) DateColumn
FROM (VALUES (0,'20180101'),
(1,'20180105'),
(2,'20180115'),
(3,'20180106'),
(4,'20180109'),
(5,'20180112'),
(6,'20180115'),
(7,'20180102'),
(8,'20180104'),
(9,'20180225')) V(ID, DateColumn)),
Table2 AS(
SELECT ID, [value], CONVERT(date, datecolumn) DateColumn
FROM (VALUES (0,18 ,'2017-11-28'),
(0,24 ,'2017-12-29'),
(0,28 ,'2018-01-06'),
(1,455,'2018-01-03'),
(1,468,'2018-01-16'),
(2,55 ,'2018-01-03'),
(3,100,'2017-12-27'),
(3,110,'2018-01-04'),
(3,119,'2018-01-10'),
(3,128,'2018-01-30'),
(4,223,'2018-01-01'),
(4,250,'2018-01-09'),
(4,258,'2018-01-11')) V(ID, [Value],DateColumn))
SELECT T1.ID,
T2.[Value],
T2.DateColumn
FROM Table1 T1
CROSS APPLY (SELECT TOP 1 *
FROM Table2 ca
WHERE T1.ID = ca.ID
ORDER BY ABS(DATEDIFF(DAY, ca.DateColumn, T1.DateColumn))) T2;
Note that if the difference is days is the same, the row returned will be random (and could differ each time the query is run). For example, if Table had the date 20180804 and Table2 had the dates 20180803 and 20180805 they would both have the value 1 for ABS(DATEDIFF(DAY, ca.DateColumn, T1.DateColumn)). You therefore might need to include additional logic in your ORDER BY to ensure consistent results.
dude.
I'll say a couple of things here for you to consider, since SQL Server is not my comfort zone, while SQL itself is.
First of all, I'd join TABLE1 with TABLE2 per ID. That way, I can specify on my SELECT clause the following tuple:
SELECT ID, Value, DateDiff(d, T1.Date, T2.Date) qt_diff_days
Obviously, depending on the precision of the dates kept there, rather they have times or not, you can change the date field on DateDiff function.
Going forward, I'd also make this date difference an absolute number (to resolve positive / negative differences and consider only the elapsed time).
After that, and that's where it gets tricky because I don't know the SQL Server version you're using, but basically I'd use a ROW_NUMBER window function to rank all my lines per difference. Something like the following:
SELECT
ID, Value, Abs(DateDiff(d, T1.Date, T2.Date)) qt_diff_days,
ROW_NUMBER() OVER(PARTITION BY ID ORDER BY Abs(DateDiff(d, T1.Date, T2.Date)) ASC) nu_row
ROW_NUMBER (Transact-SQL)
Numbers the output of a result set. More specifically, returns the sequential number of a row within a partition of a result set, starting at 1 for the first row in each partition.
If you could run ROW_NUMBER properly, you should notice the query will rank it's data per ID, starting with 1 and increasing this ranking by it's difference between both dates, reseting it's rank to 1 when ID changes.
After that, all you need to do is select only those lines where nu_row equals to 1. I'd use a CTE to that.
WITH common_table_expression (Transact-SQL)
Specifies a temporary named result set, known as a common table expression (CTE).

SQL Server: run function for each row based on the provided row value

I am still learning SQL and I was wandering how can I pass a row value into a function in a cross apply query.
Assuming that this is my first table
SELECT
[Project], [emp_id],
[Allocation_start], [Allocation_end]
FROM
First_table
I have a function that takes 2 arguments - start and end date, and returns dates split by the week intervals...
Example :
select * from [udf_GetIntervals]('2017-01-01','2017-01-30')
Data:
dt_start dt_end
----------------------
2016-12-26 2017-01-01
2017-01-02 2017-01-08
2017-01-09 2017-01-15
2017-01-16 2017-01-22
2017-01-23 2017-01-29
2017-01-30 2017-02-05
What I did was I run the mentioned function with dates starting from the 1st July to the 30th December and stored it in virtual table and then used cross apply with that table.
select *
from [First_table]
cross apply
(select * from #temp) b
Which works, but it works independently of the first_tables start and end date always returning all weeks per 1 project record from first table.
I was wondering, how can i do this, using first_tables start_date and end_date values, so that my cross apply returns only records with the week_intervals contained in the rows start/end date.
I would appreciate any hints on the matter.
You can use APPLY do perform row-wise actions. CROSS APPLY behaves similar to an INNER JOIN, while OUTER APPLY is rather like a LEFT JOIN.
A Table-Valued-Function returns a table. You must provide an alias and inlcude this resultset into your column list:
SELECT [Project]
,[emp_id]
,[Allocation_start]
,[Allocation_end]
,WeekDates.*
From First_table
CROSS APPLY dbo.[udf_GetIntervals]([Allocation_start],[Allocation_end]) AS WeekDates
select *
from First_table cross apply [udf_GetIntervals]([Allocation_start],[Allocation_end])

SQL of a daterange without empty spaces

I need to generate a diagram out of data from a table. This table has the following date:
Timestamp | Value
01-20-2013| 5
01-21-2013| 7
01-22-2013| 3
01-25-2013| 5
As you can see not every date has a value. If I put that into a diagram it looks weird. Dates are used for the X-axis. As 01-23-2013 and 01-24-2013 is missing this values are either not printed in the diagram (looks weird) or the are printed put the line of the diagram goes from 3 directly to 5 and not to 0 as it should.
Is there a way via SQL to select the data so that it looks like this:
Timestamp | Value
01-20-2013| 5
01-21-2013| 7
01-22-2013| 3
01-23-2013| 0
01-24-2013| 0
01-25-2013| 5
Any help is appreciated!
Regards,
Alex
Edit: I had no clue that the database engine was that important. This is running on a MySQL 5 Database (not sure about the complete version string).
There are various ways to do this, depending on the database. Date functions are notoriously database independent.
Here is an approach using a "driver" table with all dates and to use this for a left outer join:
select driver.timestamp, coalesce(t.value, 0) as value
from (select distinct timestamp + n.n as timestamp
from t cross join
(select 0 as n union all select 1 union all select 2 union all select 3
)
) driver left outer join
t;
This version assumes that there are gaps of no more than three days.
In some databases, you can construct the list of dates using a recursive CTE. Such an approach would handle gaps of any size.