Oracle SQL if statement? - sql

how would it be possible to check if a certain value in salesman_id is null, and if it is, assign it to something else (in this case, 0)?
here's what i've wrote up so far:
SELECT O.SALESMAN_ID, SUM(OI.UNIT_PRICE * QUANTITY)
FROM ORDERS O, ORDER_ITEMS OI
GROUP BY O.SALESMAN_ID
ORDER BY O.SALESMAN_ID;

I would suggest:
SELECT COALESCE(O.SALESMAN_ID, 0) as SALESMAN_ID, SUM(OI.UNIT_PRICE * QUANTITY)
FROM ORDERS O JOIN
ORDER_ITEMS OI
ON o.ORDER_ID = OI.ORDER_ID. -- guessing at the relationship
GROUP BY COALESCE(O.SALESMAN_ID, 0)
ORDER BY COALESCE(O.SALESMAN_ID, 0);
Your query as written would produce non-sensical results. Always use proper, explicit, standard, readable JOIN syntax. Never use commas in the FROM clause.

SELECT nvl(O.SALESMAN_ID,0), SUM(OI.UNIT_PRICE * QUANTITY)
FROM ORDERS O, ORDER_ITEMS OI
GROUP BY nvl(O.SALESMAN_ID,0)
ORDER BY 1;

For completeness sake, a further option would be...
SELECT salesman_id, SUM(total)
FROM (
SELECT CASE WHEN o.salesman_id IS NULL
THEN 0
ELSE o.salesman_id as SALESMAN_ID,
OI.UNIT_PRICE * QUANTITY AS total
FROM ORDERS O JOIN
ORDER_ITEMS OI
ON o.ORDER_ID = OI.ORDER_ID
) AS ilv
GROUP BY salesman_id;
But null is a value too....
SELECT CASE WHEN ilv.salesman_id IS NULL
THEN 0
ELSE ilv.salesman_id as SALESMAN_ID,
total
FROM (
SELECT salesman_id
SUM(OI.UNIT_PRICE * QUANTITY) AS total
FROM ORDERS O JOIN
ORDER_ITEMS OI
ON o.ORDER_ID = OI.ORDER_ID
GROUP BY sles,an_id
) AS ilv;

I think the query is incorrect, you need to join orders and ORDER_ITEMS using order_id
scott

Related

How to select top 300 for each orders total order item value

I want to view the top 300 items ordered by total net price, how do I do this please?
Using SSMS 2014
If I remove group by I get error: Column 'orderitems.orderid' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Please see workings below:
select top 300
orderitems.orderid, orders.traderid, orders.orderdate,
SUM(orderitems.nettprice) AS nettprice
from orderitems
INNER JOIN orders ON orders.tradertype = 'S' AND orders.id =
orderitems.orderid
where orderitems.ordertype = 'PO'
group by orderitems.orderid, orders.traderid, orders.orderdate,
orderitems.nettprice
order by orderitems.nettprice DESC
You need to order by the SUM value. You can either put that in the ORDER BY explicitly, or you can use the SELECT column name without a table reference (in other words the column alias you use in the SELECT)
I strongly recommend you use short table aliases to make your code more readable
select top 300
oi.orderid,
o.traderid,
o.orderdate,
SUM(oi.nettprice) AS nettprice
from orderitems AS oi
INNER JOIN orders AS o ON o.tradertype = 'S' AND o.id = oi.orderid
where oi.ordertype = 'PO'
group by
oi.orderid, o.traderid, o.orderdate
order by
nettprice DESC
-- alternatively
order by
SUM(oi.nettprice) DESC
Perhaps what you need here is a windowed SUM and then a TOP. The PARTITION BY clause is based on this comment:
SELECT TOP (300)
oi.orderid,
o.traderid,
o.orderdate,
SUM(oi.nettprice) OVER (PARTITION BY oi.itemnumber, oi.partid, oi.quantity) AS totalnettprice
FROM dbo.orderitems oi
INNER JOIN dbo.orders o ON o.id = oi.orderid
WHERE o.tradertype = 'S'
AND oi.ordertype = 'PO'
ORDER BY oi.totalnettprice DESC;
Very simple:
select top 300
itm.orderid,
ord.traderid,
ord.orderdate,
SUM(itm.nettprice) AS price
from orderitems itm
INNER JOIN orders ord ON
ord.tradertype = 'S' AND ord.id = itm.orderid
where itm.ordertype = 'PO'
group by
itm.orderid,
ord.traderid,
ord.orderdate
order by price DESC

SQL sum from single table with join

