Assume our company has multiple marketing campaigns for one specific product, which might boost the sales of it in certain way. The results of the campaign are shown in the following data table:
Date CampaignID QtySold
2017-01-05 1 20
2017-01-18 2 35
2017-01-23 1 15
…
For modeling purposes, the desired output table looks like this:
CampaignID JanQtySold FebQtySold … DecQtySold
1 55 30
2 45 20
…
N
I have tried to get the month of each transaction for each campaign, and then group by campaignID and month.
select
campaignid,
strftime('%m',date) as Month,
sum(qtysold) as Sum_Qty
from campaign
group by campaignid, month
;
The return should include a unique CampaignID but I don't know how to proceed.
I believe that you want something like :-
select
campaignid,
coalesce(
(
SELECT sum(qtysold) FROM campaign AS c2 WHERE campaign.campaignid = c2.campaignid AND CAST(strftime("%m",date) AS INTEGER) = 1
)
,0) AS JanQtySold,
coalesce(
(
SELECT sum(qtysold) FROM campaign AS c2 WHERE campaign.campaignid = c2.campaignid AND CAST(strftime("%m",date) AS INTEGER) = 2
)
,0) As FebQtySold,
coalesce(
(
SELECT sum(qtysold) FROM campaign AS c2 WHERE campaign.campaignid = c2.campaignid AND CAST(strftime("%m",date) AS INTEGER) = 3
)
,0) As MarQtySold
/* .............. and so on ..........*/
from campaign
group by campaignid
;
That is introducing more groups is not really what you want for more derived columns (introducing more grouping components will introduce more rows, so you only want the 1 grouping component i.e. the campaign id). Rather you want to use sub-queries to generate the data for the derived columns.
Note for brevity only 3 months (Jan-Mar) have been shown, the other months is just a matter of copying one of the months and then amending the test value and the column name respectively.
Example :-
Using :-
DROP TABLE IF EXISTS campaign;
CREATE TABLE IF NOT EXISTS campaign (Date TEXT, CampaignID INTEGER, QtySold INTEGER);
INSERT INTO campaign VALUES
('2017-01-05',1,20),('2017-01-23',1,15),('2017-02-01',1,5),
('2017-01-18',2,35)
;
select
campaignid,
coalesce(
(
SELECT sum(qtysold) FROM campaign AS c2 WHERE campaign.campaignid = c2.campaignid AND CAST(strftime("%m",date) AS INTEGER) = 1
)
,0) AS JanQtySold,
coalesce(
(
SELECT sum(qtysold) FROM campaign AS c2 WHERE campaign.campaignid = c2.campaignid AND CAST(strftime("%m",date) AS INTEGER) = 2
)
,0) As FebQtySold,
coalesce(
(
SELECT sum(qtysold) FROM campaign AS c2 WHERE campaign.campaignid = c2.campaignid AND CAST(strftime("%m",date) AS INTEGER) = 3
)
,0) As MarQtySold
from campaign
group by campaignid
;
results in :-
You need conditional aggregation with a CASE statement inside SUM():
select
CampaignID,
sum(case strftime('%m',Date) when '01' then QtySold else 0 end) as JanQtySold,
sum(case strftime('%m',Date) when '02' then QtySold else 0 end) as FebQtySold,
sum(case strftime('%m',Date) when '03' then QtySold else 0 end) as MarQtySold,
sum(case strftime('%m',Date) when '04' then QtySold else 0 end) as AprQtySold,
sum(case strftime('%m',Date) when '05' then QtySold else 0 end) as MayQtySold,
sum(case strftime('%m',Date) when '06' then QtySold else 0 end) as JunQtySold,
sum(case strftime('%m',Date) when '07' then QtySold else 0 end) as JulQtySold,
sum(case strftime('%m',Date) when '08' then QtySold else 0 end) as AugQtySold,
sum(case strftime('%m',Date) when '09' then QtySold else 0 end) as SepQtySold,
sum(case strftime('%m',Date) when '10' then QtySold else 0 end) as OctQtySold,
sum(case strftime('%m',Date) when '11' then QtySold else 0 end) as NovQtySold,
sum(case strftime('%m',Date) when '12' then QtySold else 0 end) as DecQtySold
from campaign
where strftime('%Y',Date) = '2017'
group by CampaignID
I added the condition:
where strftime('%Y',Date) = '2017'
just in case there are rows in the table for multiple years.
If you don't want zeros in the results just remove all else 0 from the case statements.
See the demo.
Just to make it more efficient, you can a use a CTE:
with cte as (
select
CampaignID,
strftime('%Y',Date) as year,
strftime('%m',Date) as month,
QtySold
from campaign
)
select
CampaignID,
sum(case month when '01' then QtySold else 0 end) as JanQtySold,
sum(case month when '02' then QtySold else 0 end) as FebQtySold,
sum(case month when '03' then QtySold else 0 end) as MarQtySold,
sum(case month when '04' then QtySold else 0 end) as AprQtySold,
sum(case month when '05' then QtySold else 0 end) as MayQtySold,
sum(case month when '06' then QtySold else 0 end) as JunQtySold,
sum(case month when '07' then QtySold else 0 end) as JulQtySold,
sum(case month when '08' then QtySold else 0 end) as AugQtySold,
sum(case month when '09' then QtySold else 0 end) as SepQtySold,
sum(case month when '10' then QtySold else 0 end) as OctQtySold,
sum(case month when '11' then QtySold else 0 end) as NovQtySold,
sum(case month when '12' then QtySold else 0 end) as DecQtySold
from cte
where year = '2017'
group by CampaignID
See the demo.
Results:
| CampaignID | JanQtySold | FebQtySold | MarQtySold | AprQtySold | MayQtySold | JunQtySold | JulQtySold | AugQtySold | SepQtySold | OctQtySold | NovQtySold | DecQtySold |
| ---------- | ---------- | ---------- | ---------- | ---------- | ---------- | ---------- | ---------- | ---------- | ---------- | ---------- | ---------- | ---------- |
| 1 | 35 | 65 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 2 | 35 | 75 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
enter image description here
How to generate dates between tow date column based on each row
A row generator technique should be used, such as:
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> with test (sno, start_date, end_date) as
2 (select 1, date '2018-01-01', date '2018-01-05' from dual union
3 select 2, date '2018-01-03', date '2018-01-05' from dual
4 )
5 select sno, start_date + column_value - 1 datum
6 from test,
7 table(cast(multiset(select level from dual
8 connect by level <= end_date - start_date + 1)
9 as sys.odcinumberlist))
10 order by sno, datum;
SNO DATUM
---------- ----------
1 01.01.2018
1 02.01.2018
1 03.01.2018
1 04.01.2018
1 05.01.2018
2 03.01.2018
2 04.01.2018
2 05.01.2018
8 rows selected.
SQL>
I need the the below output on Teradata :
DATE_HOME WORKING_DAY
01/01/2018 0
02/01/2018 1
03/01/2018 1
04/01/2018 1
05/01/2018 1
06/01/2018 0
07/01/2018 0
08/01/2018 1
09/01/2018 1
Output required
DATE_HOME WORKING_DAY Updated_DATE
01/01/2018 0 02/01/2018
02/01/2018 1 02/01/2018
03/01/2018 1 03/01/2018
04/01/2018 1 04/01/2018
05/01/2018 1 05/01/2018
06/01/2018 0 08/01/2018
07/01/2018 0 08/01/2018
08/01/2018 1 08/01/2018
09/01/2018 1 09/01/2018
That's a simple task for first_value:
first_value(case when WORKING_DAY = 1 then DATE_HOME end ignore nulls)
over (order by DATE_HOME
rows between current date and unbounded following)
Change the non-business dates into NULL and then search for the first non-NULL value.
Edit:
In fact there's no need for first_value as you sort by the same column, a simple min works, too:
min(case when WORKING_DAY = 1 then DATE_HOME end)
over (order by DATE_HOME
rows between current date and unbounded following)
Good lord, this is ugly, but it seems to work. I don't have access to a TD system, so it's more verbose than it could be:
SELECT
date_home,
working_day,
CAST(
CASE
-- If current date is a non-work day, add appropriate number of days to non-work day to get next work-day
WHEN working_day = 0 THEN date_home + INTERVAL '1' DAY * (ROW_NUMBER() OVER(PARTITION BY update_date ORDER BY date_home DESC))
ELSE date_home
END
AS DATE) AS Update_date
FROM (
SELECT
date_home,
working_day,
CASE
-- If current and previous days were non-work days, group row by PrevWorkDay value
WHEN MIN(working_day) OVER(ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) = 0 AND working_day = 0 THEN MIN(PrevWorkDay) OVER(ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING)
ELSE PrevWorkDay
END AS Update_Date
FROM (
SELECT
date_home,
working_day,
CASE
-- Track "baseline" previous date_home value for new group of "non-work day" rows
WHEN COALESCE(MIN(working_day) OVER(ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING), 1) = 1 AND working_day = 0 THEN COALESCE(MIN(Date_Home) OVER(ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING), date_home)
ELSE NULL
END AS PrevWorkDay
FROM holiday_calendar
) src
) src
ORDER BY date_home
This assumes your source data is stored in a table called "holiday_calendar".
The COALESCE's are used to handle the first row in the result set, which can't compute values for the previous row, since there is no previous row.
Give it a try and let me know.
How would one create my expected results. Any help would be appreciated, Thanks in advance!
Master Calendar:
SELECT DISTINCT
--CA.CALENDAR_DATE,
TO_CHAR(CALENDAR_DATE,'MM/DD/YYYY') AS CALENDAR_DATE
TO_CHAR(NEXT_DAY(CALENDAR_DATE, 'Monday') - 7, 'MM/DD/YY-') ||
TO_CHAR(NEXT_DAY(CALENDAR_DATE, 'Monday') - 1, 'MM/DD/YY') AS WEEK_OF_YEAR,
ROW_NUMBER () OVER ( ORDER BY CALENDAR_DATE) AS MasterCalendar_RNK
FROM CALENDAR CA
WHERE 1=1
--AND CA.CALENDAR_DATE BETWEEN ADD_MONTHS(TRUNC(SYSDATE), -12) AND TRUNC(SYSDATE)
--AND CA.CALENDAR_DATE BETWEEN TRUNC(SYSDATE) -5 AND TRUNC(SYSDATE)
ORDER BY TO_DATE(CALENDAR_DATE,'MM/DD/YYYY') DESC
Input:
Member StartDate EndDate
A 1/31/17
B 2/1/17 2/15/17
Expected Results:
Member StartDate EndDate Week_Of_Year Active
A 1/31/17 1/30/17-2/5/17 1
A 1/31/17 2/6/17-2/12/17 1
A 1/31/17 2/13/17-2/19/17 1
B 2/1/17 2/15/17 1/30/17/2/5/17 1
B 2/1/17 2/15/17 2/6/17-2/12/17 1
B 2/1/17 2/15/17 2/13/17-2/19/17 1
I have the below table: I also have a calendar table if needed.
ID Start_dt End_dt
1 1/9/2016 3/10/2016
Expected Output:
ID Start_dt End_dt Month ActiveCustomerPerMonth
1 1/9/16 3/10/2016 201601 1
1 1/9/16 3/10/2016 201602 1
1 1/9/16 3/10/2016 201603 0 (Not Active end of Month)
I need this as I'm working on a current query that will utilize a case statement to count if the customer was active for that month. If the member was active on the last day of the month, the member would be considred active for that month. But I need to be able to count for al months for that customer.
CASE
WHEN LAST_DAY(x.END_DT) = x.END_DT
THEN '1'
WHEN TO_CHAR(X.END_DT,'MM/DD/YYYY') != '01/01/3000'
OR X.DISCHARGE_REASON IS NOT NULL
THEN '0'
WHEN X.FIRST_ASSGN_DT IS NULL
THEN '0'
ELSE '1'
END ActiveMemberForMonth
I'm new to Oracle and was reading about connect by but did not understand the process and not sure if this would be the proper place to use.
Something like this.
with
test_data ( id, start_dt, end_dt ) as (
select 1, to_date('1/9/2016' , 'mm/dd/yyyy'), to_date('3/10/2016', 'mm/dd/yyyy')
from dual union all
select 2, to_date('1/23/2016', 'mm/dd/yyyy'), to_date('5/31/2016', 'mm/dd/yyyy')
from dual
)
-- end of test data; solution (SQL query) begins below this line
select id, start_dt, end_dt,
to_char(add_months(trunc(start_dt, 'mm'), level - 1), 'yyyymm') as mth,
case when end_dt < last_day(end_dt)
and level = 1 + months_between(trunc(end_dt, 'mm'), trunc(start_dt, 'mm'))
then 0 else 1 end as active_at_month_end
from test_data
connect by level <= 1 + months_between(trunc(end_dt, 'mm'), trunc(start_dt, 'mm'))
and prior id = id
and prior sys_guid() is not null
order by id, mth -- optional
;
ID START_DT END_DT MTH ACTIVE_AT_MONTH_END
--- ---------- ---------- ------ -------------------
1 2016-01-09 2016-03-10 201601 1
1 2016-01-09 2016-03-10 201602 1
1 2016-01-09 2016-03-10 201603 0
2 2016-01-23 2016-05-31 201601 1
2 2016-01-23 2016-05-31 201602 1
2 2016-01-23 2016-05-31 201603 1
2 2016-01-23 2016-05-31 201604 1
2 2016-01-23 2016-05-31 201605 1
8 rows selected.