CTE, Incorrect syntax near ';' SQL SERVER - sql-server

I've written such PROCEDURE in Sql Server:
CREATE PROCEDURE p_SelectDatesFromRange
(
#startDate DATETIME,
#endDate DATETIME
)
AS
(
;WITH dates AS
(
SELECT #startDate AS [Date]
UNION ALL
SELECT DATEADD(day, 1, [Date])
FROM dates
WHERE [Date] <= #endDate - 1
)
SELECT *
FROM dates
);
It should return all dates, between two, given parameters. But I can't run it, it gives me an error.
Incorrect syntax near ';'
Incorrect syntax near ')'
This errors are at the beginning of 'with', an at the end of code of procedure.
I'm learning CTE, so sorry, if this is obvious mistake.
Please help, thanks, Mike.

I'm not sure that your error has anything to do with CTEs per se, but rather you are trying to use parentheses to denote the body of your stored procedure. Either drop the parentheses, or possibly use a BEGIN and END block:
CREATE PROCEDURE p_SelectDatesFromRange
(
#startDate DATETIME,
#endDate DATETIME
)
AS
BEGIN
;WITH dates AS
(
SELECT #startDate AS [Date]
UNION ALL
SELECT DATEADD(day, 1, [Date])
FROM dates
WHERE [Date] <= #endDate - 1
)
SELECT *
FROM dates
END;

This works
CREATE PROCEDURE DBO.DateRange
#start datetime,
#end datetime
AS
with cte as
( SELECT #start as strtDt, #end as endDt
union all
select strtDt + 1, endDt
from cte
where strtDt < endDt
)
select * from cte

Related

Stored procedure to add 30 days using DATEDIFF within while loop condition in Date Dimension table

