Home > Mobile >  Need help for Top 10 Products (by Amounts) - weekly
Need help for Top 10 Products (by Amounts) - weekly

Time:02-26

I have create new table and insert sample data into the table. I want to get the top 10 products rank by amount weekly. I tried SQL Query but give the wrong data. I have shared the table, sample data, and SQL Query below.

I have a table:

CREATE TABLE product_table (
product_name          VARCHAR2(20),
amount                NUMBER,
datetime DATE
)

Sample Data:

INSERT INTO product_table (product_name, amount, datetime)
SELECT 'P1', 10000, TRUNC(SYSDATE, 'IW') FROM DUAL UNION ALL
SELECT 'P2', 15000, TRUNC(SYSDATE, 'IW') FROM DUAL UNION ALL
SELECT 'P3', 18000, TRUNC(SYSDATE, 'IW') FROM DUAL UNION ALL
SELECT 'P4', 11000, TRUNC(SYSDATE, 'IW') FROM DUAL UNION ALL
SELECT 'P5', 13000, TRUNC(SYSDATE, 'IW') FROM DUAL UNION ALL
SELECT 'P6', 16000, TRUNC(SYSDATE, 'IW') FROM DUAL UNION ALL
SELECT 'P7', 19000, TRUNC(SYSDATE, 'IW') FROM DUAL UNION ALL
SELECT 'P8', 20000, TRUNC(SYSDATE, 'IW') FROM DUAL UNION ALL
SELECT 'P9', 24000, TRUNC(SYSDATE, 'IW') FROM DUAL UNION ALL
SELECT 'P10',26000, TRUNC(SYSDATE, 'IW') FROM DUAL UNION ALL
SELECT 'P11',34000, TRUNC(SYSDATE, 'IW') FROM DUAL UNION ALL
SELECT 'P1', 8000, TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL UNION ALL
SELECT 'P2', 17000, TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL UNION ALL
SELECT 'P3', 22000, TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL UNION ALL
SELECT 'P4', 23000, TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL UNION ALL
SELECT 'P5', 26000, TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL UNION ALL
SELECT 'P6', 34000, TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL UNION ALL
SELECT 'P7', 31000, TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL UNION ALL
SELECT 'P8', 42000, TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL UNION ALL
SELECT 'P9', 54000, TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL UNION ALL
SELECT 'P10', 14000,  TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL UNION ALL
SELECT 'P11', 19000, TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY FROM DUAL UNION ALL
SELECT 'P1', 8000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -1) FROM DUAL UNION ALL
SELECT 'P2', 16000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -1) FROM DUAL UNION ALL
SELECT 'P3', 21000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -1) FROM DUAL UNION ALL
SELECT 'P4', 22000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -1) FROM DUAL UNION ALL
SELECT 'P5', 25000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -1) FROM DUAL UNION ALL
SELECT 'P6', 33000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -1) FROM DUAL UNION ALL
SELECT 'P7', 32000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -1) FROM DUAL UNION ALL
SELECT 'P8', 41000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -1) FROM DUAL UNION ALL
SELECT 'P9', 53000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -1) FROM DUAL UNION ALL
SELECT 'P10', 24000,  ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -1) FROM DUAL UNION ALL
SELECT 'P11', 29000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -1) FROM DUAL UNION ALL
SELECT 'P1', 7000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -2) FROM DUAL UNION ALL
SELECT 'P2', 15000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -2) FROM DUAL UNION ALL
SELECT 'P3', 20000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -2) FROM DUAL UNION ALL
SELECT 'P4', 21000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -2) FROM DUAL UNION ALL
SELECT 'P5', 24000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -2) FROM DUAL UNION ALL
SELECT 'P6', 32000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -2) FROM DUAL UNION ALL
SELECT 'P7', 31000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -2) FROM DUAL UNION ALL
SELECT 'P8', 40000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -2) FROM DUAL UNION ALL
SELECT 'P9', 52000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -2) FROM DUAL UNION ALL
SELECT 'P10', 34000,  ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -2) FROM DUAL UNION ALL
SELECT 'P11', 39000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -2) FROM DUAL UNION ALL
SELECT 'P1', 6000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -3) FROM DUAL UNION ALL
SELECT 'P2', 14000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -3) FROM DUAL UNION ALL
SELECT 'P3', 19000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -3) FROM DUAL UNION ALL
SELECT 'P4', 20000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -3) FROM DUAL UNION ALL
SELECT 'P5', 23000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -3) FROM DUAL UNION ALL
SELECT 'P6', 31000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -3) FROM DUAL UNION ALL
SELECT 'P7', 30000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -3) FROM DUAL UNION ALL
SELECT 'P8', 39000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -3) FROM DUAL UNION ALL
SELECT 'P9', 51000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -3) FROM DUAL UNION ALL
SELECT 'P10', 54000,  ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -3) FROM DUAL UNION ALL
SELECT 'P11', 69000, ADD_MONTHS(TRUNC(SYSDATE, 'IW'), -3) FROM DUAL;

Query:

SELECT 
   RANK() OVER (
     ORDER BY SUM(
       CASE
       WHEN datetime >= TRUNC(SYSDATE, 'IW')
       AND  datetime <  TRUNC(SYSDATE, 'IW')   INTERVAL '7' DAY
       THEN 1
       END
     ) DESC
   ) AS rank_this_week,
   product_name,
   SUM(AMOUNT) TOTAL_AMOUNT
FROM PRODUCT_TABLE
GROUP BY product_name, amount
ORDER BY AMOUNT DESC
FETCH FIRST 10 ROWS ONLY;

Result:

enter image description here

This query does not show correct Top 10 Products (by Amounts) - weekly Rank

enter image description here

CodePudding user response:

You haven't said what your expected output is; however, you appear to want to order by the weekly amount so you need to order by the rank_this_week column and not include amount in the GROUP BY clause:

SELECT RANK() OVER (
         ORDER BY SUM(
           CASE
           WHEN datetime >= TRUNC(SYSDATE, 'IW')
           AND  datetime <  TRUNC(SYSDATE, 'IW')   INTERVAL '7' DAY
           THEN amount
           END
         ) DESC
       ) AS rank_this_week,
       product_name,
       SUM(
         CASE
         WHEN datetime >= TRUNC(SYSDATE, 'IW')
         AND  datetime <  TRUNC(SYSDATE, 'IW')   INTERVAL '7' DAY
         THEN amount
         END
       ) AS total_amount_this_week,
       SUM(AMOUNT) TOTAL_AMOUNT
FROM PRODUCT_TABLE
GROUP BY product_name
ORDER BY rank_this_week
FETCH FIRST 10 ROWS ONLY;

Which, for the sample data, outputs:

RANK_THIS_WEEK PRODUCT_NAME TOTAL_AMOUNT_THIS_WEEK TOTAL_AMOUNT
1 P11 34000 190000
2 P10 26000 152000
3 P9 24000 234000
4 P8 20000 182000
5 P7 19000 143000
6 P3 18000 100000
7 P6 16000 146000
8 P2 15000 77000
9 P5 13000 111000
10 P4 11000 97000

db<>fiddle here

CodePudding user response:

After you posted a comment and displayed desired result, here's one option. Read comments within code.

SQL> with
  2  -- Each DATES CTE returns desired "date" value
  3  dates_tw  as (select to_char(sysdate, 'yyyy iw') this_week                 from dual),
  4  dates_pw  as (select to_char(sysdate - 7, 'yyyy iw') previous_week         from dual),
  5  dates_lm  as (select trunc(add_months(sysdate, -1), 'mm') last_month       from dual),
  6  dates_ma2 as (select trunc(add_months(sysdate, -2), 'mm') two_months_ago   from dual),
  7  dates_ma3 as (select trunc(add_months(sysdate, -3), 'mm') three_months_ago from dual),

  8  sums as
  9  -- compute total amount for reach "date" value from DATES CTEs, hence CROSS JOIN.
 10  -- As DATES CTEs contain a single row, that shouldn't be too bad (performance wise)
 11    (select p.product_name,
 12        sum(case when to_char(p.datetime, 'yyyy iw') = tw.this_week         then amount else 0 end) sum_tw,
 13        sum(case when to_char(p.datetime, 'yyyy iw') = pw.previous_week     then amount else 0 end) sum_pw,
 14        sum(case when trunc(p.datetime, 'mm')        = lm.last_month        then amount else 0 end) sum_lm,
 15        sum(case when trunc(p.datetime, 'mm')        = ma2.two_months_ago   then amount else 0 end) sum_ma2,
 16        sum(case when trunc(p.datetime, 'mm')        = ma3.three_months_ago then amount else 0 end) sum_ma3
 17      from product_table p cross join dates_tw tw
 18                           cross join dates_pw pw
 19                           cross join dates_lm lm
 20                           cross join dates_ma2 ma2
 21                           cross join dates_ma3 ma3
 22      group by product_name
 23    ),

 24  ranks as
 25  -- rank products per each total amount
 26    (select s.product_name,
 27       rank() over (order by sum_tw desc) rnk_tw,
 28       rank() over (order by sum_pw desc) rnk_pw,
 29       rank() over (order by sum_lm desc) rnk_lm,
 30       rank() over (order by sum_ma2 desc) rnk_ma2,
 31       rank() over (order by sum_ma3 desc) rnk_ma3
 32     from sums s
 33    )

 34  -- finally, return only product that "now" rank as the first 5 (or as many as you want),
 35  -- and display their rank for other dates
 36  select rnk_tw as "rank this week",
 37         product_name,
 38         rnk_pw as "previous week",
 39         rnk_lm as "last month",
 40         rnk_ma2 as "2 months ago",
 41         rnk_ma3 as "3 months ago"
 42  from ranks
 43  where rnk_tw <= 5
 44  order by rnk_tw;

rank this week PRODUCT_NAME         previous week last month 2 months ago 3 months ago
-------------- -------------------- ------------- ---------- ------------ ------------
             1 P11                              8          5            3            1
             2 P10                             10          7            4            2
             3 P9                               1          1            1            3
             4 P8                               2          2            2            4
             5 P7                               4          4            6            6

SQL>
  • Related