Printing a pattern of alphanumeric numbers in Oracle sql - sql

How can I create a temp table in a sql select statement which will hold list of alphanumeric numbers.
Select yearlyQuarters from temp;
2023Q1
2022Q4
2022Q3
2022Q2
2022Q1
2021Q4
2021Q3
2021Q2
2021Q1
......
I tried creating the temp data as
WITH t(n) AS (
SELECT 1900 from dual
UNION ALL
SELECT n+1 from t WHERE n < 3000)
SELECT * FROM t;
I am not sure how can I add the quarter details to the numbers.

You can cross-join the result with the quarters. For example:
WITH t(n) AS (
SELECT 2000 from dual
UNION ALL
SELECT n+1 from t WHERE n < 2003)
SELECT t.n || 'Q' || y.q
FROM t
cross join (
select 1 as q from dual
union all select 2 from dual
union all select 3 from dual
union all select 4 from dual
) y
Result:
T.N||'Q'||Y.Q
-------------
2000Q1
2000Q2
2000Q3
2000Q4
2001Q1
2001Q2
2001Q3
2001Q4
2002Q1
2002Q2
2002Q3
2002Q4
2003Q1
2003Q2
2003Q3
2003Q4
See example at db<>fiddle.

Start with the date 1900-01-01 and repeatedly add 3 months to get the next quarter and then use TO_CHAR to format it:
SELECT TO_CHAR(
ADD_MONTHS(DATE '1900-01-01', 3 * LEVEL - 3),
'YYYY"Q"Q'
) AS yearly_quarters
FROM DUAL
CONNECT BY ADD_MONTHS(DATE '1900-01-01', 3 * LEVEL - 3) <= SYSDATE;
fiddle

Related

Find all Boxes containing files where all the files of the box have expiration date inferior to a DATE

