SQL Server 2008 R2 - convert all datetime parts (as ints) into a datetime column - tsql

I have a table that has the datetime pieces (year, month, day, hour, minute, second, millisecond) stored as integers. I'd like to concatenate them into a single datetime column.
I've tried various approaches but none work - there seems to be no simple way to put these items together?

You can convert each part to a varchar and concatenate them together in the format of an ISO datetime string. Then use Convert to convert the string to a DateTime.
Here is an example. You would need to replace each hard coded integer with the name of the column from your table.
SELECT CONVERT(DATETIME, CAST(2016 AS VARCHAR(4)) -- year
+ '-' + CAST('0' + CAST(8 AS VARCHAR(2)) AS VARCHAR(2)) -- month
+ '-' + RIGHT('0' + CAST(13 AS VARCHAR(2)), 2) -- day of month
+ 'T' + RIGHT('0' + CAST(16 AS VARCHAR(2)), 2) -- hours (I assume its military time (24 hours))
+ ':' + RIGHT('0' + CAST(32 AS VARCHAR(2)), 2) -- minutes
+ ':' + RIGHT('0' + CAST(07 AS VARCHAR(2)), 2) -- seconds
+ '.' + RIGHT('000' + CAST(64 AS VARCHAR(3)), 3)) AS MyDate -- milliseconds
FROM yourTable
Or with column names (assumed)
SELECT CONVERT(DATETIME, CAST(yt.Year AS VARCHAR(4)) -- year
+ '-' + CAST('0' + CAST(yt.Month AS VARCHAR(2)) AS VARCHAR(2)) -- month
+ '-' + RIGHT('0' + CAST(yt.Day AS VARCHAR(2)), 2) -- day of month
+ 'T' + RIGHT('0' + CAST(yt.Hours AS VARCHAR(2)), 2) -- hours (I assume its military time (24 hours))
+ ':' + RIGHT('0' + CAST(yt.Minutes AS VARCHAR(2)), 2) -- minutes
+ ':' + RIGHT('0' + CAST(yt.Seconds AS VARCHAR(2)), 2) -- seconds
+ '.' + RIGHT('000' + CAST(yt.Milliseconds AS VARCHAR(3)), 3)) AS MyDate -- milliseconds
FROM yourTable yt
One more note. Microsoft recommends that you use DateTime2 instead of DateTime to persist date time values starting with Sql Server 2008 (which you tagged in your question).

Prior to sql server 2012, you can use a series of nested DATEADD() functions to mimic DATETIMEFROMPARTS() function
Create and populate sample data (In your next question, please save us this step)
DECLARE #T as table
(
cYear int,
cMonth int,
cDay int,
cHour int,
cMinute int,
cSecond int,
cMillisecond int
)
INSERT INTO #T VALUES(2016, 6, 22, 16, 34, 25, 3)
The query:
SELECT *,
DATEADD(MILLISECOND, cMillisecond,
DATEADD(SECOND, cSecond,
DATEADD(MINUTE, cMinute,
DATEADD(HOUR, cHour,
DATEADD(DAY, cDay -1,
DATEADD(MONTH, cMonth - 1,
DATEADD(YEAR, cYear - 2000, '2000-01-01')
)
)
)
)
)
) As TheDate
FROM #T
Results:
cYear cMonth cDay cHour cMinute cSecond cMillisecond TheDate
----- ------ ---- ----- ------- ------- ------------- -----------------------
2016 6 22 16 34 25 3 2016-06-22 16:34:25.003
Note that the base date I'm using is January 1st 2000, therefor you need to subtract 2000 from the year, 1 from the month and 1 from the days.

Related

How can i get a week range for a given month in Postgress

