Group data from 3 tables and sort by week ending date - sql

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

Related

SQL - Get data between two dates grouped by week

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

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.

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.

To display only previous three months even the months before is not exist in database

Below is my new sql so far as i do not manage to use Dale M advice,
SELECT
all_months.a_month_id AS month,
year($P{date}) as year,
count(case when clixsteraccount.rem_joindate between DATE_FORMAT($P{date}-INTERVAL 2 MONTH, '%Y-%m-01') AND $P{date} THEN clixsteraccount.rem_registerbycn end) AS
total_activation,
'ACTIVATION(No)' AS fake_column
FROM clixsteraccount right join all_months on all_months.a_month_id = date_format(clixsteraccount.rem_joindate,'%m') and
(clixsteraccount.rem_registrationtype = 'Normal')and(clixsteraccount.rem_kapowstatus='pending' or clixsteraccount.rem_kapowstatus='success')
GROUP BY year,month
HAVING month BETWEEN month(date_sub($P{date},interval 2 month)) and month($P{date})
So, what i do is create a table with two fields, a_month_id(1,2,3...,12) and a_month(name of months). Sql above does give me what i want which is to display previous 3 months even the months before is not exist.
exp: data start on July. So, i want to display May,June and July data like 0,0,100.
The problem occur when it comes to next months or next year. When i try to generate sql based on parameter on Jan, it doesn't work like i thought. I do realize the problem are with 'Having' condition. Do anyone have idea how to improvised this sql to make it continue generate in the next,next year.
Thank you in advanced.
OK, I will make a few suggestions and give you an answer that will work on SQL Server - you will need to make any translations yourself.
I note that your query will aggregate all years together, i.e. Dec 2012 + Dec 2013 + Dec 2014 etc. Based on your question I don't think that is your intention so I will keep each distinct. You can change the query if that was your intention. I have also not included your selection criteria (other than by the month).
I suggest that you utilize an index table. This is a table stored in your database (or the master database if possible) with an clustered indexed integer column running from 0 to n where n is a sufficiently large number - 10,000 will be more than enough for this application (there are 12 months in a year so 10,000 represents 833 years). This table is so useful everyone should have one.
SELECT DATEADD(month, it.id, 0 ) AS month
,ISNULL(COUNT(clixsteraccount.rem_registerbycn), 0) AS registration
,'REGISTRATION(No)' AS fake_column
FROM cn
INNER JOIN ON ca.rem_registerbycn = cn.cn_id
clixsteraccount ca
RIGHT JOIN
IndexTable it ON it.id = DATEDIFF(month, 0, clixsteraccount.rem_joindate)
WHERE it.id BETWEEN DATEDIFF(month, 0, #StartDate) - 3 AND DATEDIFF(month, 0, GETDATE())
GROUP BY it.id
The way it works is by converting the clixsteraccount.rem_joindate to an integer that represents the number of months since date 0 (01-01-1900 in SQL Server). This is then matched to the id column of the IndexTable and limited by the dates you select. Because every number exists in the index table and we are using an outer join it doesn't matter if there are months missing from your data.

SQL query with week days

I would like to know what is the best way of creating a report that will be grouped by the last 7 days - but not every day i have data. for example:
08/01/10 | 0
08/02/10 | 5
08/03/10 | 6
08/04/10 | 10
08/05/10 | 0
08/06/10 | 11
08/07/10 | 1
is the only option is to create a dummy table with those days and join them altogether?
thank you
Try something like this
WITH LastDays (calc_date)
AS
(SELECT DATEADD(DAY, DATEDIFF(DAY, 0, CURRENT_TIMESTAMP) - 6, 0)
UNION ALL
SELECT DATEADD(DAY, 1, calc_date)
FROM LastDays
WHERE DATEADD(DAY, 1, calc_date) < CURRENT_TIMESTAMP)
SELECT ...
FROM LastDays l LEFT JOIN (YourQuery) t ON (l.cal_date = t.YourDateColumn);
Many people will suggest methods for dynamically creating a range of dates that you can then join against. This will certainly work but in my experience a calendar table is the way to go. This will make the SQL trivial and generic at the cost of maintaining the calendar table.
At some point in the future someone will come along and ask for another report that excludes weekends. You then have to make your dynamic days generation account for weekends. Then someone will ask for working-days excluding public-holidays at which point you have no choice but to create a calendar table.
I would suggest you bite the bullet and create a calendar table to join against. Pre-populate it with every date and if you want to think ahead then add columns for "Working Day" and maybe even week number if your company uses a non-standard week-number for reporting
You don't mention the specific language (please do for a more detailed answer), but most versions of sql have a function for the current date (GetDate(), for instance). You could take that date, subtract x (7) days and build your WHERE statement like that.
Then you could GROUP BY the day-part of that date.
select the last 7 transactions and left join it with your query and then group by the date column. hope this helps.