GROUP BY after CASE WHEN - sql

I am trying to create a table from a join and summing some fields based on id. This part is working great. I am also trying to add an additional column and using a case when statement I want to populate it.
Here is the script
CREATE TABLE TABLE1
AS
SELECT ID, IDC, SUM(AMOUNT) PRICE, SUM(COST) COST, SUM(AMOUNT-COST) PROFIT,
CASE PROFIT
WHEN PROFIT < 1000 THEN 'Low'
WHEN PROFIT < 5000 THEN 'Medium'
ELSE 'High'
END AS PROFITLEVEL
FROM
(SELECT DISTINCT ID, IDC, AMOUNT, COST
FROM ORDER_ITEMS
LEFT JOIN ORDERS
ON ID = IDC)
GROUP BY ID, IDC;
This however returns a ORA-00905 : Missing keyword error.
Any help would be appreciated

You are using the CASE in a wrong way; besides, you try to use the alias PROFIT at the same level you define it.
You need to edit you CASE and use the expression that gives the PROFIT instead of the alias PROFIT:
CREATE TABLE TABLE1 AS
SELECT ID,
IDC,
SUM(AMOUNT) PRICE,
SUM(COST) COST,
SUM(AMOUNT - COST) PROFIT,
CASE
WHEN SUM(AMOUNT - COST) < 1000 THEN 'Low'
WHEN SUM(AMOUNT - COST) < 5000 THEN 'Medium'
ELSE 'High'
END AS PROFITLEVEL
FROM (SELECT DISTINCT ID,
IDC,
AMOUNT,
COST
FROM ORDER_ITEMS LEFT JOIN ORDERS ON ID = IDC)
GROUP BY ID, IDC;
The way you tried to use the CASE is useful if you need to check single values; for example:
select level,
case level
when 1 then 'one'
when 2 then 'two'
else 'other'
end
from dual
connect by level <=3

Related

SQL Server 2016: Group by while updating the records

I have a scenario wherein I need to determine the Margin Type for each Customer/Business combination against a product - meaning the Product with the highest Margin for a Customer /Business combination should be identified as 'High' Margin Type. All the other Product with the same combination should be identified as Low. So, I need a SQL to update the column MARGIN_TYPE accordingly.
I tried doing something like this, but do not understand how to group it by customer/business combination. Any help will be appreciated. Thank you!
UPDATE ORDER_TABLE
SET MARGIN_TYPE = 'High'
where MARGIN = (SELECT MARGIN FROM (SELECT MAX(MARGIN) FROM ORDER_TABLE) AS
MARGIN)
I think you just want an updatable CTE:
WITH CTE AS(
SELECT Customer,
Margin,
MarginType,
MAX(Margin) OVER (PARTITION BY Customer) AS MaxMargin
FROM dbo.YOurTable)
UPDATE CTE
SET MarginType = CASE Margin WHEN MaxMargin THEN 'High' ELSE 'Low' END;
With an updatebale CTE:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY CUSTOMER, BUSINESS ORDER BY MARGIN DESC) rn
FROM ORDER_TABLE
)
UPDATE cte
SET MARGIN_TYPE = CASE WHEN rn = 1 THEN 'High' ELSE 'Low' END
Please use below query,
merge into ORDER_TABLE OT
using (select customer, business, product, margin, case when rnk = 1 then 'High' else
'Low' end as MARGIN_TYPE
select customer, business, product, margin, row_number() over (partition by customer,
business, product, margin order by margin desc) as rnk
from ORDER_TABLE
group by) QRY
on (OT.customer = QRY.customer and OT.business = QRY.business and OT.margin =
QRY.margin)
when matched then
update
set
OT.MARGIN_TYPE = QRY.MARGIN_TYPE;
UPDATE ORD
SET MARGIN_TYPE = case ORD.margin = m.margin then 'high' else 'low' end
from ORDER_TABLE ORD
left outer join
(SELECT customer,business,MAX(MARGIN) MARGIN
FROM ORDER_TABLE
group by customer,business) m
on(ORD.customer = m.customer and ORD.business = m.business)

calculate rate of attribute by id sql