I have two table orders and orderitems.
order table has id,order_total,recieved_amount
orderitems table has id,order_id,name,total_item
I want the sum of recieved_amount, order_total from the order table and sum of total_item from orderitems. so I used
SELECT SUM(`received_amount`) as totalRecieved,
SUM(`order_total`) as orderTotal
FROM `orders` AS `Order`
LEFT JOIN `order_items` AS `OrderItems`
ON (`OrderItems`.`order_id`=`Order`.`id`)
and
SELECT SUM(`received_amount`) as totalRecieved,
SUM(`order_total`) as orderTotal
FROM `orders` AS `Order`
LEFT JOIN `order_items` AS `OrderItems`
ON (`OrderItems`.`order_id`=`Order`.`id`)
group by order.id
but none of them is giving me the correct result.
You are aggregating at two levels of your data hierarchy. This causes a problem with Cartesian products, for each order.
The solution is to aggregate along order_items before doing the join:
SELECT SUM(received_amount) as totalRecieved,
SUM(order_total) as orderTotal
FROM orders o LEFT JOIN
(SELECT oi.order_id, SUM(total_items) as total_items
FROM order_items oi
GROUP BY oi.order_id
) oi
ON oi.order_id = o.id;
As an alternative, you can benefit from cte structure or a temp table like below:
;with cte (order_id, SumTotalItems) as (
SELECT oi.order_id, SUM(total_items) as SumTotalItems
FROM order_items oi
GROUP BY oi.order_id
)
select sum(o.receivedamount) as SumReceivedAmount
, sum(o.order_total) as SumOrderTotal
, cte.SumTotalItems
from orders o
left outer join cte on o.id = cte.order_id

SQL EXISTS returns all rows, more than two tables

I know similar questions like this have been asked before, but I have not seen one for more than 2 tables. And there seems to be a difference.
I have three tables from which I need fields, customers where I need customerID and orderID from, orders from which I get customerID and orderID and lineitems from which I get orderID and quantity (= quantity ordered).
I want to find out how many customers bought more than 2 of the same item, so basically quantity > 2 with:
SELECT COUNT(DISTINCT custID)
FROM customers
WHERE EXISTS(
SELECT *
FROM customers C, orders O, lineitems L
WHERE C.custID = O.custID AND O.orderID = L.orderID AND L.quantity > 2
);
I do not understand why it is returning me the count of all rows. I am correlating the subqueries before checking the >2 condition, am I not?
I am a beginner at SQL, so I'd be thankful if you could explain it to me fundamentally, if necessary. Thanks.
You don't have to repeat customers table in the EXISTS subquery. This is the idea of correlation: use the table of the outer query in order to correlate.
SELECT COUNT(DISTINCT custID)
FROM customers c
WHERE EXISTS(
SELECT *
FROM orders O
JOIN lineitems L ON O.orderID = L.orderID
WHERE C.custID = O.custID AND L.quantity > 2
);
I would approach this as two aggregations:
select count(distinct customerid)
from (select o.customerid, l.itemid, count(*) as cnt
from lineitems li join
orders o
on o.orderID = l.orderId
group by o.customerid, l.itemid
) ol
where cnt >= 2;
The inner query counts the number of items that each customer has purchased. The outer counts the number of customers.
EDIT:
I may have misunderstood the question for the above answer. If you just want where quantity >= 2, then that is much easier:
select count(distinct o.customerid)
from lineitems li join
orders o
on o.orderID = l.orderId
where l.quantity >= 2;
This is probably the simplest way to express the query.
I suggest you to use "joins" ,
Try this
select
count(*)
From
orders o
inner join
lineitems l
on
l.orderID = o.orderID
where
l.quantity > 2

Query Total Amounts for Orders where the amount of each item was greater than >100

