How to write an SQL report using distinct and sum - sql

I'm trying to write an SQL report that groups rows, removes duplicates, and sums up values in virtual columns.
I have this table
make | model | warranty | price
-------+--------+----------+-------
Honda | Accord | 2 | 700
Honda | Civic | 3 | 500
Lexus | ES 350 | 1 | 900
Lexus | ES 350 | 1 | 900
Lexus | ES 350 | 2 | 1300
Lexus | ES 350 | 3 | 1800
(6 rows)
I'm trying to create a report that adds two virtual columns, qty and total. Total is the sum of qty * price. The table should like the one below.
qty | make | model | warranty | price | total
-------+--------+--------+----------+-------------
1 | Honda | Accord | 2 | 700 | 700
1 | Honda | Civic | 3 | 500 | 500
2 | Lexus | ES 350 | 1 | 900 | 1800
1 | Lexus | ES 350 | 2 | 1300 | 1300
1 | Lexus | ES 350 | 3 | 1800 | 1800
(5 rows)

I think this is simple aggregation:
select count(*) as qty, make, model, warranty,
avg(price) as price, sum(price) as total
from t
group by make, model, warranty;

Related

SUB TOTAL WITH MULTIPLE TABLE

I want to show the subtotals of each product name and maybe the commands I made are useless,
is there any suggestions for me
I have 3 tables
select
ORDERS.ORDER_NUM,
PRODUCT.PRODUCT_ID,
PRODUCT.PRODUCT_NAME,
ORDER_DETAILS.QUANTITY,
ORDER_DETAILS.UNIT_PRICE,
sum(ORDER_DETAILS.UNIT_PRICE) as SUBTOTAL
from
ORDER_DETAILS
inner join ORDERS on ORDER_DETAILS.ORDER_NUM = ORDERS.ORDER_NUM
inner join PRODUCT on ORDER_DETAILS.PRODUCT_ID = PRODUCT.PRODUCT_ID
group by
ORDER_DETAILS.ITEM_NUM
order by
PRODUCT.PRODUCT_NAME;
+-----------+------------+--------------+----------+------------+----------+
| ORDER_NUM | PRODUCT_ID | PRODUCT_NAME | QUANTITY | UNIT_PRICE | SUBTOTAL |
+-----------+------------+--------------+----------+------------+----------+
| 3004 | 2001 | BEER | 10 | 160,000 | 160 |
| 3012 | 2001 | BEER | 5 | 160,000 | 160 |
| 3002 | 2005 | CAKE | 5 | 50,000 | 50 |
| 3001 | 2004 | CIGARETTE | 2 | 25,000 | 25 |
| 3011 | 2004 | CIGARETTE | 5 | 25,000 | 25 |
| 3005 | 2007 | ICE CREAM | 50 | 10,000 | 10 |
| 3007 | 2010 | MILK | 3 | 45,000 | 45 |
| 3010 | 2010 | MILK | 7 | 12,000 | 12 |
| 3008 | 2008 | NOODLES | 1 | 5,000 | 5 |
| 3013 | 2006 | SODA | 50 | 12,000 | 12 |
| 3009 | 2002 | WINE | 1 | 200,000 | 200 |
| 3003 | 2002 | WINE | 2 | 200,000 | 200 |
| 3006 | 2002 | WINE | 1 | 200,000 | 200 |
+-----------+------------+--------------+----------+------------+----------+
You don't specify the database. However, you need to fix your query:
Details from the order and order line are not appropriate, if you are summarizing by product.
The SELECT and GROUP BY lists should be consistent.
The total you want is probably the price times the quantity.
So, the query should be more like this:
select p.PRODUCT_ID, p.PRODUCT_NAME,
sum(od.UNIT_PRICE * od.QUANTITY) as SUBTOTAL
from ORDER_DETAILS od join
ORDERS o
on od.ORDER_NUM = o.ORDER_NUM join
PRODUCT p
on od.PRODUCT_ID = p.PRODUCT_ID
group by p.PRODUCT_ID, p.PRODUCT_NAME
order by p.PRODUCT_NAME;
Also note the use of table aliases, so the query is easier to write and to read.

Divide a number into multiple bands

