How to Generate weeks of supply - teradata

I currently have a table where I have number of units sold and the week that they are sold in. I am trying to get the average of the prior six weeks of units sold. I am using the Fiscal EOW date as the date to show the units sold. I am stumped on this problem.
I am using the following:
select
b.FISC_EOW_DT,
a.*,
avg(net_unit_qty) over (partition by sid order by FISC_EOW_DT rows between 5
preceding and current row) as avsaleslast6wk
FROM tbl1 a
JOIN (SELECT DISTINCT FISC_WK_OF_MTH_ID,FISC_EOW_DT FROM tbl2 ) B
ON B.FISC_WK_OF_MTH_ID=A.FISC_WK_OF_MTH_ID
where sid = 12345
This works however it calculates the last 5 rows, regardless if they are the previous week or not. So for example:
if the weeks was:
12/01/2016
01/08/2017
06/01/2017
08/01/2017
It will calculate the average of these 4 weeks even though they are not consecutive. I need to know how to calculate the Average sales including weeks that are not consecutive. So for the week going back from:
01/08/2017
to
06/01/2017
there would be 0 for average sales since the last 6 weeks are not represented.
Any help would be greatly appreciated.
TBL1
SID FISC_EOW_DT NET_UNIT_QTY
1234 01/01/2017 1
1234 01/08/2017 2
1234 01/15/2017 3
1234 01/22/2017 2
1234 01/29/2017 1
1234 06/09/2017 1
Expected result:
SID FISC_EOW_DT NET_UNIT_QTY AVSALESLAST6WEEKS
1234 01/01/2017 1 0(0+0+0+0+0+0)/6
1234 01/08/2017 2 .167(1+0+0+0+0+0)/6
1234 01/15/2017 3 .50(2+1+0+0+0+0)/6
1234 01/21/2017 2 1(3+2+1+0+0+0)/6
1234 01/28/2017 1 1.33(2+3+2+1+0+0)/6
1234 06/09/2017 1 0(0+0+0+0+0+0)6<----SINCE THERE HAVE BEEN NO SALES FOR MULTIPLE WEEKS
I would like to account for the weeks where there have been no sales. So I will need to create the blank weeks through coding, however I am not sure how to do that.
SELECT
D1_PROD_11_SKU_ID
,D4_TIME_01_FISC_WK_OF_MTH_ID
,FISC_EOW_DT
,net_unit_qty
,(
CASE WHEN Min(FISC_EOW_DT)
Over (PARTITION BY D1_PROD_11_SKU_ID ORDER BY FISC_EOW_DT ROWS BETWEEN 1 Preceding AND current row) >= FISC_EOW_DT - 6*7
THEN Min(net_unit_qty)
Over (PARTITION BY D1_PROD_11_SKU_ID ORDER BY FISC_EOW_DT ROWS BETWEEN 1 Preceding AND current row)
ELSE 0
END
+
CASE WHEN Min(FISC_EOW_DT)
Over (PARTITION BY D1_PROD_11_SKU_ID ORDER BY FISC_EOW_DT ROWS BETWEEN 1 Preceding AND 1 Preceding) >= FISC_EOW_DT -6*7
THEN Min(net_unit_qty)
Over (PARTITION BY D1_PROD_11_SKU_ID ORDER BY FISC_EOW_DT ROWS BETWEEN 1 Preceding AND 1 Preceding)
ELSE 0
END
+ CASE WHEN Min(FISC_EOW_DT)
Over (PARTITION BY D1_PROD_11_SKU_ID ORDER BY FISC_EOW_DT ROWS BETWEEN 2 Preceding AND 2 Preceding) >= FISC_EOW_DT - 6*7
THEN Min(net_unit_qty)
Over (PARTITION BY D1_PROD_11_SKU_ID ORDER BY FISC_EOW_DT ROWS BETWEEN 2 Preceding AND 2 Preceding)
ELSE 0
END
+ CASE WHEN Min(FISC_EOW_DT)
Over (PARTITION BY D1_PROD_11_SKU_ID ORDER BY FISC_EOW_DT ROWS BETWEEN 3 Preceding AND 3 Preceding) >= FISC_EOW_DT - 6*7
THEN Min(net_unit_qty)
Over (PARTITION BY D1_PROD_11_SKU_ID ORDER BY FISC_EOW_DT ROWS BETWEEN 3 Preceding AND 3 Preceding)
ELSE 0
END
+ CASE WHEN Min(FISC_EOW_DT)
Over (PARTITION BY D1_PROD_11_SKU_ID ORDER BY FISC_EOW_DT ROWS BETWEEN 4 Preceding AND 4 Preceding) >= FISC_EOW_DT - 6*7
THEN Min(net_unit_qty)
Over (PARTITION BY D1_PROD_11_SKU_ID ORDER BY FISC_EOW_DT ROWS BETWEEN 4 Preceding AND 4 Preceding)
ELSE 0
END
+ CASE WHEN Min(FISC_EOW_DT)
Over (PARTITION BY D1_PROD_11_SKU_ID ORDER BY FISC_EOW_DT ROWS BETWEEN 5 Preceding AND 5 Preceding) >= FISC_EOW_DT - 6*7
THEN Min(net_unit_qty)
Over (PARTITION BY D1_PROD_11_SKU_ID ORDER BY FISC_EOW_DT ROWS BETWEEN 5 Preceding AND 5 Preceding)
ELSE 0
END
) AS ROLLING_SIX_WEEK_SALES
FROM TBL1