Title was complicated, here the request:
I want to select boxes (box).
All files (file) from a box need to have an expiration date inferior to '01/01/2022';
so does the box.
Here is a request not working, but giving the good JOIN :
select distinct b.code,b.EXPIRATION_DATE
from box b
join COMPONENT_A cpp on cpp.b_Id=b.Id
join COMPONENT_Z cpt on cpt.A_Id=cpp.Id
join file f on f.Z_Id =cpt.Id
where
b.EXPIRATION_DATE < to_date('01.01.2022', 'dd.mm.yyyy')
and f.EXPIRATION_DATE < to_date('01.01.2022', 'dd.mm.yyyy');
This request does not work because it still gives boxes where some files inside have EXPIRATION_DATE > to_date('01.01.2022', 'dd.mm.yyyy')
What I need Example :
Box_1(01/01/2001) : file1(01/01/1999); file2(01/01/2023) NOT SELECTED (because file 2)
Box_2(01/01/2024) : file1(01/01/1999); file2(01/01/2002) NOT SELECTED (box date)
Box_3(01/01/2001) : file1(01/01/1999); file2(01/01/2001) SELECTED (all < 01/01/2022)
I am sure there is a Group by somewhere, file table is a huge table so performance is important.
To check that the maximum file date for each box code is less than 2022-01-01, you can use:
SELECT b.code,
MAX(b.EXPIRATION_DATE) AS expiration_date
FROM box b
join COMPONENT_A cpp on cpp.b_Id=b.Id
join COMPONENT_Z cpt on cpt.A_Id=cpp.Id
join "FILE" f on f.Z_Id =cpt.Id
WHERE b.EXPIRATION_DATE < DATE '2022-01-01'
GROUP BY b.code
HAVING MAX(f.EXPIRATION_DATE) < DATE '2022-01-01';
or, to invert the condition and check that there are no files in a box that are greater than or equal to 2022-01-01, you can use:
SELECT code,
expiration_date
FROM box b
WHERE EXPIRATION_DATE < DATE '2022-01-01'
AND NOT EXISTS (
SELECT 1
FROM COMPONENT_A cpp
join COMPONENT_Z cpt on cpt.A_Id=cpp.Id
join "FILE" f on f.Z_Id =cpt.Id
WHERE cpp.b_Id = b.Id
AND f.EXPIRATION_DATE >= DATE '2022-01-01'
);
(Note: there is a slight difference between the two queries when a box has no files. The first query will not return a box with no files whereas the second query would.)
Which, for the sample data:
CREATE TABLE box (id, code, expiration_date) AS
SELECT 1, 'Box_1', DATE '2001-01-01' FROM DUAL UNION ALL
SELECT 2, 'Box_2', DATE '2024-01-01' FROM DUAL UNION ALL
SELECT 3, 'Box_3', DATE '2001-01-01' FROM DUAL;
CREATE TABLE component_a (id, b_id) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 2 FROM DUAL UNION ALL
SELECT 3, 3 FROM DUAL;
CREATE TABLE component_z (id, a_id) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 2 FROM DUAL UNION ALL
SELECT 3, 3 FROM DUAL;
CREATE TABLE "FILE" (id, z_id, expiration_date) AS
SELECT 1, 1, DATE '1999-01-01' FROM DUAL UNION ALL
SELECT 2, 1, DATE '2023-01-01' FROM DUAL UNION ALL
SELECT 3, 2, DATE '1999-01-01' FROM DUAL UNION ALL
SELECT 4, 2, DATE '2002-01-01' FROM DUAL UNION ALL
SELECT 5, 3, DATE '1999-01-01' FROM DUAL UNION ALL
SELECT 6, 3, DATE '2001-01-01' FROM DUAL;
Both output:
CODE
EXPIRATION_DATE
Box_3
01-JAN-01
db<>fiddle here
If expiration_date columns' datatype is DATE, then don't compare them to strings. Use date literal or TO_DATE function with appropriate format mask. Both options below:
where b.EXPIRATION_DATE < date '2022-01-01'
and f.EXPIRATION_DATE < to_date('01.01.2022', 'dd.mm.yyyy')
As of the query itself, you didn't post sample data (CREATE TABLE and INSERT INTO statements; it is difficult for us to guess what might be written in those 4 tables) so I created my own, simplified test case.
I removed component% tables, but - what might be useful here - is the temp CTE which returns max expiration_date for each box - then you'd use it in the final query.
Something like this:
SQL> with box (code, expiration_date) as
2 (select 'Box_1', date '2001-01-01' from dual union all
3 select 'Box_3', date '2001-01-01' from dual
4 ),
5 tfile (code, cfile, expiration_date) as
6 (select 'Box_1', 'file1', date '1999-01-01' from dual union all
7 select 'Box_1', 'file2', date '2023-01-01' from dual union all
8 --
9 select 'Box_3', 'file1', date '1999-01-01' from dual union all
10 select 'Box_3', 'file2', date '2001-01-01' from dual
11 ),
12 --
13 temp as
14 -- find MAX expiration_date for each box code
15 (select code, max(expiration_date) expiration_date
16 from tfile
17 group by code
18 )
19 select b.code, b.expiration_date
20 from box b join temp f on f.code = b.code
21 where b.expiration_date < date '2022-01-01'
22 and f.expiration_date < date '2022-01-01';
CODE EXPIRATION
----- ----------
Box_3 01.01.2001
SQL>

How to get date and amount between two columns in oracle?

