Create missing months dynamically from group on date field [duplicate] - sql

This question already has answers here:
SQL select, pad with chronological missing months
(3 answers)
Closed 7 years ago.
In the following SQL i'm trying to insert rows to fill in the missing months in the results. The solution is very close thanks to post SQL select, pad with chronological missing months
But yet this code runs gr8 but still have missing months, issue is how to join/union the temp table
DECLARE #StartDate DATETIME = dateadd(m,-12,getdate()), #EndDate DATETIME = getdate(), #DATE DATETIME
DECLARE #TEMP AS TABLE (MeterReadDate datetime)
SET #DATE = #StartDate
WHILE #DATE <= #EndDate
BEGIN
INSERT INTO #TEMP VALUES ( #DATE)
SET #DATE = DATEADD(MONTH,1,#DATE)
END
SELECT convert(char(7), t.MeterReadDate, 121),count(*)
FROM #TEMP m left join
[PremiseMeterReadProviders] t
on convert(char(7), t.MeterReadDate, 121) = convert(char(7), m.MeterReadDate, 121)
where (t.MeterReadDate > dateadd(m,-12,getdate()))
group by convert(char(7), t.MeterReadDate, 121)
order by convert(char(7), t.MeterReadDate, 121)

Try something like this....
;WITH Months AS
(
SELECT TOP 12
CONVERT(CHAR(7),
DATEADD(MONTH
, - ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
, GETDATE()
)
,121) MonthNo
FROM master..spt_values
)
SELECT convert(char(7), t.MeterReadDate, 121),count(*)
FROM Months m
LEFT JOIN [PremiseMeterReadProviders] t
ON convert(char(7), t.MeterReadDate, 121) = m.MonthNo
AND (t.MeterReadDate > dateadd(m,-12,getdate()))
group by convert(char(7), t.MeterReadDate, 121)
order by convert(char(7), t.MeterReadDate, 121)

I think you need to reconstruct the query as below
DECLARE #StartDate DATETIME = dateadd(m,-12,getdate()), #EndDate DATETIME = getdate(), #DATE DATETIME
DECLARE #TEMP AS TABLE (MeterReadDate datetime)
SET #DATE = #StartDate
WHILE #DATE <= #EndDate
BEGIN
INSERT INTO #TEMP VALUES ( #DATE)
SET #DATE = DATEADD(MONTH,1,#DATE)
END
SELECT convert(char(7), m.MeterReadDate, 121),count(*)
FROM #TEMP m left join
[Announcement] t
on convert(char(7), t.ExpiryDate, 121) = convert(char(7), m.MeterReadDate, 121)
WHERE (t.ExpiryDate IS NULL OR t.ExpiryDate > dateadd(m,-12,getdate()))
group by convert(char(7), m.MeterReadDate, 121)
order by convert(char(7), m.MeterReadDate, 121)
In the where condition, I added t.ExpiryDate IS NULL, because this will be null for the missing month.

Related

SQL Count with zero values