This utilizes EXPAND ON to create the missing rows with a zero quantity, applies the average and finally removes the added rows again:
SELECT
SID
,Begin(pd) AS eow_dt
-- set the quantity to zero for non-existing weeks
,CASE WHEN FISC_EOW_DT = Begin(pd) THEN net_unit_qty ELSE 0 END AS qty
-- finally calculate the average of the previous 5 plus the current row
,Sum(qty)
Over (PARTITION BY sid
ORDER BY Begin(pd)
ROWS 5 Preceding) / 6.000 AS avsaleslast6wk
FROM
(
SELECT
SID
,FISC_EOW_DT
,NET_UNIT_QTY
,pd
FROM
(
SELECT
SID
,FISC_EOW_DT
,NET_UNIT_QTY
-- first: find the next existing row using LEAD
,Coalesce(Min(FISC_EOW_DT)
Over (PARTITION BY SID
ORDER BY FISC_EOW_DT
ROWS BETWEEN 1 Following AND 1 Following )
,FISC_EOW_DT+7) AS next_week
FROM tbl1
) AS dt
-- then: create the missing weeks
EXPAND ON PERIOD(FISC_EOW_DT, next_week) AS pd BY INTERVAL '7' DAY
) AS dt
-- remove the non-existing weeks again
QUALIFY qty > 0
ORDER BY 1,2
Edit:
Of course this assumes that there's only a single row per week.
Another solution uses a brute force approach which is ok for a small number of weeks: check if each of the previous 6 rows is within the range of 6 weeks, then add the quantity.
SELECT
SID
,FISC_EOW_DT
,net_unit_qty
,(CASE WHEN Min(FISC_EOW_DT)
Over (PARTITION BY sid ORDER BY FISC_EOW_DT ROWS BETWEEN 1 Preceding AND 1 Preceding) >= FISC_EOW_DT - 5*7
THEN Min(net_unit_qty)
Over (PARTITION BY sid ORDER BY FISC_EOW_DT ROWS BETWEEN 1 Preceding AND 1 Preceding)
ELSE 0
END
+ CASE WHEN Min(FISC_EOW_DT)
Over (PARTITION BY sid ORDER BY FISC_EOW_DT ROWS BETWEEN 2 Preceding AND 2 Preceding) >= FISC_EOW_DT - 5*7
THEN Min(net_unit_qty)
Over (PARTITION BY sid ORDER BY FISC_EOW_DT ROWS BETWEEN 2 Preceding AND 2 Preceding)
ELSE 0
END
+ CASE WHEN Min(FISC_EOW_DT)
Over (PARTITION BY sid ORDER BY FISC_EOW_DT ROWS BETWEEN 3 Preceding AND 3 Preceding) >= FISC_EOW_DT - 5*7
THEN Min(net_unit_qty)
Over (PARTITION BY sid ORDER BY FISC_EOW_DT ROWS BETWEEN 3 Preceding AND 3 Preceding)
ELSE 0
END
+ CASE WHEN Min(FISC_EOW_DT)
Over (PARTITION BY sid ORDER BY FISC_EOW_DT ROWS BETWEEN 4 Preceding AND 4 Preceding) >= FISC_EOW_DT - 5*7
THEN Min(net_unit_qty)
Over (PARTITION BY sid ORDER BY FISC_EOW_DT ROWS BETWEEN 4 Preceding AND 4 Preceding)
ELSE 0
END
+ CASE WHEN Min(FISC_EOW_DT)
Over (PARTITION BY sid ORDER BY FISC_EOW_DT ROWS BETWEEN 5 Preceding AND 5 Preceding) >= FISC_EOW_DT - 5*7
THEN Min(net_unit_qty)
Over (PARTITION BY sid ORDER BY FISC_EOW_DT ROWS BETWEEN 5 Preceding AND 5 Preceding)
ELSE 0
END
+ net_unit_qty
) / 6.000 AS avsaleslast6wk
FROM tbl1
This is a lot of cut & paste & modify, but probably quite efficient, a single step.

