Suppose I have a SQL table as shown below where Min Spend is the minimum spend for each year and is a calculated column created using SQL-Window Function
|------------|-------|--------|----------|
| Year |Month | Spend |Min Spend |
|------------|-------|--------|----------|
| 2018 | Jan | 10 | 10 |
| 2018 | Feb | 20 | 10 |
| 2018 | Oct | 25 | 10 |
| 2019 | Jan | 90 | 45 |
| 2019 | Aug | 60 | 45 |
| 2019 | Nov | 45 | 45 |
|------------|-------|--------|----------|
I would like to create a new column as a calculated field in the table that gives me the month corresponding the the 'Min Spend' for that year as shown below
|------------|-------|--------|----------|---------------|
| Year |Month | Spend |Min Spend |Min Spend Month|
|------------|-------|--------|----------|---------------|
| 2018 | Jan | 10 | 10 | Jan |
| 2018 | Feb | 20 | 10 | Jan |
| 2018 | Oct | 25 | 10 | Jan |
| 2019 | Jan | 90 | 45 | Nov |
| 2019 | Aug | 60 | 45 | Nov |
| 2019 | Nov | 45 | 45 | Nov |
|------------|-------|--------|----------|---------------|
Can anybody suggest how to approach this?
You can use window functions like this:
select t.*,
min(spend) over (partition by year) as min_spend,
first_value(month) over (partition by year order by spend) as min_spend_month
from t;
tbl_vacations
vac_id | vac_name
1 | American vacation
2 | European vacation
tbl_vacation_stops
stop_id | vac_id | stop_sequence | stop_name | stop_strt_day | stop_end_day
1 | 1 | 1 | New York | may 1 2018 | may 3 2018
2 | 1 | 2 | Boston | may 4 2018 | may 6 2018
3 | 1 | 3 | Chicago | may 7 2018 | may 9 2018
4 | 2 | 1 | Paris | jun 10 2018 | jun 15 2018
5 | 2 | 2 | Berlin | jun 16 2018 | jun 19 2018
select
v.vac_id as vac_id,
v.vac_name as vac_name,
vs.stop_strt_day as vac_strt_day
from tbl_vacations v
join tbl_vacation_stops vs
where v.vac_id=vs.vac_id and vs.stop_sequence='1'
vac_id | vac_name | vac_strt_day | vac_end_day
1 | American vacation | may 1 2018 | may 9 2018
2 | European vacation | jun 10 2018 | jun 19 2018
If there are a different number of stops in each vacation, how do I figure out the vac_end_day based on max stop sequence?
this would do the trick:
select
v.vac_id as vac_id,
v.vac_name as vac_name,
(select stop_strt_day from tbl_vacation_stops where vac_id = v.vac_id
and stop_sequence = (select min(stop_sequence) from tbl_vacation_stops where vac_id =
v.vac_id)
) as vac_strt_day,
(select stop_end_day from tbl_vacation_stops where vac_id = v.vac_id
and stop_sequence = (select max(stop_sequence) from tbl_vacation_stops where vac_id =
v.vac_id)
) as vac_end_day
from tbl_vacations v
My query seems simple and maybe im over thinking it. Consider 2 tables: Client A and Service B. The clients table has 2 dates, Service Start Date & Service End Date. Now, our KPI is we should have provided atleast 1 service to each client that has been active with us for more than 15 days in that month. This is a monthly KPI. My interface lets you pick a 3 month or 6 month period for reporting and the report needed is Count of Client serviced by Month. This will be grouped by month and the Region they are in.
Client Table A
C_ID | Name | SrvStDt | SrvEndDt | ServManager | Region
1 | ABCD | 1 Jan 16 | 10 Mar 16 | AAAAAA | North
2 | EDFG | 1 Feb 16 | 15 Aug 16 | BBBBBB | South
3 | YTHG | 1 Mar 16 | 1 Jul 16 | CCCCCC | East
4 | WEFG | 21 Apr 16| 15 May 16 | DDDDDD | West
5 | POIU | 18 May 16| 15 Jul 16 | AAAAAA | North
6 | QWER | 30 Jun 16| -- | BBBBBB | South
7 | CVBH | 21 Jul 16| -- | CCCCCC | East
8 | ASDR | 1 Aug 16 | -- | DDDDDD | West
9 | LKJU | 15 Jan 16| 15 Jul 16 | AAAAAA | North
10 | MNBU | 15 Mar 16| 15 Jul 16 | BBBBBB | South
Notes : No end date signifies the client is still active
Service Table B
ID | SrvDAte
1 | 15 Jan 16
1 | 12 Feb 16
1 | 01 Mar 16
1 | 10 Mar 16
2 | 15 Feb 16
2 | 15 Mar 16
2 | 31 Mar 16
2 | 15 Apr 16
2 | 15 May 16
2 | 15 Jul 16
2 | 15 Aug 16
3 | 21 Mar 16
3 | 23 Mar 16
3 | 23 Apr 16
3 | 23 May 16
4 | 29 Apr 16
5 | 23 May 16
6 | 12 Jul 16
7 | 23 Jul 16
9 | 23 Mar 16
9 | 23 Apr 16
9 | 23 May 16
10 | 19 Mar 16
10 | 19 Apr 16
10 | 19 May 16
10 | 19 Jun 16
For a report from 1 April - 31 Jul the Result im after will look something like this:
Region | Month | Total Active | Total Serviced
North | Apr-16 | XXXX | XXXX
North | May-16 | XXXX | XXXX
North | Jun-16 | XXXX | XXXX
North | Jul-16 | XXXX | XXXX
South | Apr-16 | XXXX | XXXX
South | May-16 | XXXX | XXXX
South | Jun-16 | XXXX | XXXX
South | Jul-16 | XXXX | XXXX
East | Apr-16 | XXXX | XXXX
East | May-16 | XXXX | XXXX
East | Jun-16 | XXXX | XXXX
East | Jul-16 | XXXX | XXXX
West | Apr-16 | XXXX | XXXX
West | May-16 | XXXX | XXXX
West | Jun-16 | XXXX | XXXX
West | Jul-16 | XXXX | XXXX
I'm struggling with how to identify clients that are active for at least 15 days in a month for all of the reporting period. I can put a between clause for the whole period, e.g., 1 Apr - 1 Jul, but how do I break it into months and evaluate for each month?
I'm using MS Access VBA & SQL queries. I would like an SQL query that gives me this result set.
I'm struggling with how to identify clients that are active for at least 15 days in a month for all of the reporting period. I can put a between clause for the whole period, e.g., 1 Apr - 1 Jul, but how do I break it into months and evaluate for each month?
One way to derive those counts would be to use a "numbers table" that starts at zero and covers the largest possible number of days between the start date and the end date, e.g.,
[Numbers]
n
---
0
1
2
3
...
998
999
Then, for sample data
[TableA]
C_ID SrvStDt SrvEndDt
---- ---------- ----------
1 2016-01-01 2016-01-04
2 2016-02-28 2016-03-02
3 2016-08-10
the query
SELECT
C_ID,
DateAdd("d", n, SrvStDt) AS SrvDate
FROM TableA, Numbers
WHERE
DateAdd("d", n, SrvStDt) <= Nz(SrvEndDt, Date())
returns
C_ID SrvDate
---- ----------
1 2016-01-01
1 2016-01-02
1 2016-01-03
1 2016-01-04
2 2016-02-28
2 2016-02-29
2 2016-03-01
2 2016-03-02
3 2016-08-10
3 2016-08-11
3 2016-08-12
3 2016-08-13
3 2016-08-14
3 2016-08-15
and if we wrap that in an aggregation query to COUNT the dates in each Year/Month (GROUP BY)
SELECT
C_ID,
Year(SrvDate) AS SrvYear,
Month(SrvDate) AS SrvMonth,
COUNT(*) AS SrvDays
FROM
(
SELECT
C_ID,
DateAdd("d", n, SrvStDt) AS SrvDate
FROM TableA, Numbers
WHERE
DateAdd("d", n, SrvStDt) <= Nz(SrvEndDt, Date())
)
GROUP BY
C_ID,
Year(SrvDate),
Month(SrvDate)
we get
C_ID SrvYear SrvMonth SrvDays
---- ------- -------- -------
1 2016 1 4
2 2016 2 2
2 2016 3 2
3 2016 8 6
To the MDX gurus,
I have been beating my head against this one for a week and I am nowhere close to solving it. Can it be solved?
Here's the challenge:
To create a Calculated Member Expression in SSAS BIDs to calculate the Weighted_Members which is described as the following:
"For any date period chosen, we need to calculate the sum of the Weights that is associated with the most recent visit of a unique member."
In pseudo-code: SUM(DISTINCT Member’s (MAX (Date’s Weight)))
NOTES:
* The WEIGHT is given to a member’s visit to a particular location and is applicable for 1 month.
Here is a sample of the fact table showing:
* Two members (membership id: 100 and 103)
* Visiting 3 different locations (location id: 200, 220 and 230)
* At different dates throughout 2014 and 2015.
Visits_F_ID | Visit_Date | Membership_ID | Location_ID | Weights |
1 | Jan 1, 2014 | 100 | 230 | 3.5 |
2 | Mar 1, 2014 | 100 | 220 | 2.0 |
3 | May 1, 2015 | 100 | 220 | 2.5 |
4 | Apr 1, 2014 | 103 | 200 | 1.0 |
5 | Jul 1, 2014 | 103 | 220 | 1.5 |
6 | Sep 1, 2014 | 103 | 230 | 0.5 |
7 | Nov 1, 2014 | 103 | 220 | 3.0 |
8 | Jan 1, 2015 | 103 | 220 | 1.0 |
9 | Aug 1, 2015 | 103 | 200 | 7.0 |
10 | Sep 1, 2015 | 103 | 230 | 4.5 |
11 | Dec 1, 2015 | 103 | 200 | 1.5 |
Dimensions:
The Visit Date Dimension has the following attributes:
* YEAR
* Quarter
* MONTH
* Date
* Calendar Year->Quarter->Month->Date (calendar_quarter_hierarchy)
* Calendar Year->Month->Date (calendar_month_hierarchy)
The Membership Dimension has the following attributes:
* membership_id (currently visibility set to false (or hidden) as there are >5M records)
* Gender
* Age Cohort
The Location Dimension has the following attributes:
* Location_ID
* Location_Name
* City
* Province
* Province->City->Location_Name (Geographical_hierarchy)
Examples:
Example #1.) The Weighted_Members for the YEAR 2014 would be calculated as follows:
STEP 1: filtering the fact data for activity in YEAR 2014.
Visits_F_ID | Visit_Date | Membership_ID | Location_ID | Weights |
=============================================================================
1 | Jan 1, 2014 | 100 | 230 | 2.5 |
2 | Mar 1, 2014 | 100 | 220 | 2.0 |
4 | Apr 1, 2014 | 103 | 200 | 1.0 |
5 | Jul 1, 2014 | 103 | 220 | 1.5 |
6 | Sep 1, 2014 | 103 | 230 | 0.5 |
7 | Nov 1, 2014 | 103 | 220 | 3.0 |
STEP 2: taking the data with the most recent date for each unique member from the above:
Visits_F_ID | Visit_Date | Membership_ID | Location_ID | Weights |
=============================================================================
2 | Mar 1, 2014 | 100 | 220 | 2.0 |
7 | Nov 1, 2014 | 103 | 220 | 3.0 |
STEP 3: sum the Weights to give the Weighted_Members = 2.0 + 3.0 is 5.0
======
Example #2.) If the cube user slices for the time period of 2015, following the same three steps in example #1 above, the Weighted_Members:
Visits_F_ID | Visit_Date | Membership_ID | Location_ID | Weights |
=============================================================================
3 | May 1, 2015 | 100 | 220 | 2.5 |
11 | Dec 1, 2015 | 103 | 200 | 1.5 |
Weighted_Members = 2.5 + 1.5 is 4.0
======
Example #3.) If the cube user slices for the time period of Mar 2014 to Oct 2014 and is interested in visits to location_id = 220, the Weighted_Members:
Visits_F_ID | Visit_Date | Membership_ID | Location_ID | Weights |
=============================================================================
2 | Mar 1, 2014 | 100 | 220 | 2.0 |
5 | Jul 1, 2014 | 103 | 220 | 1.5 |
Weighted_Members = 2.0 + 1.5 is 3.5
======
Example #4.) If the cube user slices for the time period of July 2015 to Aug 2015, the Weighted_Members:
Visits_F_ID | Visit_Date | Membership_ID | Location_ID | Weights |
=============================================================================
9 | Aug 1, 2015 | 103 | 200 | 7.0 |
Weighted_Members = 7.0
Based on my understanding - You can give this a try:
WITH MEMBER Measures.YourCalcMember AS
SUM
(
generate
(
Customer.CustomerID.MEMBERS AS S,
s.CURRENT *
TAIL(
NonEmpty
(
[Date].[Date].[Date].MEMBERS, --The last date for the "current" customer
(s.CURRENT, [Measures].[Weight])
)
)
)
,
Measures.[Weight]
)
SELECT Measures.YourCalcMember ON 0,
Location.LocationID.MEMBERS ON 1
FROM
(
SELECT [Date].[Year].&[2014] ON 0 FROM [Your Cube] --The year filter
)
Using the "generate" function, loop thru the customers and obtain a cross-set of customerId and the 'last' date for that customer. Over this set then, obtain the sum of weights.
All said, further details are needed before this question can be attempted correctly.
I have the following table.
Data_table
R_id I_id Metric CType Timespan Quantity Date
1 1 S C Week 100 4/5/2015
1 1 Q C Week 200 4/5/2015
1 1 I D Week 80 4/5/2015
1 2 S C Week 150 4/5/2015
1 2 Q C Week 100 4/5/2015
1 2 I D Week 50 4/5/2015
Metric can have a limited set of values (S, Q, I..)
CType will be C, D or nil.
Timespan can be Weekly/Daily.
Date will be a Sunday (start of week) for Weekly and that day's date for Daily.
My goal is to convert this to a daily view which would involve
If Timespan is Daily, copy the Quantity for the above metrics as it is.
Converting a Weekly quantity to 7 Daily quantities.
If the CType is D copy the quantity as it is.
If the CType is C use a constant percentage breakdown logic to distribute the weekly over 7 days.eg [30%, 10%, 10%, 5%, 10%, 15% 20%] = 100%
Creating the following VIEW.
R_id I_id Date S Q I ... (other metrics whose CType is not nil)
1 1 4/5/2015 30 60 80 ... (the quantity of the other metrics)
1 1 4/6/2015 10 20 80
1 1 4/7/2015 10 20 80
1 1 4/8/2015 5 10 80
1 1 4/9/2015 10 20 80
1 1 4/10/2015 15 30 80
1 1 4/11/2015 20 40 80
1 2 4/5/2015 45 30 50
1 2 4/6/2015 15 10 50
1 2 4/7/2015 15 10 50
1 2 4/8/2015 7.5 5 50
1 2 4/9/2015 15 10 50
1 2 4/10/2015 22.5 15 50
1 2 4/11/2015 30 20 50
I can write a bunch of java methods which will pull out the data from the above table and get the values for metrics as needed. But for a large dataset, the performance will not be very good. Databases are meant for this type of data computation. Once this view is created, I can quickly (and simply) query it to get what I want. I can write simple sql queries. But I have no clue how to even begin approaching this problem! I can see a PIVOT here (logically, I don't know how a query would or even can achieve it). But how to compute the 7 daily quantities from a weekly quantity and put it in the VIEW?
Suggestions and guidance will be much appreciated.
You can use hierarchical queries to generate daily data.
SQL Fiddle
Query:
select
r_id,
i_id,
metric,
ctype,
timespan,
quantity,
tdate + level - 1 as m_tdate,
level as m_level,
(case ctype
when 'C' then
(case level
when 1 then 0.3
when 2 then 0.1
when 3 then 0.1
when 4 then 0.05
when 5 then 0.1
when 6 then 0.15
when 7 then 0.2
end)
else 1
end) * quantity as m_quantity
from myt
where timespan = 'Week'
connect by level <= 7
and r_id = prior r_id
and i_id = prior i_id
and metric = prior metric
and ctype = prior ctype
and timespan = prior timespan
and prior sys_guid() is not null
This will generate seven day data for each record
Results:
| R_ID | I_ID | METRIC | CTYPE | TIMESPAN | QUANTITY | M_TDATE | M_LEVEL | M_QUANTITY |
|------|------|--------|-------|----------|----------|-----------------------|---------|------------|
| 1 | 1 | I | D | Week | 80 | May, 04 2015 00:00:00 | 1 | 80 |
| 1 | 1 | I | D | Week | 80 | May, 05 2015 00:00:00 | 2 | 80 |
| 1 | 1 | I | D | Week | 80 | May, 06 2015 00:00:00 | 3 | 80 |
| 1 | 1 | I | D | Week | 80 | May, 07 2015 00:00:00 | 4 | 80 |
| 1 | 1 | I | D | Week | 80 | May, 08 2015 00:00:00 | 5 | 80 |
| 1 | 1 | I | D | Week | 80 | May, 09 2015 00:00:00 | 6 | 80 |
| 1 | 1 | I | D | Week | 80 | May, 10 2015 00:00:00 | 7 | 80 |
| 1 | 1 | Q | C | Week | 200 | May, 04 2015 00:00:00 | 1 | 60 |
| 1 | 1 | Q | C | Week | 200 | May, 05 2015 00:00:00 | 2 | 20 |
| 1 | 1 | Q | C | Week | 200 | May, 06 2015 00:00:00 | 3 | 20 |
| 1 | 1 | Q | C | Week | 200 | May, 07 2015 00:00:00 | 4 | 10 |
| 1 | 1 | Q | C | Week | 200 | May, 08 2015 00:00:00 | 5 | 20 |
| 1 | 1 | Q | C | Week | 200 | May, 09 2015 00:00:00 | 6 | 30 |
| 1 | 1 | Q | C | Week | 200 | May, 10 2015 00:00:00 | 7 | 40 |
| 1 | 1 | S | C | Week | 100 | May, 04 2015 00:00:00 | 1 | 30 |
| 1 | 1 | S | C | Week | 100 | May, 05 2015 00:00:00 | 2 | 10 |
| 1 | 1 | S | C | Week | 100 | May, 06 2015 00:00:00 | 3 | 10 |
| 1 | 1 | S | C | Week | 100 | May, 07 2015 00:00:00 | 4 | 5 |
| 1 | 1 | S | C | Week | 100 | May, 08 2015 00:00:00 | 5 | 10 |
| 1 | 1 | S | C | Week | 100 | May, 09 2015 00:00:00 | 6 | 15 |
| 1 | 1 | S | C | Week | 100 | May, 10 2015 00:00:00 | 7 | 20 |
| 1 | 2 | I | D | Week | 50 | May, 04 2015 00:00:00 | 1 | 50 |
| 1 | 2 | I | D | Week | 50 | May, 05 2015 00:00:00 | 2 | 50 |
| 1 | 2 | I | D | Week | 50 | May, 06 2015 00:00:00 | 3 | 50 |
| 1 | 2 | I | D | Week | 50 | May, 07 2015 00:00:00 | 4 | 50 |
| 1 | 2 | I | D | Week | 50 | May, 08 2015 00:00:00 | 5 | 50 |
| 1 | 2 | I | D | Week | 50 | May, 09 2015 00:00:00 | 6 | 50 |
| 1 | 2 | I | D | Week | 50 | May, 10 2015 00:00:00 | 7 | 50 |
| 1 | 2 | Q | C | Week | 100 | May, 04 2015 00:00:00 | 1 | 30 |
| 1 | 2 | Q | C | Week | 100 | May, 05 2015 00:00:00 | 2 | 10 |
| 1 | 2 | Q | C | Week | 100 | May, 06 2015 00:00:00 | 3 | 10 |
| 1 | 2 | Q | C | Week | 100 | May, 07 2015 00:00:00 | 4 | 5 |
| 1 | 2 | Q | C | Week | 100 | May, 08 2015 00:00:00 | 5 | 10 |
| 1 | 2 | Q | C | Week | 100 | May, 09 2015 00:00:00 | 6 | 15 |
| 1 | 2 | Q | C | Week | 100 | May, 10 2015 00:00:00 | 7 | 20 |
| 1 | 2 | S | C | Week | 150 | May, 04 2015 00:00:00 | 1 | 45 |
| 1 | 2 | S | C | Week | 150 | May, 05 2015 00:00:00 | 2 | 15 |
| 1 | 2 | S | C | Week | 150 | May, 06 2015 00:00:00 | 3 | 15 |
| 1 | 2 | S | C | Week | 150 | May, 07 2015 00:00:00 | 4 | 7.5 |
| 1 | 2 | S | C | Week | 150 | May, 08 2015 00:00:00 | 5 | 15 |
| 1 | 2 | S | C | Week | 150 | May, 09 2015 00:00:00 | 6 | 22.5 |
| 1 | 2 | S | C | Week | 150 | May, 10 2015 00:00:00 | 7 | 30 |
Once you have this, you need to pivot the result, which can be done by simple GROUP BY
Query:
with x as (
select
r_id,
i_id,
metric,
ctype,
timespan,
quantity,
tdate + level - 1 as m_tdate,
level as m_level,
(case ctype
when 'C' then
(case level
when 1 then 0.3
when 2 then 0.1
when 3 then 0.1
when 4 then 0.05
when 5 then 0.1
when 6 then 0.15
when 7 then 0.2
end)
else 1
end) * quantity as m_quantity
from myt
where timespan = 'Week'
connect by level <= 7
and r_id = prior r_id
and i_id = prior i_id
and metric = prior metric
and ctype = prior ctype
and timespan = prior timespan
and prior sys_guid() is not null
UNION ALL
select
r_id,
i_id,
metric,
ctype,
timespan,
quantity,
tdate as m_tdate,
1 as m_level,
quantity as m_quantity
from myt
where timespan = 'Day'
)
select
r_id,
i_id,
m_tdate,
sum(case when metric = 'S' then m_quantity end) S,
sum(case when metric = 'Q' then m_quantity end) Q,
sum(case when metric = 'I' then m_quantity end) I
from x
group by
r_id,
i_id,
m_tdate
order by
r_id,
i_id,
m_tdate
Results:
| R_ID | I_ID | M_TDATE | S | Q | I |
|------|------|-------------------------|--------|--------|-----|
| 1 | 1 | May, 04 2015 00:00:00 | 30 | 60 | 80 |
| 1 | 1 | May, 05 2015 00:00:00 | 10 | 20 | 80 |
| 1 | 1 | May, 06 2015 00:00:00 | 10 | 20 | 80 |
| 1 | 1 | May, 07 2015 00:00:00 | 5 | 10 | 80 |
| 1 | 1 | May, 08 2015 00:00:00 | 10 | 20 | 80 |
| 1 | 1 | May, 09 2015 00:00:00 | 15 | 30 | 80 |
| 1 | 1 | May, 10 2015 00:00:00 | 20 | 40 | 80 |
| 1 | 2 | April, 03 2015 00:00:00 | (null) | (null) | 120 |
| 1 | 2 | May, 04 2015 00:00:00 | 45 | 30 | 50 |
| 1 | 2 | May, 05 2015 00:00:00 | 15 | 10 | 50 |
| 1 | 2 | May, 06 2015 00:00:00 | 15 | 10 | 50 |
| 1 | 2 | May, 07 2015 00:00:00 | 7.5 | 5 | 50 |
| 1 | 2 | May, 08 2015 00:00:00 | 15 | 10 | 50 |
| 1 | 2 | May, 09 2015 00:00:00 | 22.5 | 15 | 50 |
| 1 | 2 | May, 10 2015 00:00:00 | 30 | 20 | 50 |