Create a calendar day table - sql

I am trying to write a code where i can plug in a date and my table below will populate the expected date table with all the date for the particular month from CD1(Calendar Day 1) all the way to CD30 or CD31 or in February case CD28. I know i should begin my code with something like
Declare #startdate as datetime
Set #startdate = '20170401'
But after that I get confused with the DateAdd and DatePart code to create this query to produce the results
date rule | expected date |
----------------------------
| CD1 | 4/1/2017 |
| CD2 | 4/2/2017 |
| CD3 | 4/3/2017 |
| CD4 | 4/4/2017 |
| CD5 | 4/5/2017 |
| CD6 | 4/6/2017 |
Can anyone provide any assistance?

Try this,
Declare #startdate as datetime
Set #startdate = '20170401'
;with cte as
(
select #startdate dt,1 ruleid
union ALL
select dateadd(day,1,dt)
,ruleid+1
from cte
where
dt<dateadd(day,-1,dateadd(month, datediff(month,0,#startdate)+1,0))
)
select *,'CD'+cast(ruleid as varchar) CalenderRule
from cte

DECLARE #startdate datetime = '2017-04-01'
DECLARE #startdate_for_loop datetime
SET #startdate_for_loop = #startdate
CREATE TABLE #T (date_rule nvarchar(100), exp_date datetime)
declare #x int = 1
WHILE MONTH(#startdate) = MONTH(#startdate_for_loop)
BEGIN
INSERT INTO #T VALUES ('CD' + CAST(#x as nvarchar(max)), #startdate_for_loop)
SET #x = #x + 1
SET #startdate_for_loop = DATEADD(DD, 1, #startdate_for_loop)
END
SELECT * FROM #T

Try below query, this will give you the required output:
DECLARE #STARTDATE DATETIME
SET #STARTDATE= CAST(MONTH(CURRENT_TIMESTAMP) AS VARCHAR(100))+'/'+'01'+'/'+CAST(YEAR(CURRENT_TIMESTAMP) AS VARCHAR(100))
;WITH MONTHDATA
AS
(SELECT #STARTDATE MONTHDATE
UNION ALL
SELECT DATEADD(D,1,MONTHDATE) FROM MONTHDATA WHERE MONTHDATE<DATEADD(D,-1,DATEADD(M,1,#STARTDATE))
)
SELECT 'CD'+CAST( (ROW_NUMBER()OVER (ORDER BY MONTHDATE)) AS VARCHAR(100))DATE_RULE,CONVERT(VARCHAR,MONTHDATE,101)MONTHDATE FROM MONTHDATA
OUTPUT
----------------------
DATE_RULE MONTHDATE
----------------------
CD1 03/01/2017
CD2 03/02/2017
CD3 03/03/2017
.
.
.
CD29 03/29/2017
CD30 03/30/2017
CD31 03/31/2017
----------------------

Related

How to build a recursive table in inline-table-valued function (iTVF)?

I have four parameters to be accepted by the function:
#FromDate DATE, #ToDate DATE, #DataInterval INT, #RangeInterval INT
The result set is obtained using a CTE:
;WITH Dates_CTE (FromDt, ToDt) AS
(
SELECT
DATEADD(D,-#RangeInterval,#FromDate) AS [FromDt],
#FromDate AS [ToDt]
UNION ALL
SELECT
DATEADD(D,#DataInterval,D.FromDt) AS [FromDt],
DATEADD(D,#DataInterval,D.ToDt) AS [ToDt]
FROM Dates_CTE D
WHERE D.ToDt <= #ToDate
)
But using a CTE makes it a multi-table-valued function and it becomes a performance-killer.
Is there another way to achieve this in an inline function?
The example of the desired result with the input #FromDate = '20180701', #ToDate = '20180901', #DataInterval = 5, #RangeInterval = 30 is:
+------------+------------+
| FromDate | ToDate |
+------------+------------+
| 2018-06-01 | 2018-07-01 |
| 2018-06-06 | 2018-07-06 |
| 2018-06-11 | 2018-07-11 |
| 2018-06-16 | 2018-07-16 |
| 2018-06-21 | 2018-07-21 |
| 2018-06-26 | 2018-07-26 |
| 2018-07-01 | 2018-07-31 |
| 2018-07-06 | 2018-08-05 |
| 2018-07-11 | 2018-08-10 |
| 2018-07-16 | 2018-08-15 |
| 2018-07-21 | 2018-08-20 |
| 2018-07-26 | 2018-08-25 |
| 2018-07-31 | 2018-08-30 |
| 2018-08-05 | 2018-09-04 |
+------------+------------+
How can we achieve this result in an iTVF? Thanks in advance.
Really you need no recursion. Use a table of numbers. Here the table of numbers is generated on the fly it can be persisted in the db for better perfomance.
DECLARE #FromDate DATE, #ToDate DATE, #DataInterval INT, #RangeInterval INT;
SELECT #FromDate = '20180701', #ToDate = '20180901', #DataInterval = 5, #RangeInterval = 30;
-- table of 1000 numbers starting 0
with t0(n) as (
select n
from (
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
) t(n)
),nmbs as(
select row_number() over(order by t1.n) - 1 n
from t0 t1, t0 t2, t0 t3
)
select
DATEADD(D, #DataInterval*n - #RangeInterval, #FromDate) FromDate
,DATEADD(D, #DataInterval*n, #FromDate) ToDate
from nmbs
where DATEADD(D, #DataInterval*n , #FromDate) <= #ToDate;
You may need to adjust where conditions per your requierments.
Demo with the persisted table of numbers (aka tally table).
You can use a tally table inside your inline table-value function.
CREATE TABLE Numbers (N INT PRIMARY KEY NOT NULL);
INSERT INTO Numbers
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) -1 N
FROM
(
VALUES (NULL), (NULL), (NULL), (NULL), (NULL)
) T1(V) CROSS JOIN
(
VALUES (NULL), (NULL), (NULL), (NULL), (NULL)
) T2(VV) CROSS JOIN
(
VALUES (NULL), (NULL), (NULL), (NULL), (NULL) --Add Values as needed
) T3(VVV);
Then create your function as
CREATE FUNCTION dbo.MyFunc
(
#FromDate DATE,
#ToDate DATE,
#DataInterval INT,
#RangeInterval INT
)
RETURNS TABLE
RETURN
SELECT DATEADD(Day, (#DataInterval * N) - #RangeInterval, #FromDate) FromDate,
DATEADD(Day, #DataInterval * N, #FromDate) ToDate
FROM Numbers
WHERE DATEADD(Day, #DataInterval * N, #FromDate) <= #ToDate;
And finally, use it as
SELECT *
FROM dbo.MyFunc ('20180701', '20180901', 5, 30);
Online Demo
you can use while :
declare #FromDate DATE = '20180701', #ToDate DATE= '20180901', #DataInterval INT= 5, #RangeInterval INT= 30,#date date ,#r int =0
declare #table table (d1 date, d2 date)
set #date= #FromDate
while (#date <#ToDate )
begin
insert into #table
select dateadd(d,#r,#FromDate ),DATEADD(D,-#RangeInterval+#r,#FromDate)
set #r=#r+#DataInterval
set #date = dateadd(d,#r,#FromDate )
end
select * from #table

Condition for MAX(Booking) within a specific period

I have a query that is looping between core working hours(7:30-16:30).
This query returns the most recent bookings per group till date but I can't seem to figure out the condition to make sure the most recent returned was that recorded as of the loop time,
i.e at 07:30 today, the most recent bookings from groups F01-F03 were ... regardless of the date the booking was recorded.
My query looks like this at the moment:
DECLARE #ALLBOOKINGS TABLE (BOOKING_GRP NVARCHAR(3), BOOKING_DATE DATETIME)
DECLARE #MAX_BACKDATE DATE = dateadd(DAY,datediff(DAY,0,getdate())-5,0),
#CURRENT_TIME TIME(0) = GETDATE(),
#START_TIME TIME(0) = '07:30:00';
BEGIN
INSERT INTO #ALLBOOKINGS
SELECT BOOKING_GRP, BOOKING_DATE
FROM TABLE1
WHERE BOOKING_DATE >= #MAX_BACKDATE
END
WHILE (#START_TIME <= #CURRENT_TIME)
BEGIN
SELECT *
FROM #ALLBOOKINGS AS T1
WHERE BOOKING_DATE = (SELECT MAX(BOOKING_DATE) FROM #ALLBOOKINGS WHERE BOOKING_GRP = T1.BOOKING_GRP)
--AND
--cast(BOOKING_DATE as time(0)) > #START_TIME AND cast(BOOKING_DATE as time(0)) < #CURRENT_TIME <<<<<<<<<<<<<<<<< this does not work, only returns max till now
SET #START_TIME = DATEADD(MINUTE,10,#START_TIME) END
Table:
+-------+-----------+-----------+-------------+-----------+-------------+
| BOOKING_GRP | BOOKING_DATE | #START_TIME |
+-------+-----------+-----------+-------------+-----------+-------------+
| F01 | 2018-12-10 11:48:50.363 | 07:30:00 |
| F02 | 2018-12-10 11:22:06.367 | 07:30:00 |
| F03 | 2018-12-10 11:21:14.240 | 07:30:00 |
+-------+-----------+-----------+-------------+-----------+-------------+
FAKE DATA - CURRENT RESULT
+-------+-----------+-----------+-------------+-----------+-------------+
| BOOKING_GRP | BOOKING_DATE | #START_TIME |
+-------+-----------+-----------+-------------+-----------+-------------+
| F01 | 2018-12-07 10:34:50.363 | 07:30:00 |
| F02 | 2018-12-10 12:32:06.367 | 07:30:00 |
| F03 | 2018-12-06 11:37:14.240 | 07:30:00 |
+-------+-----------+-----------+-------------+-----------+-------------+
EXPECTED RESULT - WITHIN THE LAST 5 DAYS, THE MOST RECENT BOOKINGS MADE PER GROUP AS OF 07:30 TODAY
I'm not completely sure I understand the question, but you could give the following query a shot:
SELECT Booking_Group, MAX (Booking_Date) AS MaxDate
FROM SomeTable AS ST
WHERE Booking_Date BETWEEN DATEADD (MINUTE, 30, DATEADD (HH, 7, CAST (CAST (GETDATE() AS DATE) AS DATETIME)) ) AND GETDATE()
GROUP BY Booking_Group
There is no reason to loop.
declare #start datetime = '20180101'
,#end datetime = getdate()
select BOOKING_GRP,max(BOOKING_DATE) as LATEST_BOOKING_DATE
from Table1
where BOOKING_DATE between #start and #end
group by BOOKING_GRP
Solved by adding DATE to the while condition
DECLARE #STARTTIME dateTIME = dateadd(day, datediff(day, 0, getdate()), 0) + '07:30',
#CURRENTTIME DATETIME= GETDATE()
WHILE (#START_TIME <= #CURRENT_TIME)
BEGIN
SELECT *
FROM #ALLBOOKINGS AS T1
WHERE BOOKING_DATE = (SELECT MAX(BOOKING_DATE) FROM #ALLBOOKINGS WHERE BOOKING_GRP = T1.BOOKING_GRP
AND DATE_BOOKED < #STARTTIME
)
SET #START_TIME = DATEADD(MINUTE,10,#START_TIME) END

sql server print all dates between two date columns

i have a table containing 3 columns. ID,Start_Date,End_Date.i want to print all the days between Start_Date and End_Date along with ID.
For Example i have table
+----+------------+------------+
| ID | Start_Date | End_Date |
+----+------------+------------+
| 1 | 2017-01-01 | 2017-01-05 |
+----+------------+------------+
and i want result like
+----+------------+
| ID | Date |
+----+------------+
| 1 | 2017-01-01 |
| 1 | 2017-01-02 |
| 1 | 2017-01-03 |
| 1 | 2017-01-04 |
| 1 | 2017-04-05 |
+----+------------+
Use Common table expression :
DECLARE #StartDT DATETIME = '2017-01-01'
DECLARE #EndDT DATETIME = '2017-01-05'
DECLARE #Id INT = 1
;WITH CTE (_id , _Date)AS
(
SELECT #Id , #StartDT
UNION ALL
SELECT #Id , DATEADD(DAY,1,_Date)
FROM CTE
WHERE _Date < #EndDT
)
SELECT * FROM CTE
Create a so called tally table (see e. g. here: https://dwaincsql.com/2014/03/27/tally-tables-in-t-sql/) and use it to create all dates between "from" and "to" date.
SELECT TOP 1000000 N=IDENTITY(INT, 1, 1)
INTO dbo.Tally
FROM master.dbo.syscolumns a CROSS JOIN master.dbo.syscolumns b;
go
declare #dateFrom datetime = '20170101';
declare #dateTo datetime = '20170105';
select dateadd(day, N - 1, #dateFrom)
from Tally
where N between 1 and datediff(day, #dateFrom, #dateTo) + 1
Or:
select dateadd(day, t.N - 1, o.DateFrom)
from Tally t
cross join OtherTable o
where t.N between 1 and datediff(day, o.DateFrom, o.DateTo) + 1
A tally table is very useful for such cases, it could also be filled with dates in a second column, starting from 1900-01-01 or so.
DECLARE #StartDT DATETIME = '2017-01-01'
DECLARE #EndDT DATETIME = '2017-01-05'
DECLARE #Id INT = 1
SELECT RANK() OVER (
ORDER BY (SELECT 1)) AS SeqNo
,CAST (Start_Date AS DATE) AS Start_Date
FROM (
SELECT #StartDT + Row_Number() OVER (ORDER BY Rno) - 1 AS Start_Date
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) - 1 AS Rno
FROM master..spt_values
) Dt
) Dt2
WHERE Dt2.Start_Date <= #EndDT
OutPut
SeqNo Start_Date
--------------------
1 2017-01-01
1 2017-01-02
1 2017-01-03
1 2017-01-04
1 2017-01-05

Result that corresponds with customer and date

How will I get the all the date even though there is no order between two dates?
Sample Table
OrderID | Date | CusID
33942 | 6-21-2014 | 6005
34059 | 6-20-2014 | 4003
53333 | 6-23-2014 | 6005
59234 | 6-23-2014 | 4003
How I could have this result?
CusID | Date | OrderID |
4003 | 6-20-2014 | 34059 |
4003 | 6-21-2014 | null |
4003 | 6-22-2014 | null |
4003 | 6-23-2014 | 59234 |
6005 | 6-20-2014 | null |
6005 | 6-21-2014 | 33942 |
6005 | 6-22-2014 | null |
6005 | 6-23-2014 | 53333 |
This what I did so far.
I create a calendar table
CREATE TABLE #Calendar
(
[CalendarDate] DATETIME
)
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #EndDate = GETDATE()
SET #StartDate = DATEADD(Year, -1, #EndDate)
WHILE #StartDate <= #EndDate
BEGIN
INSERT INTO #Calendar
(
CalendarDate
)
SELECT
#StartDate
SET #StartDate = DATEADD(dd, 1, #StartDate)
END
Then here is my query but does not give me the needed result
Select t.CusID, c.CalendarDate, t.OrderID
From #Calendar c
left outer join
#temp t
ON
CONVERT(VARCHAR(10), c.CalendarDate, 112) = CONVERT(VARCHAR(10), t.Date, 112)
Where c.CalendarDate Between '6-20-2014' and '6-23-2014'
Order By t.Name
First off, your calendar table is broken. You are using a datetime stamp, and capturing the current time stamp in your table. Better to write the correct data, than handle it later.
Two fixes for this, use a DATE format or convert your date to remove the timestamp. I chose the latter.
From there, you narrow down to the # of days being queried, and the # of customers in those days and generate a cross join of those two subsets.
DECLARE #ORDER TABLE (ORDERID INT, ordDATE DATETIME, CUSID INT)
INSERT INTO #ORDER (ORDERID, ordDATE, CUSID)
VALUES (33942,'6-21-2014', 6005),
(34059,'6-20-2014',4003),
(53333,'6-23-2014',6005),
(59234,'6-23-2014',4003)
declare #Calendar table ([CalendarDate] DATETIME)
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #EndDate = GETDATE()
SET #StartDate = DATEADD(Year, -1, #EndDate)
WHILE #StartDate <= #EndDate
BEGIN
INSERT INTO #Calendar
(
CalendarDate
)
SELECT
CONVERT(DATETIME,FLOOR(CONVERT(FLOAT, #StartDate)))
SET #StartDate = DATEADD(dd, 1, #StartDate)
END
; with dates as
(
SELECT DISTINCT CalendarDate
FROM #Calendar
WHERE CalendarDate BETWEEN '2014-06-20' AND '2014-06-23'
),
orders as
(
SELECT DISTINCT CUSID
FROM #ORDER
WHERE ordDATE BETWEEN '2014-06-20' AND '2014-06-23'
),
cp AS
(
SELECT *
FROM dates
CROSS JOIN orders
)
SELECT cp.CUSID, cp.CalendarDate, O.ORDERID
FROM CP
LEFT OUTER JOIN #ORDER O
ON cp.CalendarDate = O.ordDATE
AND cp.CUSID = O.CUSID
If you wanted, you could move the WHERE clause outside the CTEs and into the final select statement, but the resulting cp table will be significantly larger, and slower on big datasets.
create the cartesian product of dates and customers and then join
with cte as
(
select caldate, custid
from calendar, customers
)
select *
from cte
left join temp o
on cte.custid = o.custid
and cast(caldate as date) = cast(orderdate as date)
where caldate between '20140620' and '20140623'

Looping with Summation of Fields and dateTime as a counter SQL

I have a 3 fields ( ID, Price, Date )
and it looks like this..
|ID| |Price| | Date |
--------------------------------------------
|001| |150.00| | 2007-01-01 11:48:18.000 |
|002| |150.00| | 2007-01-01 15:57:19.000 |
|003| |150.00| | 2007-01-02 13:26:12.000 |
|004| |150.00| | 2007-01-03 10:31:14.000 |
etc etc
and I need to display the TOTAL AMOUNT of sales for EACH DAY for a certain period of time.
So when I put January 1 to January 6...
it should be
| Days | Total Sales |
-------------------------------
| January 1 | --some amount |
| January 2 | --some amount |
| January 3 | --some amount |
| January 4 | --some amount |
| January 5 | --some amount |
| January 6 | --some amount |
I just cant figure it out and Im stuck with this code :) ...
DECLARE #StartDate dateTime,#EndDate dateTime, #TotalSales integer
SET #StartDate = '2007-01-02 11:41:19.000'
SET #EndDate = '2007-01-02 11:46:06.000'
SET #TotalSales = 0
while ( #StartDate = '2007-01-02 11:41:19.000' )
BEGIN
--Some codes
END
thanks :)
You don't need a loop, use set operations whenever possible:
DECLARE #StartDate dateTime,#EndDate dateTime
SET #StartDate = convert(DateTime,'2007-01-01 11:41:19.000',102)
SET #EndDate = convert(DateTime,'2007-01-04 11:46:06.000',102)
;WITH CTE AS (
SELECT ID,Price,[Date]
FROM Sales
WHERE [Date] Between #StartDate AND #EndDate
)
SELECT DATENAME( month ,[Date] ) + ' ' + DATENAME( day ,[Date] ) AS Days
, SUM(Price)AS 'Total Sales'
FROM CTE
GROUP BY DATENAME( month ,[Date] ) + ' ' + DATENAME( day ,[Date] )
SQL-Fiddle Demo
This is similar to Tim Schmelter's solution except that it handles for days that do not have any sales data:
DECLARE #StartDate DATETIME, #EndDate DATETIME
SELECT #StartDate = '01-01-2007', #EndDate = '01-06-2007'
;WITH DateRange ([Date]) AS
(
SELECT
#StartDate [Date]
UNION ALL
SELECT
DATEADD(DAY, 1, [Date]) [Date]
FROM
DateRange
WHERE
[Date] < #EndDate
)
SELECT
DATENAME(MONTH, d.[Date]) + ' ' + DATENAME(DAY, d.[Date]) AS Days,
SUM(ISNULL(Price, 0)) AS [Total Sales]
FROM
DateRange d
LEFT JOIN
Sales s
ON
d.[Date] = DATEADD(DAY, DATEDIFF(DAY, 0, s.[Date]), 0)
GROUP BY
DATENAME(MONTH, d.[Date]) + ' ' + DATENAME(DAY, d.[Date])
SQL Fiddle Example