Looking into your sample data and desired output, I think you are looking for sum of previous 6 weeks divided by 6 and reset when there is gap between weeks, in this case below can be an option.
WITH difference (
sid
,FISC_EOW_DT
,diff
)
AS (
SELECT t1.sid
,t1.FISC_EOW_DT
,t1.FISC_EOW_DT - Min(t1.FISC_EOW_DT) OVER (
ORDER BY t1.FISC_EOW_DT ROWS BETWEEN 1 preceding
AND 1 preceding
) AS diff
FROM table1 t1
)
SELECT t.sid
,t.FISC_EOW_DT
,t.NET_UNIT_QTY
,coalesce(cast(SUM(t.NET_UNIT_QTY) OVER (
ORDER BY t.FISC_EOW_DT RESET WHEN d.diff > 7 ROWS BETWEEN 7 preceding
AND 1 preceding
) AS DECIMAL(4, 3)) / 6, 0) AS AverageCalc
FROM difference d
INNER JOIN table1 t ON d.sid = t.sid
AND d.FISC_EOW_DT = t.FISC_EOW_DT;
what the query does is that derived table calculate the difference of dates between current and preceding row and in main query, the difference is used as reset if its more than 7 determining that weeks are not continuous.
Result:
SID FISC_EOW_DT NET_UNIT_QTY AverageCalc
---- ----------- ------------ ---------
1234 2017-01-01 1 0.000
1234 2017-01-08 2 0.167
1234 2017-01-15 3 0.500
1234 2017-01-22 2 1.000
1234 2017-01-29 1 1.333
1234 2017-06-09 1 0.000
PFB screenshot FYR.

Related

Mariadb Increment counter with ORDER BY

I Have table with description
Table Name : mt_user
column : id , name
Records
id
name
1
Tom
10
Carren
30
Jessy
Then I Query:
select r.id,
#rownum := #rownum + 1 as rownum
from mt_user r
cross join
(SELECT
#rownum := 0
) params
order by r.id desc;
the result is :
id
rownum
30
3
10
2
1
1
But if the query :
select r.id,
#rownum := #rownum + 1 as rownum
from mt_user r
cross join
(SELECT
#rownum := 0
) params
order by r.id asc;
the result is:
id
rownum
1
1
10
2
30
3
the question is, how can I achieved the result like this
id
rownum
1
1
10
2
30
3
but with order by r.id desc
because I trying the query in mariadb version 10.5.9, it have no problem.
it act strange on mariadb version 10.6.10 or 10.9.2

SQLite - Count Distinct with three column groupings ignores last grouping

