Looping with Summation of Fields and dateTime as a counter SQL - 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

Related

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

Create a calendar day table

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
----------------------

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'

Query to return all the days of a month

This problem is related to this, which has no solution in sight: here
I have a table that shows me all sessions of an area.
This session has a start date.
I need to get all the days of month of the start date of the session by specific area (in this case)
I have this query:
SELECT idArea, idSession, startDate FROM SessionsPerArea WHERE idArea = 1
idArea | idSession | startDate |
1 | 1 | 01-01-2013 |
1 | 2 | 04-01-2013 |
1 | 3 | 07-02-2013 |
And i want something like this:
date | Session |
01-01-2013 | 1 |
02-01-2013 | NULL |
03-01-2013 | NULL |
04-01-2013 | 1 |
........ | |
29-01-2013 | NULL |
30-01-2013 | NULL |
In this case, the table returns me all the days of January.
The second column is the number of sessions that occur on that day, because there may be several sessions on the same day.
Anyone can help me?
Please try:
DECLARE #SessionsPerArea TABLE (idArea INT, idSession INT, startDate DATEtime)
INSERT #SessionsPerArea VALUES (1,1,'2013-01-01')
INSERT #SessionsPerArea VALUES (1,2,'2013-01-04')
INSERT #SessionsPerArea VALUES (1,3,'2013-07-02')
DECLARE #RepMonth as datetime
SET #RepMonth = '01/01/2013';
WITH DayList (DayDate) AS
(
SELECT #RepMonth
UNION ALL
SELECT DATEADD(d, 1, DayDate)
FROM DayList
WHERE (DayDate < DATEADD(d, -1, DATEADD(m, 1, #RepMonth)))
)
SELECT *
FROM DayList t1 left join #SessionsPerArea t2 on t1.DayDate=startDate and t2.idArea = 1
This will work:
DECLARE #SessionsPerArea TABLE (idArea INT, idSession INT, startDate DATE)
INSERT #SessionsPerArea VALUES
(1,1,'2013-01-01'),
(1,2,'2013-01-04'),
(1,3,'2013-07-02')
;WITH t1 AS
(
SELECT startDate
, DATEADD(MONTH, DATEDIFF(MONTH, '1900-01-01', startDate), '1900-01-01') firstInMonth
, DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, '1900-01-01', startDate) + 1, '1900-01-01')) lastInMonth
, COUNT(*) cnt
FROM #SessionsPerArea
WHERE idArea = 1
GROUP BY
startDate
)
, calendar AS
(
SELECT DISTINCT DATEADD(DAY, c.number, t1.firstInMonth) d
FROM t1
JOIN master..spt_values c ON
type = 'P'
AND DATEADD(DAY, c.number, t1.firstInMonth) BETWEEN t1.firstInMonth AND t1.lastInMonth
)
SELECT d date
, cnt Session
FROM calendar c
LEFT JOIN t1 ON t1.startDate = c.d
It uses simple join on master..spt_values table to generate rows.
Just an example of calendar table. To return data for a month adjust the number of days between < 32, for a year to 365+1. You can calculate the number of days in a month or between start/end dates with query. I'm not sure how to do this in SQL Server. I'm using hardcoded values to display all dates in Jan-2013. You can adjust start and end dates for diff. month or to get start/end dates with queries...:
WITH data(r, start_date) AS
(
SELECT 1 r, date '2012-12-31' start_date FROM any_table --dual in Oracle
UNION ALL
SELECT r+1, date '2013-01-01'+r-1 FROM data WHERE r < 32 -- number of days between start and end date+1
)
SELECT start_date FROM data WHERE r > 1
/
START_DATE
----------
1/1/2013
1/2/2013
1/3/2013
...
...
1/31/2013