Creating a monthly report in T-SQL - sql-server

I have a table with the following columns
CaseID
DateLogged
CompletionDate
I am trying to create a monthly stock report.
I need to identify monthly which cases are New, current and Completed each month
for instance, All cases logged only in August are new cases while all cases completed in august would show completed and all cases logged that are not completed will be current.
DROP TABLE IF EXISTS #temptable
-- Create date variables
SET dateformat ymd
DECLARE #datefrom datetime = CONVERT(DATETIME, '2019-04-01 00:00:00', 121)
DECLARE #dateto datetime = (SELECT CAST(DATEADD(DAY, -DAY(GETDATE()), GETDATE()) AS date))
-- Recursive date table creation
;WITH monthserial AS
(
SELECT #datefrom AS monthdate
UNION ALL
SELET DATEADD(MONTH, 1, monthdate)
FROM monthserial
WHERE monthdate < #dateto
)
SELECT MN.*
INTO #temptable
FROM monthserial MN
SELECT * FROM MainTable VW
CROSS JOIN #temptable TBL
WHERE DateLogged <= monthdate) test

You will need to use case-when for your situation:
select CaseID,
case
when not (CompletionDate is null) then 'Completed'
when DateLogged >= #input then 'New'
else 'Current'
end
from yourtable
order by DateLogged;
EDIT
Since we are interested about the last entry, we can add a where clause, like this:
select CaseID,
case
when not (CompletionDate is null) then 'Completed'
when DateLogged >= #input then 'New'
else 'Current'
end
from yourtable
where not exists (
select 1
from yourtable inner
where inner.CaseID = yourtable.CaseID and inner.CompletionDate < yourtable.CompletionDate
)
order by DateLogged;

Related

Can I "left join" days between 2 dates in sql server?