I've been dealing with a problem of splitting a metric into several bands. To give you some context, let's take this example where we have certain number of orders per customer. Now, a customer may order n number of products. Let's give the customer certain discount based on the number of orders. The discounts are offered based a tiered model. I'm leaving out multiple product categories to keep it simple. Here are some examples of the tables.
Orders table
Customer | order_no
----------------------------
Customer1 | 400
Customer2 | 1200
Customer3 | 40
Customer4 | 2000
Customer5 | 700
Tiered pricing table
Tier | lower_th | higer_th | price |
--------------------------------------
Tier1 | 0 | 250 | 50 |
TIer2 | 251 | 500 | 45 |
Tier3 | 501 | 1000 | 40 |
TIer4 | 1001 | 10000 | 30 |
Example1: I want to be able to charge Customer1 $50 for 250 order and $45 for the rest of 150 products out of a total of 400.
Example2: I want to be able to charge Customer5 $50 for 250 order and $45 for another 250 and $40 for the rest 200 products out of a total of 700.
How do I achieve this in PostgreSQL? My output needs to be the following for Customer1. What's the best way to split the total number of orders and join it to the pricing tiers to get the corresponding amount?
Customer | order_no | charges |
--------------------------------
Customer1 | 250 | 50 |
Customer1 | 150 | 45 |
You can think of your tiers as intervals.
Two intervals [a1, b1] and [a2, b2] intersect when
a1 <= b2 AND b1 >= a2
The number of orders is another interval that always starts at 1.
Your two intervals are: Tiers [lower_th, higer_th] and Orders [1, order_no].
The query is a simple join using this intersection expression:
SELECT *
,CASE WHEN O.order_no > T.higer_th
THEN T.higer_th - T.lower_th + 1 -- full tier
ELSE O.order_no - T.lower_th + 1
END AS SplitOrderNumbers
FROM
Orders AS O
INNER JOIN Tiers AS T
-- ON 1 <= T.higer_th AND O.order_no >= T.lower_th
ON O.order_no >= T.lower_th
ORDER BY
O.Customer
,T.lower_th
;
You don't really need the 1 <= T.higer_th part, because it is always true, so the expression becomes simple O.order_no >= T.lower_th.
Also, usually it is better to store intervals as [closed; open). It usually simplifies arithmetic, similar to why most programming languages have array indexes starting at 0, not 1. Your intervals seem to be [closed; closed]. In this case you need to set lower_th to 1, not 0 and have +1 in the calculations.
With this adjustment of the sample data this query produces the following result:
+-----------+----------+-------+----------+----------+-------+-------------------+
| Customer | order_no | Tier | lower_th | higer_th | price | SplitOrderNumbers |
+-----------+----------+-------+----------+----------+-------+-------------------+
| Customer1 | 400 | Tier1 | 1 | 250 | 50.00 | 250 |
| Customer1 | 400 | Tier2 | 251 | 500 | 45.00 | 150 |
| Customer2 | 1200 | Tier1 | 1 | 250 | 50.00 | 250 |
| Customer2 | 1200 | Tier2 | 251 | 500 | 45.00 | 250 |
| Customer2 | 1200 | Tier3 | 501 | 1000 | 40.00 | 500 |
| Customer2 | 1200 | Tier4 | 1001 | 10000 | 30.00 | 200 |
| Customer3 | 40 | Tier1 | 1 | 250 | 50.00 | 40 |
| Customer4 | 2000 | Tier1 | 1 | 250 | 50.00 | 250 |
| Customer4 | 2000 | Tier2 | 251 | 500 | 45.00 | 250 |
| Customer4 | 2000 | Tier3 | 501 | 1000 | 40.00 | 500 |
| Customer4 | 2000 | Tier4 | 1001 | 10000 | 30.00 | 1000 |
| Customer5 | 700 | Tier1 | 1 | 250 | 50.00 | 250 |
| Customer5 | 700 | Tier2 | 251 | 500 | 45.00 | 250 |
| Customer5 | 700 | Tier3 | 501 | 1000 | 40.00 | 200 |
+-----------+----------+-------+----------+----------+-------+-------------------+
For pricing data I would use a table like this to make data maintenance easier
create table pricing_data
(
high_limit int,
price numeric
);
A view will give you the intervals you need for this using a window function:
create view pricing as
select coalesce(lag(high_limit) over (order by high_limit), 0) as last_limit,
high_limit, price
from pricing_data;
This simplifies the breakout into pricing tiers:
select o.customer,
least(o.order_no - p.last_limit, p.high_limit - p.last_limit) as order_no,
p.price as charges
from orders o
join pricing p on p.last_limit < o.order_no
order by o.customer, p.price desc
;
Result:
customer order_no charges
Customer1 250 50
Customer1 150 45
Customer2 250 50
Customer2 250 45
Customer2 500 40
Customer2 200 30
Customer3 40 50
Customer4 250 50
Customer4 250 45
Customer4 500 40
Customer4 1000 30
Customer5 250 50
Customer5 250 45
Customer5 200 40
14 rows
Fiddle here.