First, this is my table schema:
order_id, product_id, add_to_cart_order, reordered
My problem is calculate the rate of reordered by product. So we can see "add_to_cart_order" is useless, I don't know for "order_id". "reordered" can be have '1' and '0' value.
For the moment, I can have the count of "reordored" by product_id with
SELECT
product_id,
COUNT(reordered)
FROM
train
WHERE
reordered = '1'
GROUP BY
product_id;
and the count of occurrence of a product with
SELECT
product_id, COUNT(*)
FROM
train
GROUP BY
product_id;
I tried
SELECT
t1.product_id,
COUNT(t1.product_id) / (SELECT COUNT(reordered)
FROM train t2
WHERE t2.reordered = '1'
AND t1.product_id = t2.product_id
GROUP BY product_id)
FROM
train t1
GROUP BY
t1.product_id;
But it takes too much time (I don't know if it's the right request because I don't have results yet)
Is this what you are looking for?
SELECT Product_id, SUM(CASE WHEN reordered=1 THEN 1 ELSE 0 END ) /
COUNT(*) AS ReorderedRate
FROM
train
GROUP BY Product_id
Try this elegant
SELECT t1.product_id, SUM(CASE WHEN reordered = 1 THEN 1 ELSE 0 END) / COUNT(t1.product_id)
FROM train t1
GROUP BY t1.product_id;
I think the simplest method is to use AVG():
SELECT product_id,
AVG(CASE WHEN reordered = '1' THEN 1.0 ELSE 0 END)
FROM train
GROUP BY product_id;
If reordered is really a number that only takes on the values 0 and 1, then you can further simplify this to either:
SELECT product_id, AVG(reordered)
FROM train
GROUP BY product_id;
or:
SELECT product_id, AVG(reordered * 1.0)
FROM train
GROUP BY product_id;
The second is needed in databases where the average of an integer is returned as an integer.
this will compute for each product_id :
the number of lines in train cnt_prod
the number of lines in train cnt_prod_reorder that was reordered
SELECT t1.product_id, COUNT(t1.product_id) as cnt_prd,
COUNT(case when t.1.reordered='1' then 1 else NULL end ) as cnt_prd_reord
from train t1 group by t1.product_id;
So after you can do :
select st.product_id , st.cnt_prd , st.cnt_prd / st.cnt_prd_reord
from (
SELECT t1.product_id, COUNT(t1.product_id) as cnt_prd,
COUNT(case when t.1.reordered='1' then 1 else NULL end ) as cnt_prd_reord
from train t1 group by t1.product_id
) as st ;

calculate the balance amount and insert another table

I need some help on building an sql query, I have below two queries, I want to combine the sum of debit substracted from credit and then insert result as balance into another table
select sum(amount)
from ACCOUNT_TRANSACTIONS
where CUSTOMER_USER_NAME='55555' and transaction_type='credit' and account_type='customer' and IS_DELETED='false'
select sum(amount)
from ACCOUNT_TRANSACTIONS
where CUSTOMER_USER_NAME='55555' and transaction_type='debit' and account_type='customer' and IS_DELETED='false'
Could use CROSS APPLY
select a.*, b.CreditSum, c.DebitSum
from ACCOUNT_TRANSACTIONS a
cross apply
(select sum(amount) as CreditSum
from ACCOUNT_TRANSACTIONS
where CUSTOMER_USER_NAME='55555' and transaction_type='credit' and account_type='customer' and IS_DELETED='false'
) b
cross apply
(select sum(amount) as DebitSum
from ACCOUNT_TRANSACTIONS
where CUSTOMER_USER_NAME='55555' and transaction_type='debit' and account_type='customer' and IS_DELETED='false'
) c
where a.CUSTOMER_USER_NAME='55555' and a.account_type='customer' and a.IS_DELETED='false'
You can do this using conditional aggregation:
select sum(case when transaction_type = 'credit' then amount when transaction_type = 'debit' then - amount end) as balance
from ACCOUNT_TRANSACTIONS
where CUSTOMER_USER_NAME = '55555' and
account_type = 'customer' and
IS_DELETED = 'false' ;
You would then insert this into another table using insert, although I'm not sure how a single value in a single row would be useful.

Calculating difference in two values in the same column

I want to find the difference (the Profit & Loss) when I combine records with the same CATEGORY value (e.g. A's will be combined and C's will be combined).
I think you want conditional aggregation:
select category,
sum(iif(side = "BUY", - quantity * price, quantity * price)) as net
from t
where side in ("BUY", "SELL") -- may not be necessary
group by category;
Select category, buy.amt-sell.amt ProfitorLoss
from
(SELECT sum(price*quantity) amt, Category
FROM yourtable
WHERE side = 'BUY'
GROUP BY Category) buy,
(SELECT sum(price*quantity) amt, Category
FROM yourtable
WHERE side = 'SELL'
GROUP BY Category) sell
where buy.category = sell.category

SQL filter for lowest price

I have a huge table of pricing data, with the following columns
source, product, term, miles, price
The data looks something like this
LL, PH, 3, 10000, 100.00
BA, PH, 3, 10000, 200.00
LL, CH, 3, 10000, 300.00
BA, CH, 3, 10000, 400.00
I need to create a query to filter the rows to show only the line with the lowest price for each unique source/product/term/miles combination. So in the above example i'd like to end up with:
LL, PH, 3, 10000, 100.00
BA, PH, 3, 10000, 200.00
Any help appreciated. Thanks.
You can use ROW_NUMBER() for this:
SQL Fiddle
SELECT
source, product, term, miles, price
FROM(
SELECT *,
RN = ROW_NUMER() OVER(PARTITION BY source, product, term, miles ORDER BY price)
FROM [your_table]
)t
WHERE RN = 1
Or as per Coder of Code's comment, you could use MIN() and GROUP BY:
SQL Fiddle
SELECT
source, product, term, miles, price = MIN(price)
FROM [your_table]
GROUP BY source, product, term, miles
Use NOT EXISTS to return rows where no lower price exists for the same source:
select source, product, term, miles, price
from tablename t1
where not exists (select 1 from tablename t2
where t1.source = t2.source
and t2.price < t1.price)
If you want the source/product combos with lowest price, add product to the sub-select's condition:
select source, product, term, miles, price
from tablename t1
where not exists (select 1 from tablename t2
where t1.source = t2.source
and t1.product = t2.product
and t2.price < t1.price)
etc.
If there's a tie, both rows will be returned!
SELECT
source, product, term, miles, MIN(price) AS price
FROM [table_name]
GROUP BY source, product, term, miles