I'm working on a database about directors from US companies. One specific table I've created from a query contains all directors who were in a company 5 years before it declared bankruptcy (query below):
CREATE TABLE IF NOT EXISTS y_query AS
SELECT
A.dir_id, A.linked_dir_ir, A.bankrupt_co_id, A.co_name, A.event_date,
B.conn_co_id, B.conn_co_name, B.conn_co_type, B.conn_co_start_date, B.conn_co_end_date,
(CASE WHEN conn_co_start_date >= event_date THEN 1 ELSE 0 END) AS dir_hired
FROM (
SELECT
C.dir_id, C.linked_dir_ir, C.conn_co_id as bankrupt_co_id, C.overlap_start_date, C.overlap_end_date, C.conn_co_type,
D.co_name, D.filing_date, D.event_date
FROM director_network C
INNER JOIN company_bankruptcy D
ON C.conn_co_id = D.co_id
WHERE (
(C.overlap_end_date >= DATE(D.event_date, '-5 years')) AND
(C.overlap_end_date <= D.event_date))
) A
LEFT OUTER JOIN company_network B
ON A.dir_id = B.dir_id;
(linked_dir_ir should read linked_dir_id but I'm on a slow computer and it would take ~1hr to change the column name)
So, that table is fine, the query takes a while to run but it works as intended. But now I need to count the number of directors (linked_dir_ir) a given director (dir_id) was associated (ie, each row is a connection) for each bankrupt company (bankrupt_co_id) the director was in, and each connected company (conn_co_id). There can be many lines connecting a pair of directors because a new entry is made if any of them receives a promotion and so on.
(A few rows of y_query table: y_query)
So, I thought this query would work, but I'm running into problems:
SELECT dir_id, bankrupt_co_id, conn_co_id, COUNT(DISTINCT linked_dir_ir) as conn_dirs
FROM y_query
WHERE bankrupt_co_id != conn_co_id
GROUP BY dir_id, bankrupt_co_id, conn_co_id;
I am not sure why but this query disregards the last group (conn_co_id) and the result is the same for any dir_id and bankrupt_co_id, where it should vary also based on what conn_co_id is. A sample of the result (it only changes when dir_id or bankrupt_co_id changes, as depicted):
resulting query
The result is the same as if I'd only grouped with dir_id and bankrupt_co_id, when it should be different for each conn_co_id. I've done a lot of research into GROUP BY statements and how it can be tricky, but I haven't been able to crack this one. I'd greatly appreciate any help on this!
Its hard to reproduce your result. But your query with multiple groupings seems to be fine. See example below:
CREATE TABLE test (dir_id INTEGER, bankrupt_co_id INTEGER, conn_co_id INTEGER, linked_dir_id INTEGER);
with some dummy data:
select * from test;
dir_id bankrupt_co_id conn_co_id linked_dir_id
---------- -------------- ---------- -------------
1 1 1 1
1 1 1 2
1 1 1 4
1 1 1 5
1 1 1 6
1 1 1 7
1 1 2 1
1 1 2 2
1 1 2 3
1 2 2 1
3 3 2 1
3 1 2 1
3 2 2 1
3 2 2 4
1 1 1 3
1 1 4 4
The query including your conn_co_id results in:
SELECT dir_id, bankrupt_co_id, conn_co_id, COUNT(DISTINCT linked_dir_id) as conn_dirs FROM test WHERE bankrupt_co_id!=conn_co_id GROUP BY dir_id, bankrupt_co_id, conn_co_id;
dir_id bankrupt_co_id conn_co_id conn_dirs
---------- -------------- ---------- ----------
1 1 2 3
1 1 4 1
3 1 2 1
3 3 2 1
whereas without the top two results are combined:
SELECT dir_id, bankrupt_co_id, conn_co_id, COUNT(DISTINCT linked_dir_id) as conn_dirs FROM test WHERE bankrupt_co_id!=conn_co_id GROUP BY dir_id, bankrupt_co_id;
dir_id bankrupt_co_id conn_co_id conn_dirs
---------- -------------- ---------- ----------
1 1 4 4
3 1 2 1
3 3 2 1

Updating a column based on conditions

I have a table like:
ID ID2 Name
1 1
2 1
3 2
4 2
5 2
6 3
7 3
8 3
I want to Update the Name column with Values like Name1, name 2 and so on.
This will be based on condition if there are two similar values in ID2 column, for example, the first two rows, then the Name column to be updated with values Name1 and Name2 respectively.Next rows would be Name1, Name2, Name3 respectively and so on. Can someone help me with the logic for this?
ROW_NUMBER with a partition on the ID2 column, ordered by the ID column, should generate the sequences you want. Try this update query:
UPDATE yourTable t1
SET Name = (SELECT 'Name' || t.rn FROM
(
SELECT t2.ID, t2.ID2,
ROW_NUMBER() OVER (PARTITION BY t2.ID2 ORDER BY t2.ID) rn
FROM yourTable t2
WHERE t1.ID = t2.ID AND t1.ID2 = t2.ID2
) t)

sqlite select all rows where some columns are in set of possible values

col0 col1 col2 A B C
0 0 1 1 2 3
0 1 1 1 2 3
0 0 1 1 2 3
0 2 0 1 2 3
0 1 1 1 2 3
Hello, I have a table where some of the columns are col0, col1, col2. First of all I want to find each combination which meet some condition. Let's say for simplicity to find each triple which is more than once in database (group by and having is required in my more complicated real case):
SELECT col0,col1,col2 FROM table GROUP BY col0,col1,col2 HAVING COUNT(*) > 1
Gives me something like:
col0 col1 col2
0 0 1
0 1 1
Now I want to select (actually delete) all such rows which contain one of those col0,col1,col2 combinations.
So something like:
SELECT * FROM table WHERE (col0,col1,col2) IN (...select above...)
But that gives me an error, I guess just one column is allowed for WHERE clause in SQLite. So probably some kind of join has to be used but I struggle with proper syntax and kind of join to be used. How to proceed? Thank you.
The easiest way to select rows with certain combinations to values is to use a join:
SELECT MyTable.*
FROM MyTable
JOIN (SELECT col0, col1, col2
FROM MyTable
GROUP BY col0, col1, col2
HAVING COUNT(*) > 1)
USING (col1, col2, col3)
Joins do not work with DELETE, so you have to use the primary key (or a candidate primary key) in a separate step:
DELETE FROM MyTable
WHERE rowid IN (SELECT MyTable.rowid
FROM MyTable
JOIN (SELECT col0, col1, col2
FROM MyTable
GROUP BY col0, col1, col2
HAVING COUNT(*) > 1)
USING (col1, col2, col3))

pl/sql Compare with previous row

The goal is to combine Time_a within 10 min interval that has same ID.
And group the ID.
ID Time_a -> ID
------------ ----------
1 12:10:00 1
1 12:15:00 2
1 12:20:00 2
2 12:25:00
2 12:35:00
2 02:00:00
It became two '2' because time interval between row5 and row6 is more than 10 min.
I was able to combine within 10-min difference, but it doesn't distinguish ID.
select ID
from(
select id, Time_a, min(time) OVER (order by id, time rows between 1 preceding and 1 preceding) prev_t_stamp
from dual
)
where abs(Time_a-prev_t_stamp)>10/1440
If time_a is date then it works as follow. If it is a timestamp you need to cast it to a date otherwise it will not quite work. Here is my test script
drop table test;
create table test (id number, time_a date);
insert into test values (1, to_date('12:10','HH24:MI'));
insert into test values (1, to_date('12:15','HH24:MI'));
insert into test values (1, to_date('12:20','HH24:MI'));
insert into test values (2, to_date('12:25','HH24:MI'));
insert into test values (2, to_date('12:35','HH24:MI'));
insert into test values (2, to_date('14:00','HH24:MI'));
select id from (
select distinct id, case from (
select ID,case when abs(Time_a-prev_t_stamp)>10/1440 then '>' end case
from(
select id, Time_a, min(time_a) OVER (order by id, time_a rows between 1 preceding and 1 preceding) prev_t_stamp
from test
)
)
) order by 1
This results into:
table TEST dropped.
table TEST created.
1 rows inserted.
1 rows inserted.
1 rows inserted.
1 rows inserted.
1 rows inserted.
1 rows inserted.
ID
--
1
2
2

Resources