to make a global means in a request

my table SALES
ID | NAMEPRODUCT | CATEGORY | AMOUNT
1 | COMPUTER | IT | 600
2 | T-SHIRT | CLOTHING | 25
3 | Doll | TOY | 10
4 | KEYBORD | IT | 30
5 | CAP | CLOTHING | 10
3 | TOY CAR | TOY | 40
I would like to make this type of request :
SELECT SALES1.NAMEPRODUCT,
SALES1.CATEGORY,
SUM(SALES1.AMOUNT)/( select SUM(AMOUNT) FROM SALES AS SALES2 WHERE SALES1.CATEGORY=SALES2.CATEGORY) as ratio
from SALES AS SALES1
GROUP BY
SALES1.NAMEPRODUCT,SALES1.CATEGORY
NAMEPRODUCT | CATEGORY | ratio
COMPUTER | IT | 0,95
T-SHIRT | CLOTHING | 0,71
Doll | TOY | 0,20
KEYBORD | IT | 0,5
CAP | CLOTHING | 0,29
TOY CAR | TOY | 0,80
OR
SELECT SALES1.NAMEPRODUCT,
SALES1.CATEGORY,
SUM(SALES1.AMOUNT),
( select SUM(SALES2 .AMOUNT) FROM SALES2 WHERE SALES1.CATEGORY=SALES2.CATEGORY) AS TOTAL_AMOUNT_for_category
from SALES1
GROUP BY
SALES1.NAMEPRODUCT,
SALES1.CATEGORY
NAMEPRODUCT | CATEGORY | AMOUNT |TOTAL_AMOUNT_for_category
COMPUTER | IT | 600 | 630
T-SHIRT | CLOTHING | 25 | 35
Doll | TOY | 10 | 50
KEYBORD | IT | 30 | 630
CAP | CLOTHING | 10 | 35
TOY CAR | TOY | 40 | 50
I use Cassandra database with Apache Hive connector
I can't make this type of request with hive and cassandra
Can someone help me ?
You seem to have only one table. If so, use window functions:
select nameproduct, category,
sum(amount) as amount,
sum(amount) * 1.0 / sum(sum(amount)) over (partition by category) as ratio
from sales1
group by nameproduct, category;

Display the results of sub category and rollup on the parent category

I am using SAP Hana and wanted to get the data with a roll-up to the category. Also, show the data of the subcategory.
Calculating Total
example:-
So my data currently looks like this, based on my query:
category | sub_category | count
------------+----------------------+--------
Grocery | Dairy | 200
Grocery | fruits | 600
Kitchen | Microwave | 100
Kitchen | Stove | 100
Other | shoes | 400
Other | racks | 500
What I want is:
category | sub_category | count
-------------+----------------------+--------
Grocery | Dairy | 200
Grocery | fruits | 600
Total Grocery| null | 800
Kitchen | Microwave | 100
Kitchen | Stove | 100
Total Kitchen| null | 200
Other | shoes | 400
Other | racks | 500
Total Other | null | 900

query to match a value in multiple ranges

I have a table as below:
promo_code | minimum_order | discount
------------+-------------------+-------------
100 | 10 | 10%
100 | 20 | 15%
100 | 30 | 20%
101 | 13 | 7%
102 | 8 | 10%
102 | 12 | 14%
In another table I have the sales in quantity and the promotions they are eligible
sales | promo_eligibility | record_id
--------+-----------------------+-------------
14 | 100 | 1000
7 | 101 | 1001
25 | 102 | 1002
I need to get the discount that correspond to the sales volume and promotion...
as in the above examples:
record_id | discount | Comments
------------+---------------+--------------
1000 | 15% | (bigger than 10 and lower than 20)
1001 | 0% | (did not reached the minimum)
1002 | 14% |
The number of thresholds could vary from 0 to 3
Any ideas?
Thanks!!!
Something like this:
select
r.record_id,
isnull(d.discount, '0%')
from records r
outer apply (
select top 1 discount
from discounts d
where d.promo_code = r.promo_eligibility
and d.minimum_order <= r.sales)