I stuck in a query and table is
TP_DEALNUM DEALDATE MATURITYDATE TP_DEALTYPE TP_NETVALUE
500 20180521 20180524 NBORR 209221500
501 20180525 20180527 NBORR 249221500
502 20180527 20180527 NBORR 747664500
601 20180530 20180531 CBORR 1000000000
602 20180530 20180531 CBORR 500000000
and my query is,
if date difference between dealdate and MATURITYDATE is =2 then I need
DEALDATE TP_NETVALUE
20180525 249221500
20180526 249221500
and if date difference between dealdate and MATURITYDATE is =3 then I need
DEALDATE TP_NETVALUE
20180521 209221500
20180522 209221500
20180523 209221500
and date difference will be increasing till 14
I tried below query,But I'm not getting output
WITH cte AS (
SELECT tp_dealnum,dealdate, maturitydate,TP_DEALTYPE,TP_NETVALUE,
(maturitydate -dealdate) AS DateDiff
FROM tablename)
SELECT case WHEN datediff=2 THEN dealdate+1
WHEN datediff=3 THEN dealdate+1 END AS dealdate_1,
tp_netvalue FROM cte
WHERE DateDiff >= 2
I recommend using a calendar table approach here. Maintain a record of all deal dates which would cover the gaps in your current table, and then join to it:
WITH dates AS (
SELECT date '2018-05-01' AS DEALDATE FROM dual UNION ALL
SELECT date '2018-05-02' FROM dual UNION ALL
SELECT date '2018-05-02' FROM dual UNION ALL
....
SELECT date '2018-05-30' FROM dual UNION ALL
SELECT date '2018-05-31' FROM dual
)
SELECT
d.DEALDATE,
t.TP_NETVALUE
FROM dates d
INNER JOIN tablename t
ON d.DEALDATE BETWEEN t.DEALDATE AND t.MATURITYDATE - 1
WHERE
t.MATURITYDATE - t.DEALDATE BETWEEN 2 AND 3;
Note that this just generates the missing data. If you wanted to incorporate it into some other query/logic, then you would have to do additional work.
The key is generating another CTE with intermittent dates like this-
WITH deal AS
(SELECT 500 TP_DEALNUM,
20180521 DEALDATE,
20180524 MATURITYDATE,
'NBORR' TP_DEALTYPE,
209221500 TP_NETVALUE
FROM dual
UNION ALL
SELECT 501, 20180525, 20180527, 'NBORR', 249221500 FROM dual
UNION ALL
SELECT 502, 20180527, 20180527, 'NBORR', 747664500 FROM dual
UNION ALL
SELECT 601, 20180530, 20180531, 'CBORR', 1000000000 FROM dual
UNION ALL
SELECT 602, 20180530, 20180531, 'CBORR', 500000000 FROM dual),
daterange AS
(SELECT min (dealdate) start_date, max (maturitydate) end_date
FROM deal),
intermittent_date AS
( SELECT start_date + level - 1 AS int_date
FROM daterange
CONNECT BY start_date + level - 1 <= end_date)
SELECT id.int_date dealdate, dl.tp_netvalue
FROM deal dl
INNER JOIN intermittent_date id
ON id.int_date BETWEEN dl.dealdate AND dl.maturitydate - 1
WHERE dl.maturitydate - dl.dealdate = 2;
Output:
DEALDATE TP_NETVALUE
---------- -----------
20180525 249221500
20180526 249221500

Oracle distinct listagg with count

I need to display a comma-separated description per table row but I need the list to be distinct and have counts for some of the descriptions available.
every_description needs_count
---------------------------------
Bred yes
From Vendor yes
Grouped no
Removed yes
Separated no
Weaned yes
So in a day, the description could be something like Bred, Grouped, Weaned and I have this working now using LISTAGG and removing the duplicates using the solution mentioned here but I need to add counts for some of the descriptions like 5 Bred, Grouped, 2 Weaned.
Here's my current query where I'm stuck:
WITH cages AS (
SELECT 1234 AS id FROM DUAL
UNION SELECT 5678 AS id FROM DUAL
UNION SELECT 9012 AS id FROM DUAL
UNION SELECT 3456 AS id FROM DUAL
), cage_comments AS (
SELECT 1234 AS cage_id, 'Bred' AS description, TO_DATE('11/14/2017', 'MM/DD/YYYY') AS event_date FROM DUAL
UNION SELECT 5678 AS cage_id, 'Grouped' AS description, TO_DATE('11/14/2017', 'MM/DD/YYYY') AS event_date FROM DUAL
UNION SELECT 9012 AS cage_id, 'Weaned' AS description, TO_DATE('11/14/2017', 'MM/DD/YYYY') AS event_date FROM DUAL
UNION SELECT 3456 AS cage_id, 'Weaned' AS description, TO_DATE('11/14/2017', 'MM/DD/YYYY') AS event_date FROM DUAL
UNION SELECT 3456 AS cage_id, 'Bred' AS description, TO_DATE('11/02/2017', 'MM/DD/YYYY') AS event_date FROM DUAL
UNION SELECT 3456 AS cage_id, 'Grouped' AS description, TO_DATE('11/14/2017', 'MM/DD/YYYY') AS event_date FROM DUAL
), calendar AS (
SELECT dt
FROM (
SELECT TRUNC(LAST_DAY(TO_DATE(&month || '/01/' || &year, 'MM/DD/YYYY')) - ROWNUM + 1) dt
FROM DUAL CONNECT BY ROWNUM <= 31
)
WHERE dt >= TRUNC(TO_DATE(&month || '/01/' || &year, 'MM/DD/YYYY'), 'MM')
ORDER BY dt ASC
)
SELECT
cal.dt,
(
SELECT
CASE
WHEN COUNT(cc.cage_id) > 0 THEN RTRIM(
REGEXP_REPLACE(
(LISTAGG(cc.description, ',') WITHIN GROUP (ORDER BY cc.description)),
'([^,]*)(,\1)+($|,)',
'\1\3'
),
','
)
ELSE NULL
END
FROM cages c
LEFT JOIN cage_comments cc ON cc.cage_id = c.id
WHERE cc.event_date = cal.dt
) AS description
FROM calendar cal
ORDER BY cal.dt
In short - I'm just having difficulties adding the COUNT for some of the descriptions for that day. In the case above I would like to say 1 Bred for November 2, 2017 and 1 Bred, Grouped, 2 Weaned for November 14, 2017.
Currently you are aggregating all the descriptions (without DISTINCT) and then you remove the duplicate descriptions with a regular expression replace. This is very inefficient - it would be better to select distinct and then apply LISTAGG.
This becomes even more important if you need to add the count. Take the result of your join and GROUP BY description. (In particular, this will take care of DISTINCT). In the SELECT for this aggregate step, include the count. Then join the result to the additional table at the top of your question, and re-write the argument to LISTAGG to include a CASE expression, equal to the count (and a space) when the needs_count value is 'yes'.
You could use:
SELECT
cal.dt,
(
SELECT LISTAGG(CASE WHEN COUNT(*)=1 THEN ''
ELSE CAST(COUNT(*) AS VARCHAR2(10)) || ' ' END || description, ', ')
WITHIN GROUP (ORDER BY description)
FROM cages c
LEFT JOIN cage_comments cc ON cc.cage_id = c.id
WHERE cc.event_date = cal.dt
GROUP BY cc.event_date, cc.description
) AS description
FROM calendar cal
ORDER BY cal.dt;
DBFiddle Demo