I want to create a graph for my dataset for the last 24 hours.
I found a solution that works but this is pretty bad since the table I am outer joining cotains every single row in the DB since I am using the (now deprecated) "all" parameter in the group by.
Here is the solution that currently kind of works.
First I declare the date intervals that is 24 hours back in time from now. I declare it twice so I can use it later in the procedure aswell.
Declare #StartDate datetime = dateadd(hour, -24, getdate())
Declare #StartDateProc datetime = dateadd(hour, -24, getdate())
Declare #EndDate datetime = getdate()
I populate the dates into a temp table including a special formated datetsring.
create table #tempTable
(
Date datetime,
DateString varchar(11)
)
while #StartDate <= #EndDate
begin
insert into #tempTable (Date, DateString)
values (#StartDate, convert(varchar(8), #StartDate, 5) + '-' + convert(varchar(2), #StartDate, 108));
SET #StartDate = dateadd(hour,1, #StartDate);
end
This gives me data that looks like this:
Date DateString
---------------------------------------------
2015-12-09 13:59:01.970 09-12-15-13
2015-12-09 14:59:01.970 09-12-15-14
2015-12-09 15:59:01.970 09-12-15-15
2015-12-09 16:59:01.970 09-12-15-16
So what I want is to join my dataset on the matching date string and show the date even if the matching rows is zero.
Here is the rest of the query
select
Date = c.Date,
Amount = sum(c.Amount)
from
DbTable a
outer apply
(select
Date = b.DateString,
Amount = count(*)
from
#tempTable b
where
convert(varchar(8), a.DateColumn, 5) + '-' + convert(varchar(2), a.DateColumn, 108) = b.DateString
group by all
b.DateString) c
where
a.SomeParameter = 'test' and
a.DateColumn >= #StartDateProc and
a.DateColumn <= #EndDate
group by
c.Date
drop table #tempTable
Test to show actual data:
Declare #StartDate datetime = dateadd(hour, -24, getdate())
Declare #EndDate datetime = getdate()
select
dateString = convert(varchar(8),a.DateColumn,5) + '-' + convert(varchar(2),a.DateColumn, 108),
Amount = COUNT(*)
from
DbTable a
where
a.someParameter = 'test' and
a.DateColumn>= dateadd(hour, -24, getdate()) and
a.DateColumn<= getdate()
group by
convert(varchar(8),a.DateColumn,5) + '-' + convert(varchar(2),a.DateColumn, 108)
First output rows:
dateString Amount
09-12-15-14 1
09-12-15-15 1
09-12-15-16 1
09-12-15-17 3
09-12-15-18 1
09-12-15-22 3
09-12-15-23 2
As you can see here there is no data for the times from 19.00 to 21.00. This is how I want the data to be displayed:
dateString Amount
09-12-15-14 1
09-12-15-15 1
09-12-15-16 1
09-12-15-17 3
09-12-15-18 1
09-12-15-19 0
09-12-15-20 0
09-12-15-21 0
09-12-15-22 3
09-12-15-23 2
Normally, this would be approached with left join rather than outer apply. The logic is simple: keep all rows in the first table along with any matching information from the second. This means put the dates table first:
select tt.DateString, count(t.DateColumn) as Amount
from #tempTable tt left join
DbTable t
on convert(varchar(8), t.DateColumn, 5) + '-' + convert(varchar(2), t.DateColumn, 108) = tt.DateString and
t.SomeParameter = 'test'
where tt.Date >= #StartDateProc and
tt.Date <= #EndDate
group by tt.DateString;
In addition, your comparison for the dates seems overly complex, but if it works for you, it works.
The best bet here would be to use DATETIME type itself and not to lose the opportunity to use indexes:
Declare #d datetime = GETDATE()
;WITH cte1 AS(SELECT TOP 25 -1 + ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) h
FROM master..spt_values),
cte2 AS(SELECT DATEADD(hh, -h, #d) AS startdate,
DATEADD(hh, -h + 1, #d) AS enddate
FROM cte1)
SELECT c.startdate, c.enddate, count(*) as amount
FROM cte2 c
LEFT JOIN DbTable a ON a.DateColumn >= c.startdate AND
a.DateColumn < c.enddate AND
a.SomeParameter = 'test'
GROUP BY c.startdate, c.enddate

SQL select, pad with chronological missing months

Notice the 2015-02 and 2015-03 months are missing in the output from the following group by SQL. If there is no data for a month I want to show the month and 0. Anyone know how to go about this?
SELECT convert(char(7), MeterReadDate, 121),count(*)
FROM [myTable]
where (MeterReadDate > dateadd(d,-356,getdate()))
group by convert(char(7), MeterReadDate, 121)
order by convert(char(7), MeterReadDate, 121)
Sample data:
YYYY-MM COUNT
2014-06 23
2014-07 42
2014-08 80
2014-09 92
2014-10 232
2014-11 88
2014-12 8
2015-01 5
2015-04 2
2015-05 1
Still cannot clear the missing rows, here is where I am with it..
DECLARE #StartDate DATETIME = dateadd(m,-12,getdate()), #EndDate DATETIME = getdate(), #DATE DATETIME
DECLARE #TEMP AS TABLE (MeterReadDate datetime)
SET #DATE = #StartDate
WHILE #DATE <= #EndDate
BEGIN
INSERT INTO #TEMP VALUES ( #DATE)
SET #DATE = DATEADD(MONTH,1,#DATE)
END
SELECT convert(char(7), t.MeterReadDate, 121),count(*)
FROM #TEMP m left join
[myTable] t
on convert(char(7), t.MeterReadDate, 121) = convert(char(7), m.MeterReadDate, 121)
where (t.MeterReadDate > dateadd(m,-12,getdate()))
group by convert(char(7), t.MeterReadDate, 121)
order by convert(char(7), t.MeterReadDate, 121)
If you don't want to go beyond your min and max dates of your results then you can do the following:
WITH cte
AS ( SELECT convert(char(7), MeterReadDate, 121) AS [Date], COUNT(*) AS [Count]
FROM [myTable]
WHERE (MeterReadDate > dateadd(d,-356,getdate()))
GROUP by convert(char(7), MeterReadDate, 121)
),
minmax
AS ( SELECT CAST(MIN([Date] + '-01') AS DATE) AS mind ,
CAST(MAX([Date] + '-01') AS DATE) maxd
FROM cte
),
calendar
AS ( SELECT mind ,
CONVERT(CHAR(7), mind, 121) AS cmind
FROM minmax
UNION ALL
SELECT DATEADD(mm, 1, calendar.mind) ,
CONVERT(CHAR(7), DATEADD(mm, 1, calendar.mind), 121)
FROM calendar
CROSS JOIN minmax
WHERE calendar.mind < minmax.maxd
)
SELECT c.cmind AS [Date],
ISNULL(cte.[Count], 0) AS [Count]
FROM calendar c
LEFT JOIN cte ON c.cmind = cte.[Date]
OPTION ( MAXRECURSION 0 )
You need a list of dates/months that covers the entire period. Here is one method using a recursive CTE:
with months as (
select cast(getdate() - 365) as thedate
union all
select date_add(1, month, thedate)
from months
where thedate <= getdate()
)
select convert(char(7), m.thedate, 121) as yyyy-mm, count(t.MeterReadDate)
from months m left join
[myTable] t
on convert(char(7), MeterReadDate, 121) = convert(char(7), m.thedate, 121)
group by convert(char(7), m.thedate, 121)
order by convert(char(7), m.thedate, 121);
The best way to do this would be to join onto a table containing calendar information, one way to do this without actually altering the database schema (and is more dynamic in my opinion) would be to use a table variable and the DATEADD() function.
DECLARE #StartDate DATETIME = '2015-01-01', #EndDate DATETIME = '2015-12-01', #DATE DATETIME
DECLARE #TEMP AS TABLE ([DATE] DATETIME)
SET #DATE = #StartDate
WHILE #DATE <= #EndDate
BEGIN
INSERT INTO #TEMP VALUES (#DATE)
SET #DATE = DATEADD(MONTH,1,#DATE)
END
SELECT * FROM #TEMP
Set #Start and #End to your required dates, then just join onto your results set!
UPDATE:
To extract the date in the format you gave above under "sample data", meaning you would be able to join onto the table, use:
SELECT
CONCAT(YEAR([DATE]),'-',right('0' + cast(month([DATE]) as varchar),2))
FROM #TEMP
LEFT JOIN MyTable ON...

Find the Same day of Previous Year Given by Current Year Date in SQL Server

I am working with SQL Server, The scenario is to find out the Same Day's Date of Previous Year as of Today's Day.
Suppose 2014-03-06 is Today Date and Day is Thursday I want to Find the Same day in Previous lies in the same week .which is 2013-03-07
can any body help?
HERE is what i Have Written:
DECLARE #DateFrom AS DATETIME
DECLARE #DateTo AS DATETIME
SET #DateFrom = '2014-01-01'
SET #DateTo = '2014-02-10'
DECLARE #Count AS INT
SET #Count = DATEDIFF(DAY, #DateFrom, #DateTo)
CREATE TABLE #current_year /*This Year*/
(
[Date] DATETIME ,
WeekNum INT,
[Day] VARCHAR(20),
Data INT
)
CREATE TABLE #last_year /*This Year -1*/
(
[Date] DATETIME ,
WeekNum INT,
[Day] VARCHAR(20),
Data INT
)
WHILE ( #Count > 0 )
BEGIN
INSERT INTO #current_year
VALUES ( CONVERT(VARCHAR(10), #DateFrom, 101),
DATEPART(week,#DateFrom),
DATENAME(weekday, #DateFrom),#Count)
INSERT INTO #last_year
VALUES ( CONVERT(VARCHAR(10), DATEADD(YEAR, -1, #DateFrom), 101),
DATEPART(week,DATEADD(YEAR,1,#DateFrom)),
DATENAME(weekday, DATEADD(YEAR, -1, #DateFrom)),#Count)
SET #DateFrom = DATEADD(day, 1, #DateFrom)
SET #Count = #Count - 1
END
SELECT * from #current_year
SELECT * from #last_year
SELECT CONVERT(varchar(10),#current_year.[Date],111) AS CYDate,
--ISNULL(CONVERT(varchar(10),#last_year.[Date],111) ,/*CONVERT(varchar(10),DateAdd(dd, 1, DATEADD(yy, -1, #current_year.Date)),111)*/) AS LYDate
--CONVERT(varchar(10),#last_year.[Date],111) AS LYDate
Coalesce(CONVERT(varchar(10),#last_year.[Date],111) ,DateAdd(dd, 1, DATEADD(yy, -1, #current_year.Date))) AS LYDate,
#current_year.Data AS CD,
#last_year.Data AS LD
FROM #current_year
--LEFT JOIN #last_year ON #last_year.WeekNum = #current_year.WeekNum
-- AND #last_year.[Day] = #current_year.[Day]
Left JOIN #last_year ON #last_year.WeekNum = DatePart(wk, GETDATE())
DROP TABLE #current_year
DROP TABLE #last_year
Here is the Output:
Here is the output after adding your solution, now in left join it excludes (NULL) data of previous year
Basically you need to find difference in days between same dates in this and previous years, then understand "day difference" by mod 7, and sum it with date in previous year:
DECLARE #now DATETIME
SET #now = '2014-03-06'
SELECT CAST (DATEADD(YEAR, -1, #now) + (CAST (#now as INT) - CAST (DATEADD(YEAR, -1, #now) AS INT)) % 7 AS DATE)
Returns
2013-03-07
Try
DECLARE #now Date
SET #now = '2014-06-03'
SELECT DATEADD(week, -52, #now)
SELECT DateName(dw, DATEADD(yy, -1, GETDATE()))
gives Wednesday
SELECT DateName(dw, DateAdd(dd, 1, DATEADD(yy, -1, GETDATE())))
gives Thursday
edit:
SELECT DateAdd(dd, 1, DATEADD(yy, -1, GETDATE()))
gives '2013-03-07 17:30:16.590'
you need to cast the date as per you requirement..
update:
change this part with,
Left JOIN #last_year ON #last_year.WeekNum = DatePart(wk, GETDATE())
in your case:
Left JOIN #last_year ON DatePart(wk, #last_year.[Date]) = DatePart(wk, #current_year.[Date])
update 2:
Left JOIN #last_year ON (MONTH(#last_year.[Date])=MONTH(#current_year.[Date]) and Day(#last_year.[Date])=Day(#current_year.[Date]))
Output:
or
output:
Left JOIN #last_year ON (#last_year.WeekNum = #current_year.WeekNum and #last_year.[Day] = #current_year.[Day])
choose which ever is your required output.
DECLARE #Date DATE = '2014-03-06'
SELECT DATEADD(WEEK,-52,#Date)
Retrun value :
2013-03-07

write a query that will run all the days and the name of the day between two set dates [duplicate]

This question already has answers here:
Get a list of dates between two dates
(23 answers)
Closed 8 years ago.
I am trying to write a query that will run all the days and the name of the day between two set dates.
Example:
Date1 = 12/28/2005
Date2 = 12/30/2006
Results:
12/28/2005 Wednesday
12/29/2005 Thursday
12/30/2005 Friday
12/31/2005 Saturday
01/01/2006 Sunday
01/02/2006 Monday
01/03/2006 Tuesday
Any help is appreciated!
You may check this fiddle.
The code:
DECLARE #Date1 DATETIME
DECLARE #Date2 DATETIME
SET #Date1 = '20051228'
SET #Date2 = '20061230'
;WITH cteSequence ( SeqNo) as
(
SELECT 0
UNION ALL
SELECT SeqNo + 1
FROM cteSequence
WHERE SeqNo < DATEDIFF(d,#Date1,#Date2)
)
SELECT CONVERT(VARCHAR,DATEADD(d,SeqNo,#Date1),1) + ' ' + DATENAME(dw,DATEADD(d,SeqNo,#Date1))
FROM cteSequence
OPTION ( MAXRECURSION 0);
GO
You can use that table-valued function:
create function DateTable
(
#FirstDate datetime,
#LastDate datetime,
#handle nvarchar(10)='day',
#handleQuantity int=1
)
returns #datetable table (
[date] datetime
)
AS
begin
with CTE_DatesTable
as
(
select #FirstDate AS [date]
union ALL
select case #handle
when 'month' then dateadd(month, #handleQuantity, [date])
when 'year' then dateadd(year, #handleQuantity, [date])
when 'hour' then dateadd(hour, #handleQuantity, [date])
when 'minute' then dateadd(minute, #handleQuantity, [date])
when 'second' then dateadd(second, #handleQuantity, [date])
else dateadd(day, #handleQuantity, [date])
end
from CTE_DatesTable
where #LastDate >=
case #handle
when 'month' then dateadd(month, #handleQuantity, [date])
when 'year' then dateadd(year, #handleQuantity, [date])
when 'hour' then dateadd(hour, #handleQuantity, [date])
when 'minute' then dateadd(minute, #handleQuantity, [date])
when 'second' then dateadd(second, #handleQuantity, [date])
else DATEADD(day, #handleQuantity, [date])
end
)
insert #datetable ([date])
select [date] from CTE_DatesTable
option (MAXRECURSION 0)
return
end
You can call it like:
select [date],datepart(weekday,[date]) from dbo.DateTable('12/28/2005','12/30/2006',default,default)
You didn't specify your DBMS so I'm assuming Postgres:
select i::date, to_char(i, 'Day')
from generate_series(timestamp '2005-12-28 00:00:00',
timestamp '2006-12-30 00:00:00', interval '1' day) i;
The ANSI SQL solution for this would be:
with recursive date_list (the_date) as (
values ( date '2005-12-28' )
union all
select cast(p.the_date + interval '1' day as date)
from date_list p
where p.the_date <= date '2006-12-30
)
select *
from date_list;

Making SQL query results 'PRINT'?

I have a query which I eventually got to work fine but what I really need is the results to be displayed using the SQL PRINT command. The reason for this is I am automating the results to be emailed, and if I can have them come out as printed text then I can just embed the results in the email using the tool we use here. Otherwise, the current results have to be attached as a file and I would prefer the printed text if possible.
I have tried to modify the query by adding DECLARE and PRINT but I am really confused and can't figure it out. The query has 2 CTE's in it pulling data from multiple databases. What it is doing is selecting all the sale numbers/ID's from our SAP system for yesterday and comparing them with our the Sale numbers/ID's from our POS system for yesterday to make sure every sale in our POS system is now in SAP. The query itself works fine.
How can I print the results of this query?
WITH CTE1 (SAP_SALE)
AS
(
select distinct convert(BIGINT,convert(varchar(15),WERKS)+(select RIGHT(convert(Varchar(20),BONNR),7)))
as Branch_tx_no from [PDP].[pdp].[S120] WITH (NOLOCK)
where SPTAG >= CAST(CONVERT(VARCHAR(10), GETDATE() -1, 101) AS DATETIME) AND
SPTAG < CAST(CONVERT(VARCHAR(10), GETDATE(), 101) AS DATETIME)
),
CTE2 (AR_SALE)
AS
(
select convert(varchar(15),branch_no)+convert(varchar(15),sale_tx_no)
from [ARDB01].[PP_BODATA].[DBO].[sales_tx_hdr] WITH (NOLOCK)
WHERE sale_date >= CAST(CONVERT(VARCHAR(10), GETDATE() -1, 101) AS DATETIME) AND
sale_date < CAST(CONVERT(VARCHAR(10), GETDATE(), 101) AS DATETIME)
and sale_type in ('C','L')
)
SELECT AR_SALE FROM CTE2 AS CTE2
Left OUTER JOIN CTE1 AS CTE1
ON CTE1.SAP_SALE = CTE2.AR_SALE
WHERE CTE1.SAP_SALE IS NULL
ORDER BY CTE2.AR_SALE
The easiest solution is to use a cursor and PRINT one row at a time. Or you could use XML-concatenation, if you do not have any special characters in the result:
DECLARE #txt NVARCHAR(MAX);
WITH CTE1 (SAP_SALE)
AS
(
select distinct convert(BIGINT,convert(varchar(15),WERKS)+(select RIGHT(convert(Varchar(20),BONNR),7)))
as Branch_tx_no from [PDP].[pdp].[S120] WITH (NOLOCK)
where SPTAG >= CAST(CONVERT(VARCHAR(10), GETDATE() -1, 101) AS DATETIME) AND
SPTAG < CAST(CONVERT(VARCHAR(10), GETDATE(), 101) AS DATETIME)
),
CTE2 (AR_SALE)
AS
(
select convert(varchar(15),branch_no)+convert(varchar(15),sale_tx_no)
from [ARDB01].[PP_BODATA].[DBO].[sales_tx_hdr] WITH (NOLOCK)
WHERE sale_date >= CAST(CONVERT(VARCHAR(10), GETDATE() -1, 101) AS DATETIME) AND
sale_date < CAST(CONVERT(VARCHAR(10), GETDATE(), 101) AS DATETIME)
and sale_type in ('C','L')
)
SELECT #txt = (
SELECT CHAR(13)+CHAR(10)+AR_SALE FROM CTE2 AS CTE2
Left OUTER JOIN CTE1 AS CTE1
ON CTE1.SAP_SALE = CTE2.AR_SALE
WHERE CTE1.SAP_SALE IS NULL
ORDER BY CTE2.AR_SALE
FOR XML PATH(''),TYPE
).value('.','NVARCHAR(MAX)');
PRINT #txt;
If you need to use PRINT you can combine results of your query into a comma (or other char) separated VARCHAR variable and then print that variable, e.g.
DECLARE #sTMP varchar(1000)
SET #sTMP = ''
-- Your CTE....
SELECT #sTMP = #sTMP + AR_SALE + ',' FROM CTE2 AS CTE2
Left OUTER JOIN CTE1 AS CTE1
ON CTE1.SAP_SALE = CTE2.AR_SALE
WHERE CTE1.SAP_SALE IS NULL
ORDER BY CTE2.AR_SALE
PRINT #sTMP
WITH CTE1 (SAP_SALE)
AS
(
select distinct convert(BIGINT,convert(varchar(15),WERKS)+(select RIGHT(convert(Varchar(20),BONNR),7)))
as Branch_tx_no from [PDP].[pdp].[S120] WITH (NOLOCK)
where SPTAG >= CAST(CONVERT(VARCHAR(10), GETDATE() -1, 101) AS DATETIME) AND
SPTAG < CAST(CONVERT(VARCHAR(10), GETDATE(), 101) AS DATETIME)
),
CTE2 (AR_SALE)
AS
(
select convert(varchar(15),branch_no)+convert(varchar(15),sale_tx_no)
from [ARDB01].[PP_BODATA].[DBO].[sales_tx_hdr] WITH (NOLOCK)
WHERE sale_date >= CAST(CONVERT(VARCHAR(10), GETDATE() -1, 101) AS DATETIME) AND
sale_date < CAST(CONVERT(VARCHAR(10), GETDATE(), 101) AS DATETIME)
and sale_type in ('C','L')
)
SELECT AR_SALE, row_number() over (order by AR_SALE) as r
into #temp -- added this row right here
FROM CTE2 AS CTE2
Left OUTER JOIN CTE1 AS CTE1
ON CTE1.SAP_SALE = CTE2.AR_SALE
WHERE CTE1.SAP_SALE IS NULL
ORDER BY CTE2.AR_SALE
then...
declare #x varchar(100)
declare #i int
set #i = 1
while (#i <= (select max(r) from #temp)) begin
select #x=AR_SALE from #temp where r=#i
print #x
set #i=#i+1
end