Using the following two tables on SQL Server 2005, how would I write a query to return the First_Name, Last_Name, Order_Date and total amount of an order where any order line item amounts for any order (OD_Amount) are greater than 100.
Orders
Order_ID
First_Name
Last_Name
Order_Date
Order_Details
Order_Detail_ID
Order_ID
OD_Item_No
OD_Amount
Something like this maybe?
Select
t1.Order_ID, First_Name, Last_Name, Order_Date, Sum(OD_Amount) as Order_Total
From
Orders t1
Inner Join
Order_Details t2
on t1.Order_ID = t2.Order_ID
Where
t1.Order_Id in (Select Distinct Order_Id from Order_Details where OD_Amount > 100)
Group By
t1.Order_ID, First_Name, Last_Name, Order_Date
SELECT
O.first_name,
O.last_name,
O.order_date,
SUM(OD.amount)
FROM
Orders O
INNER JOIN Order_Details OD ON OD.order_id = O.order_id
WHERE EXISTS
(
SELECT
FROM
Order_Details OD2
WHERE
OD2.order_id = O.order_id AND
OD2.amount > 100
)
GROUP BY
O.first_name,
O.last_name,
O.order_date
This is when you want each individual detail order to be greater than 100
SELECT t1.First_Name, t1.Last_Name, t1.Order_Date, SUM(t2.OD_Amount) AS 'totalAmount'
FROM Orders AS t1
JOIN Order_Details AS t2 ON t1.Order_ID = t2.Order_ID
WHERE t2.OD_Amount > 100
GROUP BY t1.First_Name, t1.Last_Name, t1.Order_Date
If you wanted each order itself to be over 100 then:
SELECT t1.First_Name, t1.Last_Name, t1.Order_Date, SUM(t2.OD_Amount) AS 'totalAmount'
FROM Orders AS t1
JOIN Order_Details AS t2 ON t1.Order_ID = t2.Order_ID
GROUP BY t1.First_Name, t1.Last_Name, t1.Order_Date
HAVING SUM(t2.OD_Amount) > 100
I believe this is what you're looking for:
SELECT o.First_Name, o.Last_Name, sum(od.OD_Amount) As Order_Total_Amount FROM Order_Details AS od, Orders AS o WHERE o.Order_ID = od.Order_ID AND o.OrderID IN (SELECT Order_ID FROM Order_Details WHERE OD_Amount > 100) GROUP BY o.First_Name, o.Last_Name
I believe this is what you are looking for. The EXISTS clause limits the results to only orders that have a single line item > 100. The GROUP BY accounts for multiple orders placed on the same day by including the Order_ID column.
SELECT
o.First_Name
,o.Last_Name
,o.Order_Date
,SUM(od.OD_Amount) AS Total_Amount
FROM Orders o
INNER JOIN Order_Details od ON od.Order_ID = o.Order_ID
WHERE EXISTS(SELECT *
FROM Order_Details od2
WHERE od2.OD_Amount > 100
AND od2.Order_ID = o.Order_ID)
GROUP BY
o.Order_ID
,o.First_Name
,o.Last_Name
,o.Order_Date

Optimize SQL query for canceled orders

Here is a subset of my tables:
orders:
- order_id
- customer_id
order_products:
- order_id
- order_product_id (unique key)
- canceled
I want to select all orders (order_id) for a given customer(customer_id), where ALL of the products in the order are canceled, not just some of the products. Is there a more elegantly or efficient way of doing it than this:
select order_id from orders
where order_id in (
select order_id from orders
inner join order_products on orders.order_id = order_products.order_id
where order_products.customer_id = 1234 and order_products.canceled = 1
)
and order_id not in (
select order_id from orders
inner join order_products on orders.order_id = order_products.order_id
where order_products.customer_id = 1234 and order_products.canceled = 0
)
If all orders have at least one row in order_products, Try this
Select order_id from orders o
Where Not Exists
(Select * From order_products
Where order_id = o.order_id
And cancelled = 1)
If the above assumption is not true, then you also need:
Select order_id from orders o
Where Exists
(Select * From order_products
Where order_id = o.order_id)
And Not Exists
(Select * From order_products
Where order_id = o.order_id
And cancelled = 1)
The fastest way will be this:
SELECT order_id
FROM orders o
WHERE customer_id = 1234
AND
(
SELECT canceled
FROM order_products op
WHERE op.order_id = o.order_id
ORDER BY
canceled DESC
LIMIT 1
) = 0
The subquery will return 0 if and only if there had been some products and they all had been canceled.
If there were no products at all, the subquery will return NULL; if there is at least one uncanceled product, the subquery will return 1.
Make sure you have an index on order_products (order_id, canceled)
Something like this? This assumes that every order has at least one product, otherwise this query will return also orders without any products.
select order_id
from orders o
where not exists (select 1 from order_products op
where canceled = 0
and op.order_id = o.order_id
)
and o.customer_id = 1234
SELECT customer_id, order_id, count(*) AS product_count, sum(canceled) AS canceled_count
FROM orders JOIN order_products
ON orders.order_id = order_products.order_id
WHERE customer_id = <<VALUE>>
GROUP BY customer_id, order_id
HAVING product_count = canceled_count
You can try something like this
select orders.order_id
from #orders orders inner join
#order_products order_products on orders.order_id = order_products.order_id
where order_products.customer_id = 1234
GROUP BY orders.order_id
HAVING SUM(order_products.canceled) = COUNT(order_products.canceled)
Since we don't know the database platform, here's an ANSI standard approach. Note that this assumes nothing about the schema (i.e. data type of the cancelled field, how the cancelled flag is set (i.e. 'YES',1,etc.)) and uses nothing specific to a given database platform (which would likely be a more efficient approach if you could give us the platform and version you are using):
select op1.order_id
from (
select op.order_id, cast( case when op.cancelled is not null then 1 else 0 end as tinyint) as is_cancelled
from #order_products op
) op1
group by op1.order_id
having count(*) = sum(op1.is_cancelled);