SQL -- Get items responsible for top 50% of sales - sql

I have a table like this:
ITEM_SALES
ITEM_NAME SALES
Item_name_1 5000
...
Item_name_x 3
What I want to get is get the items that represent the top 50% of sales. So for example, if total sales was 10,000, just item_name_1 alone would represent 50% of sales.
I can obviously get total sales with:
select sum(sales) from ITEM_SALES.
...and then divide by 2 to get how many sales 50% of sales is.
However, I don't know how I'd go from there to getting the top items that represent 50% of sales.

You can do this using analytic functions:
select s.*
from (select item_name, sum(sales) as sumsales,
sum(sum(sales)) over (order by sum(sales) desc) as cumesales,
sum(sum(sales)) over () as totsales,
from item_sales
group by item_name
) s
where (cumesales - sumsales) < 0.5 * totsales;
The subquery calculates the sales for each item, as well as two other values:
The cumulative sales, from highest to that item.
The total sales.
The where clause then gets items up to and include the one that passes the 50% threshold.

Oracle Setup:
CREATE TABLE ITEM_SALES ( ITEM_NAME, SALES ) AS
SELECT 'item_name_' || LEVEL, 50 - 3 * (LEVEL - 1)
FROM DUAL
CONNECT BY LEVEL <= 16;
Query:
SELECT *
FROM (
SELECT ITEM_NAME,
SALES,
SUM( SALES ) OVER ( ORDER BY SALES DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS cumulative_sales,
SUM( SALES ) OVER ( ORDER BY SALES DESC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS total_sales
FROM ITEM_SALES
)
WHERE cumulative_sales <= total_sales/2;
Results:
ITEM_NAME SALES CUMULATIVE_SALES TOTAL_SALES
------------ ----- ---------------- -----------
item_name_1 50 50 440
item_name_2 47 97 440
item_name_3 44 141 440
item_name_4 41 182 440
item_name_5 38 220 440

Related

How to use SUM() OVER (partition by)?

Imagine, from 1st to 3rd november you have sold a certain amount of goods (there are two types A and B), and now you need to determine how much was sold in total for the day.
How can I query last 2 columns (sum and quantity for date) that my table looks like this?:
Date Type Quantity Amount Sum_Quantity Sum_Amount
01-11 A 2 100 5 300
01-11 B 3 200 5 300
02-11 A 1 700 3 950
02-11 B 2 250 3 950
03-11 A 2 600 7 800
03-11 B 5 200 7 800
And how can I query, if I want to take the results partitioned by month?
SELECT date,
type,
quantity,
amount,
-- Partition by date
SUM(quantity) OVER (PARTITION BY date) AS sum_quantity_date_part,
SUM(amount) OVER (PARTITION BY date) AS sum_amount_date_part,
-- Partition by month
SUM(quantity) OVER (
PARTITION BY EXTRACT(YEAR FROM date),
EXTRACT(MONTH FROM date)
) AS sum_quantity_month_part,
SUM(amount) OVER (
PARTITION BY EXTRACT(YEAR FROM date),
EXTRACT(MONTH FROM date)
) AS sum_amount_month_part
FROM sales
ORDER BY date, type
;

How to calculate needed amount for supply order?

Table "client_orders":
date
ordered
id
28.05
50
1
23.06
60
2
24.05
50
1
25.06
130
2
Table "stock":
id
amount
date
1
60
23.04
2
90
25.04
1
10
24.04
2
10
24.06
I want to calculate the amount I need to order (to fulfill the stock) for what date. For instance, it should be:
30 by 28.05 (60+10-50-50=-30) for id = 1
-90 by 25.06 (90-60+10-130=-90) for id = 2
I tried to do it with LAG function, but the problem is that the stock here is not updating.
SELECT *,
SUM(amount - ordered) OVER (PARTITION BY sd.id ORDER BY d.date ASC)
FROM stock sd
LEFT JOIN (SELECT date,
id,
ordered
FROM client_orders) AS d
ON sd.id = d.id
Couldn't find anything similar on the web. Grateful if you share articles/examples how to do that.
You could make a union of the two tables and sum all stock amounts with the negative of ordered amounts. For the date you could instead take the corresponding maximum value.
SELECT id,
SUM(amount),
MAX(date)
FROM (SELECT id,
-ordered AS amount,
date
FROM client_orders
UNION
SELECT *
FROM stock
) stock_and_orders
GROUP BY id
Try it here.

SQL Cumulative Sum by Group by time condition

I have a table with columns dummy_id, date_registered, item_id, quantity, price, like this:
dummy_id
date_registered
item_id
quantity
price
my_cumulative
1
2013-07-01
100
10
34.5
10
2
2013-07-01
145
8
2.3
8
3
2013-07-11
100
20
34.5
30
4
2013-07-23
100
15
34.5
45
5
2013-07-24
145
10
34.5
18
And if I want to calculate column my_cumulative which contains the cumulative totals of each item_id order by date_registered I use this code:
select dummy_id, date_registered, item_id, quantity, price,
sum(quantity) over (partition by item_id order by date_registered) as cumulative
from table t;
And it works well. But what if I now want to my_cumulative column for each row to count only orders from the last month? (calculate the sum of the quantity only for rows where the date_register column is less than the current one, no more than a month)
Is there any way to do this in sql? (prefer postgresql)
If you want cumulative quantities for the current month -- which is what I suspect you want, then change the partition by:
select dummy_id, date_registered, item_id, quantity, price,
sum(quantity) over (partition by item_id, date_trunc('month', date_registered) order by date_registered) as cumulative
from table t;
If you really want the last month, then use a range window frame with interval:
select dummy_id, date_registered, item_id, quantity, price,
sum(quantity) over (partition by item_id
order by date_registered
range between interval '1 month' preceding and current row
) as cumulative
from table t;
The first seems much more useful to me.

How to do a Min and Max of date but following the changes in price points

I'm not really sure how to word this question better so I'll provide the data that I have and the result that I'm after.
This is the data that I have
sku sales qty date
A 100 1 1-Jan-19
A 200 2 2-Jan-19
A 100 1 3-Jan-19
A 240 2 4-Jan-19
A 360 3 5-Jan-19
A 360 4 6-Jan-19
A 200 2 7-Jan-19
A 90 1 8-Jan-19
B 100 1 9-Jan-19
B 200 2 10-Jan-19
And this is the result that I'm after
sku price sum(qty) sum(sales) min(date) max(date)
A 100 4 400 1-Jan-19 3-Jan-19
A 120 5 600 4-Jan-19 5-Jan-19
A 90 4 360 6-Jan-19 6-Jan-19
A 100 2 200 7-Jan-19 7-Jan-19
A 90 1 90 8-Jan-19 8-Jan-19
B 100 3 300 9-Jan-19 10-Jan-19
As you can see, I'm trying to get the min and max date of each price point, where price = sales/qty. At this point, I can get the min and max date of the same price but I can separate it when there's another price in between. I think I have to use some sort of min(date) over (partition by sales/qty order by date) but I can't figure it out yet.
I'm using Redshift SQL
This is a gaps-and-islands query. You can do this by generating a sequence and subtracting that from the date. Then aggregate:
select sku, price, sum(qty), sum(sales),
min(date), max(date)
from (select t.*,
row_number() over (partition by sku, price order by date) as seqnum
from t
) t
group by sku, price, (date - seqnum * interval '1 day')
order by sku, price, min(date);
You can do with Sub Query and LAG
FIDDLE DEMO
SELECT SKU, Price, SUM(Qty) SumQty, SUM(Sales) SumSales, MIN(date) MinDate, MAX(date) MaxDate
FROM (
SELECT SKU,Price,SUM(is_change) OVER(order by SKU, date) is_change,Sales, Qty,date
FROM (SELECT SKU, Sales/Qty AS Price, Sales, Qty,date,
CASE WHEN Sales/Qty = lag(Sales/Qty) over (order by SKU, date)
and SKU = lag(SKU) OVER (order by SKU, date) then 0 ELSE 1 END AS is_change
FROM Tbl
)InnerSelect
) X GROUP BY sku, price,is_change
ORDER BY SKU,MIN(date)
Output

Oracle SQL Query:Find out which year total sales amount is maximum

my working table, Table name: sales
Here Is MY TABLE, [sl_no is primary key] table structure:
CREATE TABLE SALES
( SL_NO NUMBER PRIMARY KEY, REGION VARCHAR2(10) NOT NULL,
MONTH VARCHAR2(20) NOT NULL, YEAR NUMBER NOT NULL,
SALES_AMOUNT NUMBER NOT NULL )
and here is table data:
SQL> select * from sales;
SL_NO REGION MONTH YEAR SALES_AMOUNT
---------- ---------- -------------------- ---------- ------------
1 east december 2011 750000
2 east august 2011 800000
3 west january 2012 640000
5 east march 2012 1200000
6 west february 2011 580000
4 west april 2011 555000
6 rows selected.
I have tried this query to view total sales amount of those[2011,2012] year;
SELECT year, SUM(sales_amount) FROM sales GROUP BY year;
YEAR SUM(SALES_AMOUNT)
---------- -----------------
2011 2685000
2012 1840000
MY GOAL:> I want to find out the year of maximum sales amount.
I tried this,and work perfectly...but when i want to display that year also, it gives an Error.
SQL> select max(sum(sales_amount)) from sales group by year;
MAX(SUM(SALES_AMOUNT))
----------------------
2685000
SQL> select year, max(sum(sales_amount)) from sales group by year;
select year, max(sum(sales_amount)) from sales group by year
*
ERROR at line 1:
ORA-00937: not a single-group group function
Extra addition: if multiple rows have same value means....when sales amount of both year[2011,2012] remain same, Then....
plZ help me to Solve this problem.
This should work.
with yr_agg as (
select year, sum(sales_amount) as total
from sales
group by year
)
select year, total as max_total
from yr_agg
where total = (select max(total)
from yr_agg);
I think the simplest way is to order the results and take the first row:
select year, sales_amount
from (SELECT year, SUM(sales_amount) as sales_amount
FROM sales
GROUP BY year
order by sum(sales_amount) desc
) t
where rownum = 1;
EDIT:
If you need to display all the matching rows (which isn't mentioned in the question), I would suggest using the dense_rank() analytic function:
select year, sales_amount
from (SELECT year, SUM(sales_amount) as sales_amount,
dense_rank(over order by SUM(sales_amount) desc) as seqnum
FROM sales
GROUP BY year
order by sum(sales_amount) desc
) t
where seqnum = 1;
Or, you might like the max() version instead:
select year, sales_amount
from (SELECT year, SUM(sales_amount) as sales_amount,
max(sum(sales_amount)) over () as maxsa
FROM sales
GROUP BY year
order by sum(sales_amount) desc
) t
where sales_amount = maxsa;
Following select should do what you need (untested, do not have Oracle at home):
select year, total
from (
select year, sum(sales_amount) total
from sales
group by year
)
where total = (select max(total_amount)
from (
select year, sum(sales_amount) total_amount
from sales
group by year
))
Take in account, though, that it might give you different years in each execution if two of them have exactly the same total amount. You might want to include some more conditions to avoid this.
Here is my Query where multiple row can select
SELECT year,MAX(total_sale) as max_total
FROM
(SELECT year,SUM(sales_amount) AS total_sale FROM sales GROUP BY year)
GROUP BY
year HAVING MAX(total_sale) =
(SELECT MAX(total_sale) FROM (SELECT SUM(sales_amount) AS total_sale FROM sales GROUP BY year));