This is my current implementation
SELECT
date_trunc('month', do_date::date)::date as starting_of_the_month,
(date_trunc('month', do_date::date) + interval '1 month' - interval '1 day')::date as ending_of_the_month,
case when 1 + FLOOR((EXTRACT(DAY FROM do_date) - 1) / 7) = 1
THEN date_trunc('week', do_date)::date || ' - ' ||
(date_trunc('week', do_date) + '6 days') ::date end as week1,
case when 1 + FLOOR((EXTRACT(DAY FROM do_date) - 1) / 7) = 2
THEN date_trunc('week', do_date)::date || ' - ' ||
(date_trunc('week', do_date) + '6 days') ::date end as week2,
case when 1 + FLOOR((EXTRACT(DAY FROM do_date) - 1) / 7) = 3
THEN date_trunc('week', do_date)::date || ' - ' ||
(date_trunc('week', do_date) + '6 days') ::date end as week3,
case when 1 + FLOOR((EXTRACT(DAY FROM do_date) - 1) / 7) = 4
THEN date_trunc('week', do_date)::date || ' - ' ||
(date_trunc('week', do_date) + '6 days') ::date end as week4,
case when 1 + FLOOR((EXTRACT(DAY FROM do_date) - 1) / 7) = 5
THEN date_trunc('week', do_date)::date || ' - ' ||
(date_trunc('week', do_date) + '6 days') ::date end as week5
FROM sales_dos
WHERE date_trunc('month', do_date::date)::date >= '2021-02-01' AND date_trunc('month', do_date::date)::date < '2021-02-28'
This is my output for now :
I want the output to display as below :
Week 1 : 2021-02-01 - 2021-02-07
Week 2 : 2021-02-08 - 2021-02-14
Week 3 : 2021-02-15 - 2021-02-21
Week 4 : 2021-02-22 - 2021-02-28
Week 5 : -
Here is another way to do it (example for January 2021).
with
t as (select date_trunc('month', '2021-03-11'::date) as aday), -- any date in Jan-2021
s as
(
select d::date, d::date + 6 ed, extract('isodow' from d) wd
from t, generate_series (aday, aday + interval '1 month - 1 day', interval '1 day') d
)
select format ('Week %s', extract(day from d)::integer / 7 + 1) as weekname, d, ed
from s
where wd = 1;
So what you are looking for is a hybrid ISO with standard Calendar. You are taking the ISO week starting and ending period, but instead of all weeks being exactly 7 days you potentially truncate the 1st and/or last weeks.
The change to need for this is not actually extensive. For initial query returns the in the ISO week begin date instead of the 1st of the month. Then the main query then checks for week 1 and if so produces the 1st of the month. The only twist is determining the ISO week begin date. For this I've just included a function I have had for some time specifically for that. The change to the week_days function are marked --<<<.
create or replace function iso_first_of_week(date_in date)
returns date
language sql
immutable strict
/*
Given a date return the 1st day of the week according to ISO-8601.
I.e. Return the Date if it is Monday otherwise return the preceding Monday
*/
AS $$
with wk_adj(l_days) as (values (array[0,1,2,3,4,5,6]))
select date_in - l_days[ extract (isodow from date_in)::integer ]
from wk_adj;
$$;
create or replace
function week_dates( do_date_in date)
returns table (week_num integer, first_date date, last_date date)
language sql
immutable strict
as $$
with recursive date_list(week_num,first_date,terminate_date) as
( select 1
, iso_first_of_week(do_date_in)::timestamp --<<<
, (date_trunc('month', do_date_in) + interval '1 month' - interval '1 day')::timestamp
union all
select week_num+1, (first_date+interval '7 day'), terminate_date
from date_list
where first_date+interval '6 day' < terminate_date::timestamp
)
select week_num
, case when week_num = 1 --<<<
then date_trunc('month', do_date_in)::date --<<<
else first_date::date --<<<
end --<<<
, case when (first_date+interval '6 day')::date > terminate_date
then terminate_date::date
else (first_date+interval '6 day')::date
end last_date
from date_list;
$$;
---------- Original Reply
You can use a recursive query CTE to get the week number and first date for each week of the month specified. The main query calculates the ending date, shorting the last if necessary. Then wrap that into a SQL function to return the week number and date range for each week. See example.
create or replace
function week_dates( do_date_in date)
returns table (ween_num integer, first_date date, last_date date)
language sql
immutable strict
as $$
with recursive date_list(week_num,first_date,terminate_date) as
( select 1
, date_trunc('month', do_date_in)::timestamp
, (date_trunc('month', do_date_in) + interval '1 month' - interval '1 day')::timestamp
union all
select week_num+1, (first_date+interval '7 day'), terminate_date
from date_list
where first_date+interval '6 day' < terminate_date::timestamp
)
select week_num
, first_date::date
, case when (first_date+interval '6 day')::date > terminate_date
then terminate_date::date
else (first_date+interval '6 day')::date
end last_date
from date_list;
$$;
Response to: "How can i put the output in a single row with week1, week2, week3, week4 and week5". This is essentially the initial output that did not satisfy what you wanted. The term for this type action is PIVOT and is generally understood. It stems from transforming row orientation to column orientation. It is not overly difficult but it is messy.
IMHO this is something that belongs in the presentation layer and is not suitable for SQL. After all you are rearranging the data structure for presentation purposes. Let the database server use its natural format, use the presentation layer to reformat. This allows reuse of the queries instead of rewriting when the presentation is changed or another view of the same data is required.
If you actually want this then just use your initial query, or see the answer from
#Bohemian. However the below shows how this issue can be handled with just SQL (assuming the function week_dates was created).
select week1s
, case when week5e is null
then week4e
else week5e
end "end of month"
, week1s || ' - ' || week1e
, week2s || ' - ' || week2e
, week3s || ' - ' || week3e
, week4s || ' - ' || week4e
, week5s || ' - ' || week5e
from ( select max(case when (week_num=1) then first_date else NULL end) as week1s
, max(case when (week_num=1) then last_date else NULL end) as week1e
, max(case when (week_num=2) then first_date else NULL end) as week2s
, max(case when (week_num=2) then last_date else NULL end) as week2e
, max(case when (week_num=3) then first_date else NULL end) as week3s
, max(case when (week_num=3) then last_date else NULL end) as week3e
, max(case when (week_num=4) then first_date else NULL end) as week4s
, max(case when (week_num=4) then last_date else NULL end) as week4e
, max(case when (week_num=5) then first_date else NULL end) as week5s
, max(case when (week_num=5) then last_date else NULL end) as week5e
from week_dates(current_date)
) w ;
As before I have wrapped the above in a SQL function and provide an example here.
I would first simplify to:
extract(day from do_date)::int / 7 + 1 as week_in_month
then pivot on that using crosstab().

