SQL - Get data between two dates grouped by week - sql

This question might have been solved many times or even asked many times. But as I am not a savvy in SQL, I am not able to figure out things found on the internet. Like I am not able to tweak Queries from the Internet to my needs.
And here comes my need
I have a Table named Orders Containing Fields like OrderId, OrderDate etc.
I need to generate an Excel Sheet. The sheet will have the count of orders grouped by week.
(Like how many orders placed within that week)
The user can choose the year in which he/she needs the report for.
So if the user chooses the current year then I need to generate an excel report containing data from Jan 1 to today grouped by week.
If the user chooses any other year(maybe previous years) then I need to generate a report containing all the data for that year grouped by week.
Currently, I am looking for an SQL query that returns data like this(expected output)
Week Date Range Total No of Orders
-----+--------------------------+-------------------
week#1 2018-01-01 - 2018-01-07 10
week#2 2018-01-08 - 2018-01-14 0
week#3 2018-01-15 - 2018-01-21 1
How can I write a query to achieve the same?
Looking for expert advice...

You need to use CTE recursive write calendar by week number,then Orders LEFT JOIN on CTE calendar table get COUNT.
Note:
variable #Dt mock which year you want to start.
Query look like this.
DECLARE #Dt date = '2018-01-01'
;WITH CTE(Dt,maxD) AS (
SELECT DATEPART(ww,#Dt) Dt, DATEPART(ww,MAX(OrderDate)) maxD
FROM Orders
UNION ALL
SELECT (Dt +1) Dt,maxD
FROM CTE
WHERE (Dt +1) <= maxD
)
SELECT CONCAT('week#',c.Dt) 'week',
CONCAT(
CONVERT(char(10),dateadd(week,c.Dt-1, DATEADD(wk, DATEDIFF(wk,-1,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)), 0)),126)
,'-'
, CONVERT(char(10),dateadd(week,c.Dt, DATEADD(wk, DATEDIFF(wk,-1,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)), 0)),126)) 'Date Range',
COUNT(o.OrderDate) 'Total No of Orders'
FROM CTE c
LEFT JOIN Orders o on c.Dt = DATEPART(ww,o.OrderDate)
GROUP BY c.Dt
sqlfiddle:http://sqlfiddle.com/#!18/8f089/40

Related

Joining date with datetime and values per day