I want to add 30 consecutive days of data in my Date Dimension table using DATEDIFF() but I am getting blank result. Can you please help me correct the code below to get the desired result?
CREATE TABLE dbo.dateDimension (
DateKey INT NOT NULL
,DateValue DATE NOT NULL
,CYear SMALLINT NOT NULL
,CMonth TINYINT NOT NULL
,CONSTRAINT PK_DimDate PRIMARY KEY ( DateKey )
);
GO
CREATE PROC dbo.dateTest
#StartDate DATETIME
AS
WHILE (DATEDIFF(day, #StartDate, GETDATE()) <=30)
BEGIN
INSERT into dbo.dateDimension
SELECT CAST( YEAR(#StartDate) * 10000 + MONTH(#StartDate) * 100 + DAY(#StartDate) AS INT)
,#StartDate
,YEAR(#StartDate)
,MONTH(#StartDate)
SET #StartDate = DATEADD(d,1,#StartDate)
END;
GO
EXECUTE dbo.dateTest '2010-01-01'
SELECT * FROM dbo.dateDimension
The issue is that this logic:
DATEDIFF(day, #StartDate, GETDATE())
gives 3739 days with your current start date, so its never less than 30. Personally I would simply count it as follows:
DECLARE #StartDate DATETIME = '2010-01-01', #Count INT = 0;
WHILE #Count <= 30 BEGIN
INSERT into dbo.dateDimension
SELECT CAST( YEAR(#StartDate) * 10000 + MONTH(#StartDate) * 100 + DAY(#StartDate) AS INT)
, #StartDate
, YEAR(#StartDate)
, MONTH(#StartDate);
SET #StartDate = DATEADD(d,1,#StartDate);
set #Count = #Count + 1;
END;
SELECT *
FROM dbo.dateDimension;
If you are using SQL Server 2016 or above, this solution will not use a while loop, instead it uses a CTE to generate 30 rows numbered I to 30 and then uses the date to convert to yyyymmdd.
DECLARE #NUM_DAYS INT=30;
DECLARE #STARTDATE DATETIME='2020-01-01';
WITH CTE AS(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS SRL
FROM STRING_SPLIT(REPLICATE(',',#num_days-1), ',') AS A
)
INSERT INTO dbo.dateDimension
SELECT
CONVERT(INT, CONVERT(CHAR(8), DATEADD(DAY, SRL-1, #STARTDATE), 112))
, #STARTDATE
, YEAR(#STARTDATE)
, MONTH(#STARTDATE)
FROM CTE
SELECT * FROM dbo.dateDimension

How to create date table with given datetime range?

I'm trying to create a table for calendar.But sql server stops at '2000-01-11 17:45'. How can i do create dates adding 15 minutes until 2050
Create Table Calendar
(id int IDENTITY(1,1) Primary key,CalendarDate DATETIME)
Declare #beginDate DATETIME, #endDate DATETIME
Select #beginDate = '2000-01-01 17:45', #endDate = '2050-01-01 09:00'
While #beginDate <= #endDate
Begin
Insert Into dbo.Calendar(CalendarDate)
Select
#beginDate As CalendarDate
Set #beginDate = DateAdd(MINUTE, 15, #beginDate)
End
Your current syntax suggest SQL Server, so i would try with recursive CTE :
with cte as (
select #beginDate as st
union all
select DATEADD(MINUTE, 15, st)
from cte
where st < #endDate
)
Insert Into dbo.Calendar(CalendarDate)
select st as CalendarDate
from cte
option (maxrecursion 0);

Get monthly attendance for an employee

I have a query that a guy helped here in developing which gives fine result. I want to use this query in a function which later I want to use in a procedure.
CREATE FUNCTION [dbo].[fnGetWorkedDays] (#StartDate datetime, #EndDate datetime)
RETURNS int
AS
BEGIN
DECLARE #dateFrom datetime
DECLARE #dateTo datetime
SET #dateFrom = #StartDate
SET #dateTo = #EndDate
DECLARE #DAYSWORKED INT
SELECT #DAYSWORKED = (
SELECT EmpId, COUNT(*) as DaysWorked
FROM
(
SELECT DISTINCT EmpId,CAST(TimeIn AS DATE) AS [Date]
FROM myTable
WHERE TimeIn IS NOT NULL
AND CAST(TimeIn AS DATE) BETWEEN #StartDate AND #EndDate
)T
GROUP BY EmpId
ORDER BY EmpId)
RETURN ISNULL(#DAYSWORKED,0)
END
The very first error I am getting is that
Msg 1033, Level 15, State 1, Procedure fnGetWorkedDays, Line 24
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified.
I removed ORDER BY then I got another error says
Msg 116, Level 16, State 1, Procedure fnGetWorkedDays, Line 25
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
I suggest you to go though this to understand more on table valued functions vs scalar valued function Difference between scalar, table-valued, and aggregate functions in SQL server?
Regarding your scenario, you should be writing a table values function for this like following.
CREATE FUNCTION [dbo].[fnGetWorkedDays]
(
#StartDate AS DATETIME,
#EndDate datetime
)
RETURNS TABLE
AS
RETURN
SELECT EmpId, COUNT(*) as DaysWorked
FROM
(
SELECT DISTINCT EmpId,CAST(TimeIn AS DATE) AS [Date]
FROM myTable
WHERE TimeIn IS NOT NULL
AND CAST(TimeIn AS DATE) BETWEEN #StartDate AND #EndDate
)T
GROUP BY EmpId
To use the function you can write your queries like following.
DECLARE #StartDate datetime
DECLARE #EndDate datetime
SET #StartDate = '2018-01-01'
SET #EndDate = '2018-01-31'
select * from [dbo].[fnGetWorkedDays](#StartDate,#EndDate)
If you want this to work as a scalar value function, in that case you need to pass EmpId as parameter like following.
CREATE FUNCTION [dbo].[fnGetWorkedDaysEmpWise]
(
#StartDate AS DATETIME,
#EndDate datetime,
#EmpId INT
)
RETURNS INT
AS
BEGIN
DECLARE #RetunCount INT
SELECT #RetunCount = COUNT(DISTINCT CAST(TimeIn AS DATE))
FROM myTable
WHERE TimeIn IS NOT NULL
AND CAST(TimeIn AS DATE) BETWEEN #StartDate AND #EndDate
AND EmpID=#EmpId
RETURN #RetunCount
END
And you can use it directly in your select clause like following.
DECLARE #StartDate datetime
DECLARE #EndDate datetime
SET #StartDate = '2018-01-01'
SET #EndDate = '2018-01-31'
SELECT [dbo].[fnGetWorkedDaysEmpWise](#StartDate,#EndDate,1)
Is this what you are looking for .?
CREATE FUNCTION [dbo].[fnGetWorkedDays] (#StartDate datetime, #EndDate datetime)
RETURNS #DaysWorked TABLE
(
Empid Int,
DaysWorked Int
)
AS
BEGIN
insert into #DaysWorked (Empid,DaysWorked)
SELECT EmpId, COUNT(*) as DaysWorked
FROM
(
SELECT DISTINCT EmpId,CAST(TimeIn AS DATE) AS [Date]
FROM myTable
WHERE TimeIn IS NOT NULL
AND CAST(TimeIn AS DATE) BETWEEN #StartDate AND #EndDate
)T
GROUP BY EmpId
ORDER BY EmpId
RETURN
END
select * from [fnGetWorkedDays]('2018-01-01','2018-01-31')

Get dates between 2 dates with equal intervals

I have 2 dates 01/04/2017 and 30/04/2017. I want all the dates between these 2 dates with 7 days interval.
Expected Output :
01/04/2017
08/04/2017
15/04/2017
22/04/2017
29/04/2017
Please help!!
DECLARE #StartDate DATETIME,
#EndDate DATETIME
SELECT #StartDate = '2017-04-01',
#EndDate = '2017-04-30'
SELECT DATEADD(DAY, number*7, #StartDate)
FROM master.dbo.spt_values
WHERE type='P'
AND #EndDate >= DATEADD(DAY, number*7, #StartDate)
One method would be to use a Calendar table. Then return the results from there using the modulus to get the 7th rows:
WITH Dates AS(
SELECT *,
ROW_NUMBER() OVER (ORDER BY [date]) AS RN
FROM DateTable
WHERE [Date] BETWEEN '20170401' AND '20170430')
SELECT *
FROM Dates
WHERE (RN - 1) % 7 = 0;
I've used this solution, as from your post you imply that you might supply any date range, and that the 1st day may not necessarily be a Monday (or other specific day).
Try this
DECLARE #STRT DATETIME='04/01/2017',#END DATETIME ='04/30/2017'
;WITH CTE
AS
(
SELECT
MyDate = CAST(#STRT AS DATETIME)
UNION ALL
SELECT
MyDate = CAST(MyDate AS DATETIME)+7
FROM CTE
WHERE CAST(MyDate AS DATETIME)+7 < CAST(#END AS DATETIME)
)
SELECT
*
FROM CTE
result
Declare #StartDate DATE=CONVERT(DATE,'01/04/2017',104),#EndDate DATE=CONVERT(DATE,'01/12/2017',104)
Declare #String NVARCHAR(MAX)=''
WHILE (#StartDate<=#EndDate AND DATEDIFF(wk,#StartDate,#EndDate)>=0)
BEGIN
SET #String=#String+CONVERT(NVARCHAR(100),#StartDate)+CHAR(10)+CHAR(13)
SET #StartDate=DATEADD(d,7,#StartDate)
END
PRINT #String
GO

SQL UDF to return an array of dates

I have currently written a small UDF (Table Value Type) to return a table of dates between a start and end date. Start and End dates are already retrieved from another table.
Start_End_Table
WeekNumber | Start Date | End Date
Function:
CREATE FUNCTION [dbo].[DatesBetween](#startDate datetime, #endDate datetime)
RETURNS #dates TABLE (
DateValue datetime NOT NULL
)
AS
BEGIN
WHILE (#startDate <= #endDate) BEGIN
INSERT INTO #dates VALUES (#startDate);
SET #startDate = DATEADD(day, 1, #startDate);
END;
RETURN;
END;
Until now, I have been using this function in the following manner,
SELECT * FROM [dbo].[DatesBetween](#startDate datetime, #endDate datetime);
Going forward I need to get start, end dates from Start_End_Date table and call the function. Thus I do not think returning a table is an option any longer. How can I get this function to return an array of dates instead of a table? Or is there any other way I could wrap this up?
Try CROSS APPLY as it will run the function for each row in the JOINed table:
SELECT [Date Value]
FROM dbo.Start_End_Table dates
CROSS APPLY [dbo].[DatesBetween](dates.StartDate, dates.EndDate)
WHERE [Week Number]=#WeekNumber;
A sample output would be something like for a start date = 1/1/2014, end date = 1/3/2014, #weeknumber = 1:
| DateValue
1/1/2014
1/2/2014
1/3/2014
SQL Fiddle
EDIT:
Try the following Inline Table-Valued Function as it will be much faster than your multi-line TVF due to:
Inline TVFs are inherently faster than multi-line TVFs
It is set-based rather than relying on a WHILE loop
--
CREATE FUNCTION dbo.GetDates(#StartDate DATETIME, #EndDate DATETIME)
RETURNS TABLE
AS RETURN
WITH cte AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS [RowNum]
FROM sys.objects WITH (NOLOCK)
)
SELECT DATEADD(DAY, (cte.[RowNum] - 1), #StartDate) AS [DateValue]
FROM cte
WHERE cte.[RowNum] < (DATEDIFF(DAY, #StartDate, #EndDate) + 2);
GO
SELECT * FROM dbo.GetDates('1/1/2014', '1/3/2014');

Resources