SQL Server - WHERE Date Range & GROUP BY MonthName

I have 2 same queries (to return "MonthName Year" and count) as below, but only the date range in the WHERE condition is different. Query 1 gets only the June month count, while Query 2 gets count from Apr to Jul, where the Jun month count (in Query 2) is not same as June month count from Query 1. Please advise.
Query 1:
SELECT DATENAME(MONTH, SubmissionDate) + ' ' + DateName(Year, SubmissionDate) AS MonthNumber, COUNT(1) AS InquiryCount
, Cast(Datename(MONTH,SubmissionDate) + ' ' + Datename(YEAR,SubmissionDate) AS DATETIME) AS tmp
FROM [dbo].[InvestigationDetails] (nolock)
WHERE SubmissionDate>= '06/01/2016'
AND SubmissionDate <= '06/30/2016'
GROUP BY DATENAME(MONTH, SubmissionDate) + ' ' + DateName(Year, SubmissionDate), DateName(Year, SubmissionDate)
ORDER BY tmp ASC
Query 2:
SELECT DATENAME(MONTH, SubmissionDate) + ' ' + DateName(Year, SubmissionDate) AS MonthNumber, DateName(Year, SubmissionDate), COUNT(1) AS InquiryCount
, Cast(Datename(MONTH,SubmissionDate) + ' ' + Datename(YEAR,SubmissionDate) AS DATETIME) AS tmp
FROM [dbo].[InvestigationDetails] (nolock)
WHERE SubmissionDate>= '04/01/2016'
AND SubmissionDate <= '07/31/2016'
GROUP BY DATENAME(MONTH, SubmissionDate) + ' ' + DateName(Year, SubmissionDate), DateName(Year, SubmissionDate)
ORDER BY tmp ASC
Thanks,
Jay
SubmissionDate must be of type DATETIMEand thus, you are missing all values for your last day, 06/30/2016, since this equates to 06/30/2016 00:00:00. This means any records that have SubmissionDate with a time > 00:00:00 on 6/30/2016 will be excluded. For example, 6/30/2016 12:44:22 wouldn't be included in your results with your current logic.
Use one of these instead:
AND SubmissionDate < '07/01/2016'
AND SubmissionDate <= '06/30/2016 23:59:59.999'
The first method is preferred since you will get all records before 7/1/2016, which includes 6/30/2016 23:59:59.999. Of course, you should be aware of how precise DATETIME can be in SQL Server. Run the code below to see what I mean.
declare #dt datetime2 = getdate()
select #dt --more precise with datetime2
select getdate() --not as precise