How to convert partial dates in Oracle SQL

I have 2 date columns in 2 diff tables that I need to compare, both varchar2 type. Both columns have partial and full dates based on the data.
T1:
ID Partial_date1
1 19-DEC-2016
2 06-MAY-2015
3 2016
4
5 AUG-2016
6 16-NOV-2015 00:00
7 01-JAN-2016
T2:
ID Partial_date2
1 09-JAN-2016
2 2016
3 SEP-2015
4
5 23-MAR-2016 00:00
6 15-MAY-2015
7
I want to search for all records that have full dates (as it is not possible to convert partial dates), to select only the records with full dates, I have used length >10. Here is the SQL I wrote but does not seem to be working.
select t1.id from t1, t2
where t1.id =t2.id
and length(t1.partial_date1)>10
and length(t2.partial_date2)>10
and to_date(t1.partial_date1,'DD-MON-YYYY') > to_date(t2.partial_date2,'DD-MON-YYYY')
I either get an error - ORA-01830: date format picture ends before converting entire input string
or literal does not match format string.
What am I doing wrong? how do I get the right results?
It seems you consider a date complete when the string starts with DD-MMM-YYYY. You can use REGEXP_LIKE to find such rows:
where regexp_like(partial_date, '^[[:digit:]]{2}-[[:upper:]]{3}-[[:digit:]]{4}')
(You may want to adjust the pattern according to your needs, e.g. replace [[:upper:]] with [[:alpha:]].)
In order to convert a date containing a textual month you should use TO_DATE with a language parameter:
to_date(partial_date, 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=AMERICAN')
A possible query:
select tt1.id
from
(
select
id,
to_date(substr(partial_date1, 1, 11), 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=AMERICAN')
as dt
from t1
where regexp_like(partial_date1, '^[[:digit:]]{2}-[[:upper:]]{3}-[[:digit:]]{4}')
) tt1
join
(
select
id,
to_date(substr(partial_date2, 1, 11), 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=AMERICAN')
as dt
from t2
where regexp_like(partial_date2, '^[[:digit:]]{2}-[[:upper:]]{3}-[[:digit:]]{4}')
) tt2 on tt2.id = tt1.id and tt2.dt < tt1.dt;
However, keep in mind that you are still dealing with strings. Conversion on "dates" like these would fail and crash your query:
30-FEB-2017
01-YAN-2017
99-XXX-9999
So depending on the data quality your best bet may be to write a PL/SQL function in order to catch conversion errors.
When you write condition in where clause oracle may use it as access predicate. And it function to_date(t1.partial_date1,'DD-MON-YYYY') is used to any row.
That's why you get an error.
I see two ways:
First way is use a subquery to get a shrunk dataset, substr only 10 symbols and then convert it
with t1(id,partial_date1) as
(
select 1,'19-DEC-2016' from dual union all
select 2,'06-MAY-2015' from dual union all
select 3, '2016' from dual union all
select 4,'' from dual union all
select 5,'AUG-2016' from dual union all
select 6,'16-NOV-2015 00:00' from dual union all
select 7, '01-JAN-2016' from dual
), t2(id,partial_date2) as
(
select 1,'09-JAN-2016' from dual union all
select 2,'2016' from dual union all
select 3,'SEP-2015' from dual union all
select 4,'' from dual union all
select 5,'23-MAR-2016 00:00' from dual union all
select 6,'15-MAY-2015' from dual union all
select 7,'' from dual
)
select *
from
(select
t1.id,
partial_date1,
partial_date2
from
t1, t2
where
t1.id =t2.id
and length(t1.partial_date1) > 10
and length(t2.partial_date2) > 10
and rownum > 0)
where
to_date(substr(partial_date1,1,10),'DD-MON-YYYY') > to_date(substr(partial_date2,1,10),'DD-MON-YYYY');
/
The second way is to explicitly convert any format to properly one
with t1(id,partial_date1) as (
select 1,'19-DEC-2016' from dual union all
select 2,'06-MAY-2015' from dual union all
select 3, '2016' from dual union all
select 4,'' from dual union all
select 5,'AUG-2016' from dual union all
select 6,'16-NOV-2015 00:00' from dual union all
select 7, '01-JAN-2016' from dual)
,t2(id,partial_date2) as (
select 1,'09-JAN-2016' from dual union all
select 2,'2016' from dual union all
select 3,'SEP-2015' from dual union all
select 4,'' from dual union all
select 5,'23-MAR-2016 00:00' from dual union all
select 6,'15-MAY-2015' from dual union all
select 7,'' from dual)
select * from(
select t1.id,
case
when regexp_like(t1.partial_date1,'\d{1,2}-\w{3}-\d{4} \d{1,2}:\d{2}') then to_date(t1.partial_date1,'dd-MON-yyyy HH24:MI')
when regexp_like(t1.partial_date1,'\d{1,2}-\w{3}-\d{4}') then to_date(t1.partial_date1,'dd-MON-yyyy')
when regexp_like(t1.partial_date1,'\w{3}-\d{4}') then to_date(t1.partial_date1,'MON-yyyy')
when regexp_like(t1.partial_date1,'\d{4}') then to_date(t1.partial_date1,'yyyy')
end as pd1,
case
when regexp_like(t2.partial_date2,'\d{1,2}-\w{3}-\d{4} \d{1,2}:\d{2}') then to_date(t2.partial_date2,'dd-MON-yyyy HH24:MI')
when regexp_like(t2.partial_date2,'\d{1,2}-\w{3}-\d{4}') then to_date(t2.partial_date2,'dd-MON-yyyy')
when regexp_like(t2.partial_date2,'\w{3}-\d{4}') then to_date(t2.partial_date2,'MON-yyyy')
when regexp_like(t2.partial_date2,'\d{4}') then to_date(t2.partial_date2,'yyyy')
end as pd2
from t1,t2
where t1.id = t2.id)
where pd1 > pd2
/
case
when PurchaseDate = '0' then NULL
when right(PurchaseDate, 4) = '0000' then convert(date, left(PurchaseDate,4) + '1231', 112)
when RIGHT(PurchaseDate, 2) = '00' then DATEADD(day,-1,DATEADD(month,cast(left(RIGHT(Purchasedate,4),2) AS INT),DATEADD(year,cast(LEFT(Purchasedate, 4) AS int)-1900,0)))
else convert(date, ltrim(rtrim(cast(PurchaseDate as varchar(50)))), 112)
end
Apologize. This is from my phone. This does a few things. It assumes different formats plus it counters for missing days and months.
Hope this helps.

SELECT any FROM system

Can any of these queries be done in SQL?
SELECT dates FROM system
WHERE dates > 'January 5, 2010' AND dates < 'January 30, 2010'
SELECT number FROM system
WHERE number > 10 AND number < 20
I'd like to create a generate_series, and that's why I'm asking.
I assume you want to generate a recordset of arbitrary number of values, based on the first and last value in the series.
In PostgreSQL:
SELECT num
FROM generate_series (11, 19) num
In SQL Server:
WITH q (num) AS
(
SELECT 11
UNION ALL
SELECT num + 1
FROM q
WHERE num < 19
)
SELECT num
FROM q
OPTION (MAXRECURSION 0)
In Oracle:
SELECT level + 10 AS num
FROM dual
CONNECT BY
level < 10
In MySQL:
Sorry.
Sort of for dates...
Michael Valentine Jones from SQL Team has an AWESOME date function
Check it out here:
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=61519
In Oracle
WITH
START_DATE AS
(
SELECT TO_CHAR(TO_DATE('JANUARY 5 2010','MONTH DD YYYY'),'J')
JULIAN FROM DUAL
),
END_DATE AS
(
SELECT TO_CHAR(TO_DATE('JANUARY 30 2010','MONTH DD YYYY'),'J')
JULIAN FROM DUAL
),
DAYS AS
(
SELECT END_DATE.JULIAN - START_DATE.JULIAN DIFF
FROM START_DATE, END_DATE
)
SELECT TO_CHAR(TO_DATE(N + START_DATE.JULIAN, 'J'), 'MONTH DD YYYY')
DESIRED_DATES
FROM
START_DATE,
(
SELECT LEVEL N
FROM DUAL, DAYS
CONNECT BY LEVEL < DAYS.DIFF
)
If you want to get the list of days, with a SQL like
select ... as days where date is between '2010-01-20' and '2010-01-24'
And return data like:
days
----------
2010-01-20
2010-01-21
2010-01-22
2010-01-23
2010-01-24
This solution uses no loops, procedures, or temp tables. The subquery generates dates for the last thousand days, and could be extended to go as far back or forward as you wish.
select a.Date
from (
select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY as Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) a
where a.Date between '2010-01-20' and '2010-01-24'
Output:
Date
----------
2010-01-24
2010-01-23
2010-01-22
2010-01-21
2010-01-20
Notes on Performance
Testing it out here, the performance is surprisingly good: the above query takes 0.0009 sec.
If we extend the subquery to generate approx. 100,000 numbers (and thus about 274 years worth of dates), it runs in 0.0458 sec.
Incidentally, this is a very portable technique that works with most databases with minor adjustments.
Not sure if this is what you're asking, but if you are wanting to select something not from a table, you can use 'DUAL'
select 1, 2, 3 from dual;
will return a row with 3 columns, contain those three digits.
Selecting from dual is useful for running functions. A function can be run with manual input instead of selecting something else into it. For example:
select some_func('First Parameter', 'Second parameter') from dual;
will return the results of some_func.
In SQL Server you can use the BETWEEN keyword.
Link:
http://msdn.microsoft.com/nl-be/library/ms187922(en-us).aspx
You can select a range by using WHERE and AND WHERE. I can't speak to performance, but its possible.
The simplest solution to this problem is a Tally or Numbers table. That is a table that simply stores a sequence of integers and/or dates
Create Table dbo.Tally (
NumericValue int not null Primary Key Clustered
, DateValue datetime NOT NULL
, Constraint UK_Tally_DateValue Unique ( DateValue )
)
GO
;With TallyItems
As (
Select 0 As Num
Union All
Select ROW_NUMBER() OVER ( Order By C1.object_id ) As Num
From sys.columns as c1
cross join sys.columns as c2
)
Insert dbo.Tally(NumericValue, DateValue)
Select Num, DateAdd(d, Num, '19000101')
From TallyItems
Where Num
Once you have that table populated, you never need touch it unless you want to expand it. I combined the dates and numbers into a single table but if you needed more numbers than dates, then you could break it into two tables. In addition, I arbitrarily filled the table with 100K rows but you could obviously add more. Every day between 1900-01-01 to 9999-12-31 takes about 434K rows. You probably won't need that many but even if you did, the storage is tiny.
Regardless, this is a common technique to solving many gaps and sequences problems. For example, your original queries all ran in less than tenth of a second. You can also use this sort of table to solve gaps problems like:
Select NumericValue
From dbo.Tally
Left Join MyTable
On Tally.NumericValue = MyTable.IdentityColumn
Where Tally.NumericValue Between SomeLowValue And SomeHighValue