There is a table in SQL Server where data is entered day by day. In this table, data is not filled in some days.
Therefore, there are no records in the table.
Sample: dataTable
I need to generate a report like the one below from this table.
Create a table with all the days of the year. I know that I can output a report by "joining" the "dataTable" table.
But this solution seems a bit strange to me.
Is there another way?
the code i use for temp date table
CREATE TABLE tempDate (
calendarDate date,
PRIMARY KEY (calendarDate)
)
DECLARE
#start DATE= '2021-01-01',
#dateCount INT= 730,
#rowNumber INT=1
WHILE (#rowNumber < #dateCount)
BEGIN
INSERT INTO tempDate values (DATEADD(DAY, #rowNumber, #start))
set #rowNumber=#rowNumber+1
END
GO
select * from tempDate
This is how I join using this table
SELECT
*
FROM
tempDate td WITH (NOLOCK)
LEFT JOIN dataTable dt WITH (NOLOCK) ON dt.reportDate = td.calendarDate
WHERE
td.calendarDate BETWEEN '2021-09-05' AND '2021-09-15'
Create a table with all the days of the year. I know that I can output a report by "joining" the "dataTable" table.
This is the way. You can generate that "table" on the fly if you really want to, but normally the best way is to simply have a calendar table.
You can use common expression tables for dates. The code you need:
IF(OBJECT_ID('tempdb..#t') IS NOT NULL)
BEGIN
DROP TABLE #t
END
CREATE TABLE #t
(
id int,
dt date,
dsc varchar(100),
)
INSERT INTO #t
VALUES
(1, '2021.09.08', 'a'),
(1, '2021.09.09', 'b'),
(1, '2021.09.12', 'c')
DECLARE #minDate AS DATE
SET #minDate = (SELECT MIN(dt) FROM #t)
DECLARE #maxDate AS DATE
SET #maxDate = (SELECT MAX(dt) FROM #t)
;WITH cte
AS
(
SELECT #minDate AS [dt]
UNION ALL
SELECT DATEADD(DAY, 1, [dt])
FROM cte
WHERE DATEADD(DAY, 1, [dt])<=#maxDate
)
SELECT
ISNULL(CAST(t.id AS VARCHAR(10)), '') AS [id],
cte.dt AS [dt],
ISNULL(t.dsc, 'No record has been entered in the table.') AS [dsc]
FROM
cte
LEFT JOIN #t t on t.dt=cte.dt
The fastest method is to use a numbers table, you can get a date list between 2 dates with that:
DECLARE #Date1 DATE, #Date2 DATE
SET #Date1 = '20200528'
SET #Date2 = '20200625'
SELECT DATEADD(DAY,number+1,#Date1) [Date]
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number+1,#Date1) < #Date2
If you go go in LEFT JOIN this select, whit your table, you have the result that you want.
SELECT *
FROM (SELECT DATEADD(DAY,number+1,#Date1) [Date]
FROM master..spt_values WITH (NOLOCK)
WHERE type = 'P'
AND DATEADD(DAY,number+1,#Date1) < #Date2 ) as a
LEFT JOIN yourTable dt WITH (NOLOCK) ON a.date = dt.reportDate
WHERE td.[Date] BETWEEN '2021-09-05' AND '2021-09-15'

Get data from the last day of the month without the use of loops or variables

I wrote a query that should select the last record of each month in a year. I'd like to create a View based on this select, that I could run later in my project, but unfortunately I can't use any while loops or variables in a view command. Is there a way to select all these records - last days of a month in a View that I can use later?
My desired effect of the view:
The query that I'm trying to implement in a view:
DECLARE #var_day01 DATETIME;
DECLARE #month int;
SET #month = 1;
DROP TABLE IF EXISTS #TempTable2;
CREATE TABLE #TempTable2 (ID int, date datetime, INP2D float, INP3D float, ID_device varchar(max));
WHILE #month < 13
BEGIN
SELECT #var_day01 = CONVERT(nvarchar, date) FROM (SELECT TOP 1 * FROM data
WHERE DATEPART(MINUTE, CONVERT(nvarchar, date)) = '59'
AND
MONTH(CONVERT(nvarchar, date)) = (CONVERT(nvarchar, #month))
ORDER BY date DESC
) results
ORDER BY date DESC;
INSERT INTO #TempTable2 (ID, date, INP2D,INP3D,ID_device)
SELECT * FROM data
WHERE DATEPART(MINUTE, CONVERT(nvarchar, date)) = '59'
AND
MONTH(CONVERT(nvarchar, date)) = (CONVERT(nvarchar, #month))
AND
DAY(CONVERT(nvarchar, date)) = CONVERT(datetime, DATEPART(DAY, #var_day01))
ORDER BY date DESC
PRINT #var_day01
SET #month = #month +1;
END
SELECT * FROM #TempTable2;
If you are actually just after the single most recent row for each month, there is no need for a while loop to achieve this. You just need to identify the max date value for each month and then filter your source data for those for those rows.
One way to achieve this is via a row_number window function:
declare #t table(id int,dt datetime2);
insert into #t values(1,getdate()-40),(2,getdate()-35),(3,getdate()-25),(4,getdate()-10),(5,getdate());
select id
,id_device
,dt
from(select id
,id_device
,dt
,row_number() over (partition by id_device, year(dt), month(dt) order by dt desc) as rn
from #t
) as d
where rn = 1;
You can add a simple where to your select statement, in where clause you will add one day to the date field and then select the day from the resultant date. If the result date is 1 then only you will select that record
the where clause for your query will be : Where Day(DATEADD(d,1,[date])) = 1

GETDATE()-n when compared to datetime2(7) excludes the matching value

I have a situation where querying a datetime2(7) field with GETDATE()-n is not returning expected output.
Query with >= GEDATE()-20 returns all dates excluding 4/27 (if run today 5/17)
Query with >= 4/27/2018 returns all dates including 4/27.
Is it something to do with the timepart? even if the timepart is all 0's?
DECLARE #MinDate DATE = '04-01-2018',
#MaxDate DATE = '05-17-2018';
SELECT TOP (DATEDIFF(DAY, #MinDate, #MaxDate) + 1)
DateCol = CAST(DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #MinDate) AS DATETIME2(7))
INTO #temp
FROM sys.all_objects a
CROSS JOIN sys.all_objects b;
--SELECT * FROM #temp
SELECT COUNT(*) FROM #temp WHERE DateCol >= GETDATE()-20
SELECT COUNT(*) FROM #temp WHERE DateCol >= '2018-04-27' --excludes the date 4/27
/*
SELECT * FROM #temp WHERE DateCol >= GETDATE()-20 --Excludes 4/27
SELECT * FROM #temp WHERE DateCol >= '2018-04-27' --Expected output includes 4/27
*/
DROP TABLE #temp
This is due to '2018-04-27 00:00:00.0000000' not greater than GETDATE()-20.
GETDATE()-20 will give something like '2018-04-27 10:25:37.680'
For one case you are using only date and for the other scenario it is date and time.
You should change your query like following to change the date-time to date before comparing to get the desired output.
SELECT COUNT(*) FROM #temp WHERE DateCol >= cast(GETDATE() -20 as date)

Dated Between 2 date variable

I have a table with Customers IDs,Name Room Number,Start Date and End date. I want to create a list of dates (daily breakdown) between the start date and end date but want it to look like the tbale i have mocked up in excel in the link below:
i have used
=IF(TEXT(D2,"DD/MM/YYYY")=TEXT(E2,"DD/MM/YYYY"),"Same",IF(F2=DATE(YEAR(D2),MONTH(D2),DAY(D2)),"Start",IF(F2=DATE(YEAR(E2),MONTH(E2),DAY(E2)),"End","Between"))) formula in the Logic ColumN
I have tried to intregrate the following bit of SQL into my query but cant seem to get it to do what i want:
declare #fromdate dateTIME
set #fromdate= '1/1/2017'(Wanted to used Column Startdate)
declare #todate dateTIME = getdate()-1(Wanted used Column Enddate)
;with CTE AS
(SELECT #fromdate AS RESULT
UNION ALL
SELECT RESULT + 1 FROM CTE WHERE RESULT<=#todate
)
SELECT Result,1 as Count FROM CTE OPTION(MAXRECURSION 0)
Any help would be gratefully appreciated.
It is not completely clear what isn't working in your query. If it entails getting the rows per day, the easiest way to go about this is to actually have a date-table ready in your database. You can refer to this date-table in your view. See below an example which I have used in multiple projects.
CREATE PROCEDURE [dbo].[FillDateTable](#StartDate DATE, #EndDate DATE) AS
BEGIN
TRUNCATE TABLE dbo.DateTable;
WITH CTERecursive AS (
SELECT #StartDate AS 'CalendarDate'
UNION ALL
SELECT DATEADD(DAY,1,CalendarDate) AS 'CalendarDate'
FROM CTERecursive
WHERE DATEADD(DAY,1,CalendarDate)<=#EndDate
), TotalCalendar AS (
SELECT CalendarDate
, DAY(CalendarDate) AS 'Day'
, MONTH(CalendarDate) AS 'Month'
, YEAR(CalendarDate) AS 'Year'
, DATEADD(DAY,-DATEPART(WEEKDAY,CalendarDate)+1,CalendarDate) AS 'FirstDayOfWeek'
, DATEADD(DAY,6,DATEADD(DAY,-DATEPART(WEEKDAY,CalendarDate)+1,CalendarDate)) AS 'LastDayOfWeek'
FROM CTERecursive
)
INSERT INTO dbo.DateTable ([CalendarDate], [Day], [Month], [Year], [FirstDayOfWeek], [LastDayOfWeek], [NewWeek])
SELECT *
, NewWeek=CASE WHEN CalendarDate=FirstDayOfWeek THEN 1 ELSE 0 END
FROM TotalCalendar
OPTION (MAXRECURSION 0)
END
Furthermore, it'd be wise to use DATEADD instead of +1 in your CTE.
DECLARE #fromdate DATE
SET #fromdate=CONVERT(DATE,'20170101',112)
DECLARE #todate DATE=GETDATE()-1
;
WITH CTE AS
(SELECT #fromdate AS RESULT
UNION ALL
SELECT DATEADD(DAY,1,RESULT) FROM CTE WHERE RESULT<=#todate
)
SELECT [DateInclusive]=RESULT
,1 as Count FROM CTE OPTION(MAXRECURSION 0)
UPDATE: I'll presume you have a table with the Id, Room, Name, Start- and Enddate readily available. Your query could be structured as follows, yielding an overview similar to your Excel mockup:
DECLARE #Bookings TABLE ([Id] VARCHAR(30), [Room] INT, [Name] VARCHAR(255), [Startdate] DATE, [Enddate] DATE)
INSERT INTO #Bookings VALUES ('V123789',8,'H. Brown','20170201','20170315'),('V555589',1,'J. Frost','20170205','20170420');
WITH CTE AS
(SELECT Id, [Startdate] AS InclusiveDate FROM #Bookings
UNION ALL
SELECT Id, DATEADD(DAY,1,InclusiveDate) FROM CTE WHERE InclusiveDate < (SELECT Enddate FROM #Bookings WHERE Id=CTE.Id)
)
SELECT B.[Id], B.[Room], B.[Name], B.[Startdate], B.[Enddate], CTE.[InclusiveDate]
, CASE WHEN CTE.[InclusiveDate]=B.[Startdate] THEN 'Start'
WHEN CTE.[InclusiveDate]=B.[Enddate] THEN 'End'
WHEN CTE.[InclusiveDate]>B.[Startdate] AND CTE.[InclusiveDate]<B.[Enddate] THEN 'Between' END AS Logic
FROM CTE
JOIN #Bookings B on B.Id=CTE.[Id]
ORDER BY B.[Id], CTE.[InclusiveDate] ASC
OPTION(MAXRECURSION 0)

SQL Query for showing dates not stored in the table

I have a table in which i am storing dates and other information.
I wanna display the records for the dates which are not stored in the table.
Eg.. i have dates 01/01/2012[dd/mm/yyyy] , 03/01/2012 , 06/01/2012.
I wanna show the output for the dates 02/01/2012 ,04/05/2012 , 05/01/2012.
Query for this please in SQLServer2008
You can use the DATEADD function. For example, to add 1 day to current date:
SELECT DATEADD(dd, 1, GETDATE())
You can find more information on MSDN.
;WITH MYCTE AS
(
SELECT CAST('1900-01-01' AS DATETIME) DateValue
UNION ALL
SELECT DateValue + 1
FROM MYCTE
WHERE DateValue + 1 < '3550-12-31'
)
SELECT DateValue, B.SomeColumn
FROM MYCTE A
LEFT JOIN MyTable B ON A.DateValue = B.DateValue
OPTION (MAXRECURSION 0)
I got the date range cte from here (and it takes 4 seconds to generate a table of 250000 rows)
This code displays all dates from 2011 year missing in your table:
create table #dates (d datetime);
declare #start_period datetime;
set #start_period='01.01.2011';
declare #end_period datetime;
set #end_period='01.01.2012';
declare #d datetime;
set #d=#start_period
while(#d<#end_period)
begin
insert into #dates (d) values (#d)
SET #d=#d+1
end
select d from #dates where d not in (select <date> from <your_table>)

Resources