TSQL SELECT data within range of year and month

I'm trying to query data within a range of start year and month and end year and month. But SQL returnes onty the year and the month chosen. Can anyone identify the problem with my approach.
Thanks!
ALTER PROCEDURE xxx
(#JaarBegin AS int
, #JaarEind AS int
, #MaandBegin AS int
, #MaandEind AS int)
AS
BEGIN
WITH
CTE AS
(
SELECT [D_Medewerker_ID]
,[Gebruikersnaam]
,[Naam]
,[Afdelingscode]
,CONVERT(date, [Datum_uit_dienst]) AS DatumIn
,CONVERT(date, [Datum_in_dienst]) AS DatumUit
FROM [DM].[dm].[D_Medewerker] AS M
),
CTE2 AS(
SELECT F.[D_Functie_ID]
,[Generieke_Functie]
,[Specifieke_Functie]
,Fo.[D_Medewerker_ID]
FROM [DM].[dm].[D_Functie] AS F
JOIN dm.dm.F_FormatieBezetting AS Fo
ON F.D_Functie_ID = Fo.D_Functie_ID
)
SELECT DISTINCT CTE.[Gebruikersnaam]
, CTE.Naam
, CTE.Afdelingscode
, CTE.DatumIn
, CTE.DatumUit
, CTE2.Generieke_Functie
, CTE2.Specifieke_Functie
FROM CTE
JOIN CTE2
ON CTE.D_Medewerker_ID = CTE2.D_Medewerker_ID
WHERE DATEPART(year,CTE.DatumUit) BETWEEN #JaarBegin AND #JaarEind
AND DATEPART(MONTH, CTE.DatumUit) >= #MaandBegin AND DATEPART(MONTH, CTE.DatumUit) <= #MaandEind
ORDER BY CTE.DatumUit DESC;
END
You need to convert the int values you get to a date value.
In Sql server 2012 or later, you can use the built-in function DATEFROMPARTS to do this:
WHERE CTE.DatumUit >= DATEFROMPARTS ( #JaarBegin , #MaandBegin , 1 )
AND CTE.DatumUit < DATEADD(MONTH, 1, DATEFROMPARTS ( #JaarEind , #MaandBegin , 1 ))
If you are working with an earlier version of sql server, you need to build a string that represents the date (using iso format yyyy-mm-dd) and then cast it to date:
WHERE CTE.DatumUit >= CAST(RIGHT('0000' + CAST(#JaarBegin as varchar(4)), 4) + '-' + RIGHT('00' + CAST(#MaandBegin as varchar(2)), 2) +'-01' as datetime)
AND CTE.DatumUit < DATEADD(MONTH, 1, CAST(RIGHT('0000' +CAST(#JaarEind as varchar(4)), 4) + '-' + RIGHT('00' + CAST(#MaandBegin as varchar(2)), 2) +'-01' as datetime))

Count months between two timestamp on postgresql?

I want to count the number of months between two dates.
Doing :
SELECT TIMESTAMP '2012-06-13 10:38:40' - TIMESTAMP '2011-04-30 14:38:40';
Returns :
0 years 0 mons 409 days 20 hours 0 mins 0.00 secs
and so:
SELECT extract(month from TIMESTAMP '2012-06-13 10:38:40' - TIMESTAMP '2011-04-30 14:38:40');
returns 0.
age function returns interval:
age(timestamp1, timestamp2)
Then we try to extract year and month out of the interval and add them accordingly:
select extract(year from age(timestamp1, timestamp2)) * 12 +
extract(month from age(timestamp1, timestamp2))
Please note that the most voted answer by #ram and #angelin is not accurate when you are trying to get calendar month difference using.
select extract(year from age(timestamp1, timestamp2))*12 + extract(month from age(timestamp1, timestamp2))
for example, if you try to do:
select extract(year from age('2018-02-02'::date, '2018-03-01'::date))*12 + extract(month from age('2018-02-02'::date , '2018-03-01'::date))
the result will be 0 but in terms of months between March from February should be 1 no matter the days between dates.
so the formula should be like the following saying that we start with timestamp1 and timestamp2:
((year2 - year1)*12) - month1 + month2 = calendar months between two timestamps
in pg that would be translated to:
select ((extract('years' from '2018-03-01 00:00:00'::timestamp)::int - extract('years' from '2018-02-02 00:00:00'::timestamp)::int) * 12)
- extract('month' from '2018-02-02 00:00:00'::timestamp)::int + extract('month' from '2018-03-01 00:00:00'::timestamp)::int;
you can create a function like:
CREATE FUNCTION months_between (t_start timestamp, t_end timestamp)
RETURNS integer
AS $$
select ((extract('years' from $2)::int - extract('years' from $1)::int) * 12)
- extract('month' from $1)::int + extract('month' from $2)::int
$$
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
The age function give a justified interval to work with:
SELECT age(TIMESTAMP '2012-06-13 10:38:40', TIMESTAMP '2011-04-30 14:38:40');
returns 1 year 1 mon 12 days 20:00:00, and with that you can easily use EXTRACT to count the number of months:
SELECT EXTRACT(YEAR FROM age) * 12 + EXTRACT(MONTH FROM age) AS months_between
FROM age(TIMESTAMP '2012-06-13 10:38:40', TIMESTAMP '2011-04-30 14:38:40') AS t(age);
If you will do this multiple times, you could define the following function:
CREATE FUNCTION months_between (t_start timestamp, t_end timestamp)
RETURNS integer
AS $$
SELECT
(
12 * extract('years' from a.i) + extract('months' from a.i)
)::integer
from (
values (justify_interval($2 - $1))
) as a (i)
$$
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
so that you can then just
SELECT months_between('2015-01-01', now());
SELECT date_part ('year', f) * 12
+ date_part ('month', f)
FROM age ('2015-06-12', '2014-12-01') f
Result: 6 Months
Gives the differenece of months of two dates
SELECT ((extract( year FROM TIMESTAMP '2012-06-13 10:38:40' ) - extract( year FROM TIMESTAMP '2011-04-30 14:38:40' )) *12) + extract(MONTH FROM TIMESTAMP '2012-06-13 10:38:40' ) - extract(MONTH FROM TIMESTAMP '2011-04-30 14:38:40' );
The Result : 14
Have to extract months seperately for both the dates and then the difference of both the results
Here is a PostgreSQL function with the exact same behavior as the Oracle MONTHS_BETWEEN function.
It has been tested on a wide range of years (including leap ones) and more than 700k combinations of dates (including end of every months).
CREATE OR REPLACE FUNCTION months_between
( DATE,
DATE
)
RETURNS float
AS
$$
SELECT
(EXTRACT(YEAR FROM $1) - EXTRACT(YEAR FROM $2)) * 12
+ EXTRACT(MONTH FROM $1) - EXTRACT(MONTH FROM $2)
+ CASE
WHEN EXTRACT(DAY FROM $2) = EXTRACT(DAY FROM LAST_DAY($2))
AND EXTRACT(DAY FROM $1) = EXTRACT(DAY FROM LAST_DAY($1))
THEN
0
ELSE
(EXTRACT(DAY FROM $1) - EXTRACT(DAY FROM $2)) / 31
END
;
$$
LANGUAGE SQL
IMMUTABLE STRICT;
This function requires a LAST_DAY function (behaving the same as Oracle's one) :
CREATE OR REPLACE FUNCTION last_day
( DATE
)
RETURNS DATE
AS
$$
SELECT
(DATE_TRUNC('MONTH', $1) + INTERVAL '1 MONTH' - INTERVAL '1 DAY')::date
;
$$
LANGUAGE SQL
IMMUTABLE STRICT;
I had the same problem once upon a time and wrote this ... it's quite ugly:
postgres=> SELECT floor((extract(EPOCH FROM TIMESTAMP '2012-06-13 10:38:40' ) - extract(EPOCH FROM TIMESTAMP '2005-04-30 14:38:40' ))/30.43/24/3600);
floor
-------
85
(1 row)
In this solution "one month" is defined to be 30.43 days long, so it may give some unexpected results over shorter timespans.
Extract by year and months will floor on months:
select extract(year from age('2016-11-30'::timestamp, '2015-10-15'::timestamp)); --> 1
select extract(month from age('2016-11-30'::timestamp, '2015-10-15'::timestamp)); --> 1
--> Total 13 months
This approach maintains fractions of months (thanks to tobixen for the divisor)
select round(('2016-11-30'::date - '2015-10-15'::date)::numeric /30.43, 1); --> 13.5 months
Try this solution:
SELECT extract (MONTH FROM age('2014-03-03 00:00:00'::timestamp,
'2013-02-03 00:00:00'::timestamp)) + 12 * extract (YEAR FROM age('2014-03-03
00:00:00'::timestamp, '2013-02-03 00:00:00'::timestamp)) as age_in_month;
SELECT floor(extract(days from TIMESTAMP '2012-06-13 10:38:40' - TIMESTAMP
'2011-04-30 14:38:40')/30.43)::integer as months;
Gives an approximate value but avoids duplication of timestamps. This uses hint from tobixen's answer to divide by 30.43 in place of 30 to be less incorrect for long timespans while computing months.
I made a function like this:
/* similar to ORACLE's MONTHS_BETWEEN */
CREATE OR REPLACE FUNCTION ORACLE_MONTHS_BETWEEN(date_from DATE, date_to DATE)
RETURNS REAL LANGUAGE plpgsql
AS
$$
DECLARE age INTERVAL;
declare rtn real;
BEGIN
age := age(date_from, date_to);
rtn := date_part('year', age) * 12 + date_part('month', age) + date_part('day', age)/31::real;
return rtn;
END;
$$;
Oracle Example)
SELECT MONTHS_BETWEEN
(TO_DATE('2015-02-02','YYYY-MM-DD'), TO_DATE('2014-12-01','YYYY-MM-DD') )
"Months" FROM DUAL;
--result is: 2.03225806451612903225806451612903225806
My PostgreSQL function example)
select ORACLE_MONTHS_BETWEEN('2015-02-02'::date, '2014-12-01'::date) Months;
-- result is: 2.032258
From the result you can use CEIL()/FLOOR() for rounding.
select ceil(2.032258) --3
select floor(2.032258) --2
Try;
select extract(month from age('2012-06-13 10:38:40'::timestamp, '2011-04-30 14:38:40'::timestamp)) as my_months;

How do you get positive and negative rows based on date

I'm trying to get ordinance by the current date from a table. this query does what I want but it seems overkill:
WITH dates
AS (SELECT Month,
FQ,
FY,
MonthDisplay,
CAST (datepart(yyyy, [Month]) AS VARCHAR) + '-' + RIGHT(CAST ((datepart(MM, [Month]) + 100) AS VARCHAR), 2) AS YM,
fh,
LEFT(CONVERT (VARCHAR, [Month], 100), 3) + ' ' + RIGHT(fy, 4) AS MY,
LEFT(CONVERT (VARCHAR, [Month], 100), 3) AS ShortMonthName
FROM Pipeline.DimTime AS dt),
datesafter
AS (SELECT dt.FH,
dt.FQ,
dt.FY,
dt.MY,
dt.Month,
dt.MonthDisplay,
dt.ShortMonthName,
dt.YM,
ROW_NUMBER() OVER ( ORDER BY [Month]) AS RowNum
FROM dates AS dt
WHERE dt.[Month] >= (SELECT TOP 1 DATEADD(MONTH, DATEDIFF(MONTH, 0, ds.SnapshotDate), 0)
FROM dbo.vw_DimSnapshot AS ds
WHERE ds.SnapshotWeek = 'Current')),
datesbefore
AS (SELECT dt.FH,
dt.FQ,
dt.FY,
dt.MY,
dt.Month,
dt.MonthDisplay,
dt.ShortMonthName,
dt.YM,
(ROW_NUMBER() OVER ( ORDER BY [Month] DESC)) * -1 AS RowNum
FROM dates AS dt
WHERE dt.[Month] < (SELECT TOP 1 DATEADD(MONTH, DATEDIFF(MONTH, 0, ds.SnapshotDate), 0)
FROM dbo.vw_DimSnapshot AS ds
WHERE ds.SnapshotWeek = 'Current'))
SELECT *
FROM datesafter
UNION ALL
SELECT *
FROM datesbefore
ORDER BY [month];
I think you can do it in a single query by using datediff. Pass current date as one parameter and the table date as another. This will work if you are ok with skipping missing dates. For example if current date is Nov 21, then Nov 20 will show up as -1 and Nov 18 will show up as -3 even if Nov 19 is missing in the data. I am not sure what your business requirement is, so cannot comment beyond that.
And by the way if you are looking for ordinance based on months instead of day, you can use still use datediff but use the correct datepart (See: http://msdn.microsoft.com/en-us/library/ms189794.aspx).
HTH.
-Tabrez