Starting result set at certain point in results - sql

I've got an issue I'm a little stuck with. Basically, I have a table with 4 columns, one column is a date and the results returned are dependent on time of day. If the time is before 6pm return the data as is but if after 6, find the next available day.
This is some sample data assuming today is 2014-07-10:
+----------+---------------------+-----------------+----------------+
| Workzone | ScheduleDate | NextDayDelivery | StdDayDelivery |
+----------+---------------------+-----------------+----------------+
| SWANS | 2014-07-11 00:00:00 | 1 | 1 |
| SWANS | 2014-07-12 00:00:00 | 0 | 1 |
| SWANS | 2014-07-13 00:00:00 | 0 | 0 |
| SWANS | 2014-07-14 00:00:00 | 0 | 1 |
| SWANS | 2014-07-15 00:00:00 | 0 | 1 |
| SWANS | 2014-07-16 00:00:00 | 0 | 1 |
| SWANS | 2014-07-17 00:00:00 | 0 | 1 |
| SWANS | 2014-07-18 00:00:00 | 0 | 1 |
| SWANS | 2014-07-19 00:00:00 | 0 | 1 |
| SWANS | 2014-07-20 00:00:00 | 0 | 0 |
+----------+---------------------+-----------------+----------------+
This is the query so far:
DECLARE #hour AS Int
SET #hour = DatePart(hour, getdate())
SET #hour = 19
-- Work out if the time is before 18:00. If it is then this is the easy bit, use the data as is.
IF (#hour <= 18)
BEGIN
SELECT
[Workzone]
,[ScheduleDate]
,[NextDayDelivery]
,[StdDayDelivery]
FROM [tbl_Sys_QuickstartDeliveryQuota]
END
ELSE
BEGIN
-- OTHER QUERY HERE --
END
So the above simply checks the hour and performs a query based on that. If before 6pm then simply view the data in the table as it is otherwise execute the other query which is what i'm stuck on.
The sample data above is generated for the next day onwards so if today is the 2014-07-10 then the data will start from the 2014-07-11. The NextDayDelivery column will always start with 1 if the following day is a weekday or saturday with all following days set to 0 and the StdDayDelivery column will have 1 if on a weekday or saturday or 0 if a sunday or bank holiday.
What I need to do is adjust the results if the time is after 6 as this will change the next day delivery.
My thoughts are to use the StdDayDelivery column to work out the next available delivery day so then in the sample data provided the 2014-07-12 will become the first row (if today is the 11th) of the results with NextDayDelivery being set to 1 based on the StdDayDelivery being set to 1. If todays date was the 2014-07-12 then the results will start from the 14th as the 13th is a Sunday so the next available delivery day is the 14th so again, NextDayDelivery is a 1 on the 14th.
I hope this is making sense.
The two expected outputs would be:
If today is the Friday the 11th, the results would be:
2014-07-12 1 1
2014-07-13 0 0
2014-07-14 0 1
2014-07-15 0 1
2014-07-16 0 1
2014-07-17 0 1
2014-07-18 0 1
If today is the Sat the 12th, the results would be:
2014-07-13 0 0
2014-07-14 1 1
2014-07-15 0 1
2014-07-16 0 1
2014-07-17 0 1
2014-07-18 0 1
(13th values set to 0 as StdDayDelivery is showing 0 because it's a sunday)

Looks like that CROSS APPLY may help you with this task.
Create table function for [NextDayDelivery] calculation
IF OBJECT_ID(N'dbo.GetData', N'TF') IS NOT NULL
DROP FUNCTION dbo.GetData;
GO
CREATE FUNCTION GetData(#ScheduleDate AS DATETIME, #testDay AS DATETIME)
RETURNS #VV TABLE
(
[NextDayDelivery] bit
)
AS
BEGIN
INSERT INTO #VV
SELECT
CASE
WHEN (DATEDIFF(dd, #testDay, #ScheduleDate)=1 AND DATEPART(dw, #ScheduleDate)>1) OR
(DATEDIFF(dd, #testDay, #ScheduleDate)=2 AND DATEPART(dw, #testDay)=7) THEN 1
ELSE 0
END
AS [NextDayDelivery]
RETURN
END
GO
Use this function in SELECT
DECLARE #testDay DATETIME
SET #testDay = DATEADD(d,1,GETUTCDATE())
-- SET #testDay = GETUTCDATE()
SELECT t.[ScheduleDate], VV.[NextDayDelivery], t.StdDayDelivery
FROM [tbl_Sys_QuickstartDeliveryQuota] AS t
CROSS APPLY GetData(t.[ScheduleDate], #testDay) AS VV
where t.[ScheduleDate] > #testDay
Note: My code supposes that first day of week is Sunday. I.e. SELECT DATEPART(dw, '2014-07-12') returns me 7. Correct my code if you use other culture.

Related

how to display a weekday(set it to zero) of datepart function when there are no occurence of the day in the table

Sat Naam
I have a table in my databse that register of sales and stores. some stores are close on sundays so there are no sales on that day
i have the following query code to get the number of sale
SELECT DATENAME(WEEKDAY, [Data_Hora_Ficheiro_fim]),[Store_id],count([sales_no])
FROM [GPOS].[dbo].[V_Period_stats_with_CAE_GC]
group by [Store_id],[Store name],DATEPART(WEEKDAY,[Data_Hora_Ficheiro_fim])DATENAME(WEEKDAY, [Data_Hora_Ficheiro_fim])
order by DATEPART(WEEKDAY, [Data_Hora_Ficheiro_fim]),DATENAME(WEEKDAY, [Data_Hora_Ficheiro_fim]),[Store_id]
of the query would be something like(store 4 close on sunday)
day Storeid num_sales
----------|--------|----------
Monday 3 90
Tuesday 3 70
Wednesday 3 20
Thursday 3 60
Friday 3 96
Saturday 3 98
Sunday 3 200
Monday 4 90
Tuesday 4 70
Wednesday 4 20
Thursday 4 60
Friday 4 96
Saturday 4 98
so what changes do i need to do to my query in order to the output also include the line
Sunday 4 0
thanks in advance
One way would be to cross join the weekdays and distinct store_id to get all combinations of the two, and left join your view.
Using a common table expression for the first part and a Table Value Constructor (Transact-SQL)
;with Weekdays as (
select
day=datename(weekday, convert(datetime,t.n))
, s.Store_id
from (values(0),(1),(2),(3),(4),(5),(6)) t(n)
cross join (
select distinct Store_id
from [gpos].[dbo].[V_Period_stats_with_cae_gc]
) s
)
select
w.day
, w.[Store_id]
, count(v.[sales_no])
from Weekdays w
left join [gpos].[dbo].[V_Period_stats_with_cae_gc] v
on w.day = datename(weekday, v.[Data_Hora_Ficheiro_fim])
and w.Store_id = v.Store_id
group by
w.day
w.Store_id
order by
w.day
, w.Store_id
rextester demo (modified for provided example data): http://rextester.com/JZLF76266
returns:
+-----------+---------+-----------+
| day | storeid | num_sales |
+-----------+---------+-----------+
| Monday | 3 | 90 |
| Tuesday | 3 | 70 |
| Wednesday | 3 | 20 |
| Thursday | 3 | 60 |
| Friday | 3 | 96 |
| Saturday | 3 | 98 |
| Sunday | 3 | 200 |
| Monday | 4 | 90 |
| Tuesday | 4 | 70 |
| Wednesday | 4 | 20 |
| Thursday | 4 | 60 |
| Friday | 4 | 96 |
| Saturday | 4 | 98 |
| Sunday | 4 | 0 |
+-----------+---------+-----------+
If you have a table where store_id is unique (e.g. dbo.Store) then that would be an alternative source for select distinct Store_id ....
Another option would be to use a calendar table restricted to the date range you are interested in cross joined with the Store_id. This would allow you to join on an actual date data type instead of the datename() function, which would could improve performance.
Number and Calendar table reference:
Generate a set or sequence without loops - 2 - Aaron Bertrand
The "Numbers" or "Tally" Table: What it is and how it replaces a loop - Jeff Moden
Creating a Date Table/Dimension in sql Server 2008 - David Stein
Calendar Tables - Why You Need One - David Stein
Creating a date dimension or calendar table in sql Server - Aaron Bertrand

T-SQL Generate sequence of days after certain date

I have a sequence of days as an array like the following:
DECLARE #days VARCHAR(MAX) = N'Monday, Wednesday, Friday'
And a n amount of occurrences like:
DECLARE #occurrences INT = 10
In this way I can generate a table using the following query and a custom table called Numbers which contains 1,000,000 numbers:
SELECT TOP #occurrences
Item
FROM
dbo.SplitStrings_Numbers(#days, ',')
CROSS JOIN Numbers
ORDER BY n
The result is the following:
|----------------------|
| 1 | Monday |
|----------------------|
| 2 | Wednesday |
|----------------------|
| 3 | Friday |
|----------------------|
| 4 | Monday |
|----------------------|
| 5 | Wednesday |
|----------------------|
| 6 | Friday |
|----------------------|
Now what I need to complete this function is to start the sequence with the first day that occur based on a specified date.
So, when I declare start from 1st of September
DECLARE #start DATETIME = '2015-09-01'
The result should look like the following:
|------------------------------------|
| 1 | Wednesday | 2015-09-02 |
|------------------------------------|
| 2 | Friday | 2015-09-04 |
|------------------------------------|
| 3 | Monday | 2015-09-07 |
|------------------------------------|
| 4 | Wednesday | 2015-09-09 |
What I cannot calculate is this pseudo-code:
"Given an array of days and a starting date, get the first occurrence after that date".
This is not a complete answer but it will get you started:
;With DatesCte AS
(
SELECT TOP(#occurrences) DATEADD(DAY, number-1, #Start) As TheDate
FROM Numbers
), DatesWithWeekDay As
(
SELECT TheDate, DATENAME(WEEKDAY, TheDate) As WeekDayName
FROM DatesCTE
)
SELECT *
FROM DatesWithWeekDay

Weekly Average Reports: Redshift

My Sales data for first two weeks of june, Monday Date i.e 1st Jun , 8th Jun are below
date | count
2015-06-01 03:25:53 | 1
2015-06-01 03:28:51 | 1
2015-06-01 03:49:16 | 1
2015-06-01 04:54:14 | 1
2015-06-01 08:46:15 | 1
2015-06-01 13:14:09 | 1
2015-06-01 16:20:13 | 5
2015-06-01 16:22:13 | 1
2015-06-01 16:27:07 | 1
2015-06-01 16:29:57 | 1
2015-06-01 19:16:45 | 1
2015-06-08 10:54:46 | 1
2015-06-08 15:12:10 | 1
2015-06-08 20:35:40 | 1
I need a find weekly avg of sales happened in a given range .
Complex Query:
(some_manipulation_part), ifact as
( select date, sales_count from final_result_set
) select date_part('h',date )) as h ,
date_part('dow',date )) as day_of_week ,
count(sales_count)
from final_result_set
group by h, dow.
Output :
h | day_of_week | count
3 | 1 | 3
4 | 1 | 1
8 | 1 | 1
10 | 1 | 1
13 | 1 | 1
15 | 1 | 1
16 | 1 | 8
19 | 1 | 1
20 | 1 | 1
If I try to apply avg on the above final result, It is not actually fetching correct answer!
(some_manipulation_part), ifact as
( select date, sales_count from final_result_set
) select date_part('h',date )) as h ,
date_part('dow',date )) as day_of_week ,
avg(sales_count)
from final_result_set
group by h, dow.
h | day_of_week | count
3 | 1 | 1
4 | 1 | 1
8 | 1 | 1
10 | 1 | 1
13 | 1 | 1
15 | 1 | 1
16 | 1 | 1
19 | 1 | 1
20 | 1 | 1
So I 've two mondays in the given range, it is not actually dividing by it. I am not even sure what is happening inside redshift.
To get "weekly averages" use date_trunc():
SELECT date_trunc('week', my_date_column) as week
, avg(sales_count) AS avg_sales
FROM final_result_set
GROUP BY 1;
I hope you are not actually using date as name for your date column. It's a reserved word in SQL and a basic type name, don't use it as identifier.
If you group by the day of week (DOW) you get averages per weekday. and sunday is 0. (Use ISODOW to get 7 for Sunday.)

Group by date and count of entries

I'll make it short, the table looks like this:
| id (int) | registerDate (DATETIME)
|----------|-----------------
| 1 | 2014-07-29 12:00:00
| 2 | 2014-08-01 12:00:00
| 3 | 2014-08-01 12:00:00
| 4 | 2014-08-01 12:00:00
| 5 | 2014-08-02 12:00:00
| 6 | 2014-08-02 12:00:00
| 7 | 2014-08-04 12:00:00
If today is 2014-08-05, I want results like this:
| registerDate (DATETIME) | count (int)
| 2014-08-04 | 1
| 2014-08-03 | 0
| 2014-08-02 | 2
| 2014-08-01 | 1
| 2014-07-31 | 0
| 2014-07-30 | 0
| 2014-07-29 | 1
So I want the count of registered users in the past week (daily).
I tried to find it out on google (unsuccessfully) - however, I hope you can help.
SELECT registerDate, count(registerDate) FROM [TABLE] WHERE
registerDate between (GETDATE()-7) and GETDATE()
group by registerDate
order by registerDate desc
This will take a table like:
2 |1905-06-26 00:00:00.000
4 |2014-08-03 00:00:00.000
5 |2014-08-02 00:00:00.000
1 |2014-08-01 00:00:00.000
3 |2014-07-01 00:00:00.000
6 |2010-07-01 00:00:00.000
7 |2015-07-01 00:00:00.000
8 |2014-08-28 00:00:00.000
9 |2014-08-26 00:00:00.000
10 |2014-08-26 00:00:00.000
And create:
2014-08-28 00:00:00.000 | 1
2014-08-26 00:00:00.000 | 2
The problem with this is it doesn't show the days that are not in the table.
Give me a little more time, I'll have an updated version.
EDIT:
Now the more complex one...
-- Declare how far back you want to go
DECLARE #DAYSBACK int = 6
-- Select into a temptable
select CONVERT(date, registerDate) as RegDate, count(registerDate) as DateCount
INTO #temptable
from Temp where registerDate between (GETDATE()-6) and GETDATE()
group by registerDate order by registerDate desc
-- Check to see if exists if not, insert row
WHILE #DAYSBACK >= 0 BEGIN
IF NOT EXISTS (select top 1 1 from #temptable
where RegDate= CONVERT(date, (GETDATE()-#DAYSBACK))
group by RegDate)
INSERT INTO #temptable values ((GETDATE()-#DAYSBACK), 0)
SET #DAYSBACK = #DAYSBACK -1
END
-- Select what you want
select * from #temptable order by RegDate desc
-- Drop the table you created.
DROP TABLE #temptable
Using the same table as above, it will output:
Register Date | Date Count
--------------------------
2014-08-28 | 1
2014-08-27 | 0
2014-08-26 | 2
2014-08-25 | 0
2014-08-24 | 0
2014-08-23 | 0
2014-08-22 | 0
Try something like this:
select registerDate = convert(date,t.registerDate) ,
registrations = count(*)
from dbo.my_special_registration_table t
where t.registrationDate >= dateadd(day,-7,convert(date,getdate()))
group by convert(date,t.registerDate)
order by 1
If you try to filter out registrations older than 7 days using something like datediff():
where datediff(day,t.registrationDate,getdate()) <= 7
you turned the column registrationDate into an expression. As a result the query optimizer can't make use of any indices that might apply, thus forcing a table scan. If you table is large, performance is likely to be ... suboptimal.

SQLite: Multiple aggregate columns

I'm a little new to SQL world and still learning the ins and outs of the language.
I have a table with an id, dayOfWeek, and a count for each day. So any given id might appear in the table up to seven times, with a count of events for each day for each id. I'd like to restructure the table to have a single row for each id with a column for each day of the week, something like the following obviously incorrect query:
SELECT id, sum(numEvents where dayOfWeek = 0), sum(numEvents where dayOfWeek = 1) ... from t;
Is there a solid way to approach this?
EDIT:
I'm worried I may not have been very clear. The table would ideally be structured something like this:
id | Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday
0 | 13 | 45 | 142 | 3 | 36 | 63 | 15336
1 | 17 | 25 | 45 | 364 | 37 | 540 | 0
So event 0 occurred 13 times on Sunday, 45 on Monday, etc... My current table looks like this:
id | dayOfWeek | count
0 | 0 | 13
0 | 1 | 45
0 | 2 | 142
0 | 3 | 3
0 | 4 | 36
0 | 5 | 63
0 | 6 | 15336
1 | 0 | 17
1 | 1 | 25
...
Hope that helps clear up what I'm after.
The following is verbose, but should work (generic ideone sql demo unfortunately SqlLite on SqlFiddle is down at the moment):
SELECT id,
SUM(case when dayofweek = 1 then numevents else 0 end) as Day1Events,
SUM(case when dayofweek = 2 then numevents else 0 end) as Day2Events,
SUM(case when dayofweek = 3 then numevents else 0 end) as Day3Events
--, etc...
FROM EventTable
GROUP BY ID;
SELECT dayOfWeek, sum(numEvents) as numberOfEvents
FROM t
GROUP BY dayOfWeek;