I am writing a query so that I can fill my facttable. I have a table which registers the weather average per day, registered at 23:59:00 of each day (date).
I have another table in which climate control data of different rooms are registered, per minute (datetime).
And I also have a time dimension available in another database.
I want to fill my facttable with all the available timeKeys combined with all the data from my climate control table and my weather table.
I'm sorry for my English, it isn't my mother tongue.
So, to find the matching timeKey for the date values I wrote this query:
SELECT t.timeKey AS WeathertimeKey,
weather.date AS date,
weather.temperature,
weather.rainAmountMM,
weather.windDirection,
weather.windSpeed
FROM StarSchema.dbo.timeDim t, weather
WHERE DATEPART(mm, t.DATE) = DATEPART(mm, weather.date)
AND DATEPART(dd, t.DATE) = DATEPART(dd, weather.date)
AND DATEPART(Hour, t.DATE) = '23'
AND DATEPART(MINUTE, t.DATE) = '59'
RESULT: Result
My time dimension has a timeKey for every minute in 2015: timeDimension
The facttable I am trying to fill: facttable
My solution for filling the facttable was creating a view with the corresponding timeKey per day and then joining that view in my main query.
SELECT
t.timeKey as timeKey,
rt1.roomId AS roomKey,
1 AS roomDataKey,
1 AS usageKey,
1 AS knmiKey,
rt1.temperature AS temperature,
rt1.locWindow AS locWindow,
rt1.locDoor AS locDoor,
rh1.turnedOn AS turnedOn,
rh1.temperature AS temperatureHeater,
s.storyTemp AS storyTemp,
s.storyHumidity AS storyHumidity,
vw.temperature AS temperatureOutside,
vw.rainAmountMM AS rainAmountMM,
vw.windSpeed AS windSpeed,
vw.windDirection AS windDirection,
vu.gasM3 AS gasM3,
vu.electricityKWH AS electricityKWH
FROM StarSchema.dbo.timeDim t
INNER JOIN roomTemperature1 rt1 ON rt1.date = t.DATE
INNER JOIN roomHeating1 rh1 ON rt1.date = rh1.date
INNER JOIN story s ON s.date = rt1.date
INNER JOIN vw_timeKeyWeatherDay vw ON t.timeKey = vw.WeathertimeKey
INNER JOIN vw_timeKeyUsageDay vu ON t.timeKey = vu.UsagetimeKey
The result is as follows: result2
So now it only uses the timeKey of 23:59 of everyday.
I want the complete days in there, but how do I do this?
Can someone help me out?
And my apologies for my use of the English language, again.
I did my best :-)
If I understand your question property, you want to match two date columns which have a different level of precision: one is per minute the other one is per day. What I suggest is a query which stores the yyyy.mm.dd only for both. Then when you join you get a matching record every time.
You can do that by adding the number of days that distance each of your dates from the date 0 in SQL server
DECLARE #DVal DATE
DECLARE #DTVal DATETIME
SET #DVal = '2018-01-18'
SET #DTVal = '2018-01-18 12:02:01.003'
SELECT #DVal
SELECT #DTVal
SELECT DATEDIFF(D,0,#DTVal)
SELECT DATEDIFF(D,0,#DVal)
SELECT DATEADD(D,(DATEDIFF(D,0,#DTVal) ),0)
SELECT DATEADD(D,(DATEDIFF(D,0,#DVal) ),0)
Comments about the code above:
First I declare the variables, one DATE and one DATETIME and I give them slightly different values which are less than a day.
Then I select them so that we can see they are different
2018-01-18
2018-01-18 12:02:01.003
Then I select the difference in days between each date and 0, and we have the same number of days
43116
43116
Then I add this difference to the date 0, and we end up with two datetime values which are identical
2018-01-18 00:00:00.000
2018-01-18 00:00:00.000
I hope I have answered your question. Please comment on my answer if I have not. At least this is a starting point. If your goal is to get the complete range of minutes per day you can create two calculated columns, one based on the current date and the other one based on the current date +1 day, and join against the time table with a BETWEEEN ON clause, etc.

Display a rolling 12 weeks chart in SSRS report

I am calling the data query in ssrs like this:
SELECT * FROM [DATABASE].[dbo].[mytable]
So, the current week is the last week from the query (e.g. 3/31 - 4/4) and each number represents the week before until we have reached the 12 weeks prior to this week and display in a point chart.
How can I accomplish grouping all the visits for all locations by weeks and adding it to the chart?
I suggest updating your SQL query to Group by a descending Dense_Rank of DatePart(Week,ARRIVED_DATE). In this example, I have one column for Visits because I couldn't tell which columns you were using to get your Visit count:
-- load some test data
if object_id('tempdb..#MyTable') is not null
drop table #MyTable
create table #MyTable(ARRIVED_DATE datetime,Visits int)
while (select count(*) from #MyTable) < 1000
begin
insert into #MyTable values
(dateadd(day,round(rand()*100,0),'2014-01-01'),round(rand()*1000,0))
end
-- Sum Visits by WeekNumber relative to today's WeekNumber
select
dense_rank() over(order by datepart(week,ARRIVED_DATE) desc) [Week],
sum(Visits) Visits
from #MyTable
where datepart(week,ARRIVED_DATE) >= datepart(week,getdate()) - 11
group by datepart(week,ARRIVED_DATE)
order by datepart(week,ARRIVED_DATE)
Let me know if I can provide any more detail to help you out.
You are going to want to do the grouping of the visits within SQL. You should be able to add a calculated column to your table which is something like WorkWeek and it should be calculated on the days difference from a certain day such as Sunday. This column will then by your X value rather than the date field you were using.
Here is a good article that goes into first day of week: First Day of Week

Group data from 3 tables and sort by week ending date

Here is the problem I am stuck on.
I have three tables which log the dates and locations customers visit my sales centers. One table is the initial visit, the second is a able for repeat visits, of which there could be many and the third is a deposit table which stores the data related to when people leave a deposit and on which job they left the deposit.
So I need to sort all do this data by the week ending date, which I do have working on one table at a time. So for example, anyone who visited my sales center on March 3 would be counted in as traffic for the week ending march 9.
Now I would like to query the data so that I call pull the dates from the other two tables as well and have them sort by week ending along with the other data.
So my final output would look like this:
Week ending | initial visit | bback | deposit
3/9/2014 9 3 0
3/16/2104. 12. 0. 1
My tables structure looks like this:
Salescenter_clientinfo
Initial_visit (date)
Community
Customer_bback
Bback_date
Bback_community
Customer Deposit
Deposit_date
Doposit_community
All of these tables also have a field which is the customer_id which links them all.
As stated earlier, I do have the sql working which gets the job done using one table but how do I accomplish this on multiple tables?
I forgot to mention, I run this sql from an excel pivot table against an MSSql database. I am using excel to do the pivot and be the final report.
Any help in very much appreciated.
Thanks in advance.
Steve
If I'm understanding your original question correctly, you're trying to track everything that happened at your center, in each category, during a specific week. So if a customer's initial visit was during week 1, and he/she came back during week 2, those two items would fall under different weeks.
Based on that assumption, the linkage on Customer_ID is actually a bit of a red herring: you want data aggregated by week, and don't care if it all reflects the same customer's initial visit.
If so, then the following SQL might do what you are looking for:
Set Datefirst 1;
SELECT
ISNULL(vc.WeekEnding, ISNULL(b.WeekEnding, d.WeekEnding)) WeekEnding,
vc.InitialVisit,
b.BBack,
d.Deposit
FROM
(
SELECT
DATEADD(dd, 7 - DATEPART(DW, initial_visit), initial_visit) AS WeekEnding,
COUNT(initial_visit) AS InitialVisit
FROM dbo.SalesCenter_clientinfo
GROUP BY DATEADD(dd, 7 - DATEPART(DW, initial_visit), initial_visit)
) vc
FULL OUTER JOIN
(
SELECT
DATEADD(dd, 7 - DATEPART(DW, Bback_date), Bback_date) AS WeekEnding,
COUNT(Bback_date) AS BBack
FROM dbo.Customer_bback
GROUP BY DATEADD(dd, 7 - DATEPART(DW, Bback_date), Bback_date)
) b ON
vc.WeekEnding = b.WeekEnding
FULL OUTER JOIN
(
SELECT
DATEADD(dd, 7 - DATEPART(DW, Deposit_date), Deposit_date) AS WeekEnding,
COUNT(Deposit_date) AS Deposit
FROM dbo.Customer_deposit
GROUP BY DATEADD(dd, 7 - DATEPART(DW, Deposit_date), Deposit_date)
) d ON
vc.WeekEnding = d.WeekEnding
Fiddle

SQL - Finding largest period of activity by customer

I had a difficult time w/ the title, hope this is a little clearer...
I have a table of data (simplified) like so;
Date
Customer
Amount
1/1/2014
1
100.5
4/4/2014
1
122.5
2/1/2014
3
3.25
...but just short of a million records.
I would like to find the x day (let's say 90) period for each customer that has the largest total amount.
To phase the question a little differently, given all the transactions for all customers, for each customer I would like to find the 90 day period that has the largest total amount and what that total amount is in the period.
Trying to advise a brute force approach where I define all the possible ranges (or iterate over all possible ranges on the fly).
Any thoughts on a more elegant solution?
You can use a self-join for this, but the performance may not be so great:
select t.*,
(select sum(t2.amount)
from table t2
where t2.customer = t.customer and
t2.date >= dateadd(day, -90, t.date) and t2.date <= t.date
) as amount90
from table t;
There is a more efficient method in SQL Server 2012.
Here's a psuedo-code ish kind of answer that I think would work. It'd probably be really slow though.
You could have a function that calculates the number of activities provided a start date and number of days,
--function F
#userid, #startdate, #dayCount
SELECT COUNT(*)
FROM TABLE
WHERE
UserID = #userid
and date > #startDate
and date < Dateadd(#startdate, #dayCount)
and then do a max on that function?
select max(f(user, date))
from TableContainingDateRanges

select all records from one table and return null values where they do not have another record in second table

I have looked high and low for this particular query and have not seen it.
We have two tables; Accounts table and then Visit table. I want to return the complete list of account names and fill in the corresponding fields with either null or the correct year etc. this data is used in a matrix report in SSRS.
sample:
Acounts:
AccountName AccountGroup Location
Brown Jug Brown Group Auckland
Top Shop Top Group Wellington
Super Shop Super Group Christchurch
Visit:
AcccountName VisitDate VisitAction
Brown Jug 12/12/2012 complete
Super Shop 1/10/2012 complete
I need to select weekly visits and show those that have had a complete visit and then the accounts that did not have a visit.
e.g.
Year Week AccountName VisitStatus for week 10/12/2012 should show
2012 50 Brown Jug complete
2012 50 Top Group not complete
2012 50 Super Shop not complete
e.g.
Year Week AccountName VisitStatus for week 1/10/2012 should show
2012 2 Brown Jug not complete
2012 2 Top Group not complete
2012 2 Super Shop complete
please correct me if am worng
select to_char(v.visitdate,'YYYY') year,
to_char(v.visitdate,'WW') WEAK,a.accountname,v.visitaction
from accounts a,visit v
where a.accountname=v.ACCCOUNTNAME
and to_char(v.visitdate,'WW')=to_char(sysdate,'WW')
union all
select to_char(sysdate,'YYYY') year,
to_char(sysdate,'WW') WEAK,a.accountname,'In Complete'
from accounts a
where a.accountname not in ( select v.ACCCOUNTNAME
from visit v where to_char(v.visitdate,'WW')=to_char(sysdate,'WW'));
The following answer assumes that
A) You want to see every week within a given range, whether any accounts were visited in that week or not.
B) You want to see all accounts for each week
C) For accounts that were visited in a given week, show their actual VisitAction.
D) For accounts that were NOT visited in a given week, show "not completed" as the VisitAction.
If all those are the case then the following query may do what you need. There is a functioning sqlfiddle example that you can play with here: http://sqlfiddle.com/#!3/4aac0/7
--First, get all the dates in the current year.
--This uses a Recursive CTE to generate a date
--for each week between a start date and an end date
--In SSRS you could create report parameters to replace
--these values.
WITH WeekDates AS
(
SELECT CAST('1/1/2012' AS DateTime) AS WeekDate
UNION ALL
SELECT DATEADD(WEEK,1,WeekDate) AS WeekDate
FROM WeekDates
WHERE DATEADD(WEEK,1,WeekDate) <= CAST('12/31/2012' AS DateTime)
),
--Next, add meta data to the weeks from above.
--Get the WeekYear and WeekNumber for each week.
--Note, you could skip this as a separate query
--and just included these in the next query,
--I've included it this way for clarity
Weeks AS
(
SELECT
WeekDate,
DATEPART(Year,WeekDate) AS WeekYear,
DATEPART(WEEK,WeekDate) AS WeekNumber
FROM WeekDates
),
--Cross join the weeks data from above with the
--Accounts table. This will make sure that we
--get a row for each account for each week.
--Be aware, this will be a large result set
--if there are a lot of weeks & accounts (weeks * account)
AccountWeeks AS
(
SELECT
*
FROM Weeks AS W
CROSS JOIN Accounts AS A
)
--Finally LEFT JOIN the AccountWeek data from above
--to the Visits table. This will ensure that we
--see each account/week, and we'll get nulls for
--the visit data for any accounts that were not visited
--in a given week.
SELECT
A.WeekYear,
A.WeekNumber,
A.AccountName,
A.AccountGroup,
IsNull(V.VisitAction,'not complete') AS VisitAction
FROM AccountWeeks AS A
LEFT JOIN Visits AS V
ON A.AccountName = V.AccountName
AND A.WeekNumber = DATEPART(WEEK,V.VisitDate)
--Set the maxrecursion number to a number
--larger than the number of weeks you will return
OPTION (MAXRECURSION 200);
I hope that helps.