Oracle - Outer Join with join condition - sql

I have a scenario where I have two tables
ORDER
ORDER_NO
LOCATION
ITEM
QTY_RECEIVED
SHIPMENT
ORDER_NO
LOCATION
ITEM
QTY_RECEIVED
There are cases where ORDER table has a record but SHIPMENT table doesn't
I want all the rows from ORDER table where the qty is not equal to the qty in SHIPMENT table, and that will include the rows which are there in ORDER but not in shipment.
I tried doing by this:
SELECT
order_no, item, location, SUM(NVL(QTY_RECEIVED, 0))
FROM
ORDERS ol
GROUP BY
ORDER_NO, ITEM, LOCATION
HAVING
SUM (NVL(ol.QTY_RECEIVED,0)) <>
(SELECT SUM(NVL(sk.QTY_RECEIVED, 0))
FROM shipment s
WHERE s.order_no = ol.order_no
AND s.item (+)= ol.item
AND s.location (+) = ol.location
GROUP BY s.order_no, s.item, s.location);
But it doesn't give the correct result.
how should I do this?

You need LEFT JOIN in order to return the the results even for the non-existing values of SHIPMENT table :
SELECT ol.order_no, ol.item, ol.location,
SUM(NVL(ol.QTY_RECEIVED, 0)) AS "Total Quantity Of Orders",
SUM (NVL(s.QTY_RECEIVED,0)) AS "Total Quantity Of Shpm."
FROM orders ol
LEFT JOIN shipment s
ON s.order_no = ol.order_no
AND s.item = ol.item
AND s.location = ol.location
GROUP BY ol.order_no, ol.item, ol.location
HAVING SUM (NVL(ol.QTY_RECEIVED,0)) <> SUM (NVL(s.QTY_RECEIVED,0))

It seems possible to me that an order could have multiple shipments for the same item. If this is the case, you need a different approach:
select order_no, location, item, sum(o_qty), sum(s_qty)
from ((select order_no, location, item, qty_received as o_qty, 0 as s_qty
from orders
) union all
(select order_no, location, item, 0 as o_qty, qty_received as s_qty
from shipment
)
) os
group by order_no, location, item
having sum(o_qty) <> sum(s_qty);

Related

Use 1 SQL query to join 3 tables and find the category of products that generates the most revenue for each customer segment

I am using SQLite3 for this following query.
I have a table called "products" that looks like this:
I have a table called "transactions" that looks like this:
I have a table called "segments" that looks like this:
For each active segment, I want to find the category that produces the highest revenue.
I think that I know how to do this in 3 different queries.
create table table1 as
SELECT s.seg_name, p.category, t.item_qty * t.item_price as revenue
from segments s
JOIN
transactions t
on s.cust_id = t.cust_id
JOIN products p
on p.prod_id = t.prod_id
where s.active_flag = 'Y'
order by s.seg_name, p.category
;
create table table2 as
select seg_name, category, sum(revenue) as revenue
from table1
group by seg_name, category;
select seg_name, category, max(revenue) as revenue
from table2
group by seg_name;
How can I do it in 1 query?
here is one way :
select seg_name,category,revenue
from (
select
s.seg_name,
p.category,
sum(t.item_qty * t.item_price) as revenue,
rank() over (partition by seg_name order by sum(t.item_qty * t.item_price) desc) rn
from segments s
join transactions t on s.cust_id = t.cust_id
join products p on p.prod_id = t.prod_id
where s.active_flag = 'Y'
group by seg_name, p.category
) t where rn = 1

How to get Name of an item corresponding to Min and Max value in a grouped set into different columns

Table 1: ORDERS (ORDER_ID, CUSTOMER_ID, ITEM_ID, QUANTITY)
Table 2: CUSTOMERS (CUSTOMER_ID, CUSTOMER_NAME, ZIPCODE)
Table 3: ITEMS (ITEM_ID, ITEM_NAME)
Required Output: OUTPUT (ZIPCODE, MAX_ORDERED_ITEM_NAME, MIN_ORDERED_ITEM_NAME
I've tried various approaches to get the output, but haven't been able to get to the end result
Current query:
SELECT ZIPCODE, ITEM_NAME, SUM(QUANTITY) AS SALES
FROM ORDERS
JOIN CUSTOMERS ON ORDERS.CUSTOMER_ID = CUSTOMERS.CUSTOMER_ID
JOIN ITEMS ORDERS.ITEM_ID = ITEMS.ITEM_ID
GROUP BY ZIPCODE, ITEM_NAME
ORDER BY SALES DESC;
You can use analytical function ROW_NUMBER with the group by as following:
SELECT O.ZIPCODE,
MAX(CASE WHEN MAXRN = 1 THEN ITEM_NAME END) AS MAX_ORDERED_ITEM_NAME,
MAX(CASE WHEN MINRN = 1 THEN ITEM_NAME END) AS MIN_ORDERED_ITEM_NAME
FROM
(SELECT
O.ZIPCODE, I.ITEM_ID, I.ITEM_NAME,
ROW_NUMBER() OVER (PARTITION BY I.ITEM_ID ORDER BY SUM(QUANTITY) ) AS MINRN,
ROW_NUMBER() OVER (PARTITION BY I.ITEM_ID ORDER BY SUM(QUANTITY) DESC NULLS LAST) AS MAXRN
FROM
ORDERS O
JOIN ITEMS I ON I.ITEM_ID = O.ITEM_ID
JOIN CUSTOMERS C ON C.CUSTOMER_ID = O.CUSTOMER_ID
GROUP BY O.ZIPCODE, I.ITEM_ID, I.ITEM_NAME)
WHERE 1 IN (MINRN, MAXRN)
GROUP BY O.ZIPCODE;
Cheers!!

SQL export (with joins) change rows to columns - get specific row data for columns

I am working with the volusion cart software so I only have export access. I have the following tables (with a bit of sample data):
orders
orderid, shippingmethodid, orderstatus
12345, 218, Ready to Ship
12346, 152, Ready to Ship
12347, 602, Ready to Ship
12348, 10151, Processing
orderdetails
orderid, productcode, productname, qtyonpackingslip, qty
12345, proda-12, product a twelve, 1, 1
12346, prodb-14, product b fourteen, , 1
12346, prodc_15, product c fifteen, 0, 1
12347, prodd-21, product d twenty one, , 1
12347, prode-17, product e seventeen, , 1
12348, prodf-19, product f nineteen, , 1
I need to create an export query that will list the product codes as columns rather than rows. meaning that if there are multiple product codes on an order, there should be 1 row for that orderid, & however many columns as there are products in that order but only if the qtyonpackingslip is > 0 or null AND the quantity is > 0. Example of how the result needs to look:
There should be 10 item columns. I'm hesitant to post what I've tried, partially from embarrassment & partially because it's so wrong I may cause confusion about what I'm trying to do, but here's a few just so you all know I'm REALLY trying:
SELECT
o.orderid
, (SELECT productcode, row_number() over (order by productcode) as item1pc
FROM orderdetails od WHERE item1pcv= 1) As Item1code
, (SELECT productname, row_number() over (order by productname) as item1name
FROM orderdetails od WHERE item1name = 1) As Item1name
, (SELECT qtyonpackingslip row_number() over (order by qtyonpackingslip) as item1qty
FROM orderdetails od WHERE item1qty= 1) As Item1qty
, shippingmethodid
FROM orders o
WHERE EXISTS (SELECT productcode, productname, qtyonpackingslip
FROM orderdetails od
LEFT JOIN orders on o.orderid = od.orderid
WHERE od.qtyonpackingslip > 0)
WHERE o.orderstatus = 'Ready to Ship'
Not even close, Also tried this:
SELECT
o.orderid
, pd1.productcode As item1
, pd1.productname As item1name
, pd1.quantity As item1qty
, o.shippingmethodid
FROM orders o
LEFT JOIN (SELECT orderid, productcode, productname, qtyonpackingslip, quantity FROM
(SELECT ROW_NUMBER() OVER (ORDER BY orderid) AS Row
, orderid
, productcode
, productname
, quantity
FROM orderdetails WHERE qtyonpackingslip > 0
OR (qtyonpackingslip IS NULL AND quantity > 0)
) As pd1
ON o.orderid = pd1.orderid AND Row = 1
WHERE o.orderstatus = 'Ready to Ship'
Again, no dice. I've never had to get specific rows before, so I don't know what I'm doing! Any help would be greatly appreciated, thanks!
As we discussed, what you really want is not to PIVOT the data. Pivoting turns values from cells in your dataset to columns, and in your case, you still want those values as values. The difference is that you want values that are associated with the same order to be returned as a single row, appending columns to the result set for whatever number of products (and their details) the order has.
Here is the solution we came up with:
select o.orderid, o.orderdate, o.shippingmethodid,
p1.productcode as prod1code,
p1.productname as prod1desc,
p1.quantity as prod1qty,
p1.productprice as prod1price,
p2.productcode as prod2code,
p2.productname as prod2desc,
p2.quantity as prod2qty,
p2.productprice as prod2price,
p3.productcode as prod3code,
p3.productname as prod3desc,
p3.quantity as prod3qty,
p3.productprice as prod3price
from Orders o
outer apply (
select *
from (
select *, ROW_NUMBER() OVER (PARTITION BY od.orderid ORDER BY od.productname) as rn
from OrderDetails od
where o.orderid = od.orderid
) t
where rn = 1
) as p1
outer apply (
select *
from (
select *, ROW_NUMBER() OVER (PARTITION BY od.orderid ORDER BY od.productname) as rn
from OrderDetails od
where o.orderid = od.orderid
) t
where rn = 2
) as p2
outer apply (
select *
from (
select *, ROW_NUMBER() OVER (PARTITION BY od.orderid ORDER BY od.productname) as rn
from OrderDetails od
where o.orderid = od.orderid
) t
where rn = 3
) as p3
where o.orderstatus = 'Ready To Ship'
You need to use pivot table. See the static column's example. You can also make dynamic column pivot.
SELECT VendorID, [250] AS Emp1, [251] AS Emp2, [256] AS Emp3, [257] AS Emp4, [260] AS Emp5
FROM
(SELECT PurchaseOrderID, EmployeeID, VendorID
FROM Purchasing.PurchaseOrderHeader) p
PIVOT
(
COUNT (PurchaseOrderID)
FOR EmployeeID IN
( [250], [251], [256], [257], [260] )
) AS pvt
ORDER BY pvt.VendorID;

Magento: How to query (SQL) top selling products?

Im seeking for a SQL query to directly get a list of our top selling products (ordered by quantity or by amount - doesn´t really matter). This seems to be more difficult than I thought...
Searching with google only finds solutions for PHP-modules and so on - but I want SQL.
I have picked up a small query from a different site and modified it a bit:
SELECT
SUM(order_items.qty_ordered) AS ordered_qty, order_items.name AS order_items_name, order_items.product_id AS entity_id,
e.entity_type_id, e.attribute_set_id, e.type_id, e.sku, e.has_options, e.required_options, e.created_at,
e.updated_at FROM sales_flat_order_item AS order_items
INNER JOIN sales_flat_order AS `order` ON `order`.entity_id = order_items.order_id AND `order`.state <> 'canceled'
LEFT JOIN catalog_product_entity AS e
ON
-- (e.type_id NOT IN ('grouped', 'configurable', 'bundle'))
-- AND
e.entity_id = order_items.product_id AND e.entity_type_id = 4
--AND state = "complete"
WHERE (parent_item_id IS NULL)
GROUP BY order_items.product_id
HAVING (SUM(order_items.qty_ordered) > 0)
ORDER BY ORDERED_QTY DESC
It more or less seems to give a reasonable output - but the numbers differ from the admin-start screen (where only the top 5 are displayed).
Has anybody done smth. similar to that already?
Have a solution (query) now
select
year_ordered,
product_type,
sku,
name,
sum(qty_ordered) as qty,
sum(row_total) as total
from (
SELECT
YEAR(so.created_at) AS year_ordered,
-- order_id,
product_type, sku, name, qty_ordered, price, row_total
FROM `sales_flat_order` AS so
INNER JOIN `sales_flat_order_item` AS si ON si.order_id=so.entity_id
AND (so.state != "canceled" )
ORDER BY so.created_at desc
) stat
group by stat.year_ordered, stat.product_type, stat.sku, stat.name
order by year_ordered desc, total desc
I´m using a subquery to be able to control that counting the products is correct.
SELECT
YEAR(so.created_at) AS year_ordered,
-- order_id,
product_type, sku, name, qty_ordered, price, row_total
FROM `sales_flat_order` AS so
INNER JOIN `sales_flat_order_item` AS si ON si.order_id=so.entity_id
AND (so.state != "canceled" )
ORDER BY so.created_at desc
Result is a list of the ordered products per year. If you want to see a best seller list over all years a slight modification is sufficient (here ordered by total).
select
product_type,
sku,
name,
sum(qty_ordered) as qty,
sum(row_total) as total
from (
SELECT
YEAR(so.created_at) AS year_ordered,
-- order_id,
product_type, sku, name, qty_ordered, price, row_total
FROM `sales_flat_order` AS so
INNER JOIN `sales_flat_order_item` AS si ON si.order_id=so.entity_id
AND (so.state != "canceled" )
ORDER BY so.created_at desc
) stat
group by stat.product_type, stat.sku, stat.name
order by total desc

SQL Query for counting number of orders per customer and Total Dollar amount

I have two tables
Order with columns:
OrderID,OrderDate,CID,EmployeeID
And OrderItem with columns:
OrderID,ItemID,Quantity,SalePrice
I need to return the CustomerID(CID), number of orders per customer, and each customers total amount for all orders.
So far I have two separate queries. One gives me the count of customer orders....
SELECT CID, Count(Order.OrderID) AS TotalOrders
FROM [Order]
Where CID = CID
GROUP BY CID
Order BY Count(Order.OrderID) DESC;
And the other gives me the total sales. I'm having trouble combining them...
SELECT CID, Sum(OrderItem.Quantity*OrderItem.SalePrice) AS TotalDollarAmount
FROM OrderItem, [Order]
WHERE OrderItem.OrderID = [Order].OrderID
GROUP BY CID
I'm doing this in Access 2010.
You would use COUNT(DISTINCT ...) in other SQL engines:
SELECT CID,
Count(DISTINCT O.OrderID) AS TotalOrders,
Sum(OI.Quantity*OI.SalePrice) AS TotalDollarAmount
FROM [Order] O
INNER JOIN [OrderItem] OI
ON O.OrderID = OI.OrderID
GROUP BY CID
Order BY Count(DISTINCT O.OrderID) DESC
Which Access unfortunately does not support. Instead you can first get the Order dollar amounts and then join them before figuring the order counts:
SELECT CID,
COUNT(Orders.OrderID) AS TotalOrders,
SUM(OrderAmounts.DollarAmount) AS TotalDollarAmount
FROM [Orders]
INNER JOIN (SELECT OrderID, Sum(Quantity*SalePrice) AS DollarAmount
FROM OrderItems GROUP BY OrderID) AS OrderAmounts
ON Orders.OrderID = OrderAmounts.OrderID
GROUP BY CID
ORDER BY Count(Orders.OrderID) DESC
If you need to include Customers that have orders with no items (unusual but possible), change INNER JOIN to LEFT OUTER JOIN.
Create a query which uses your 2 existing queries as subqueriers, and join the 2 subqueries on CID. Define your ORDER BY in the parent query instead of in a subquery.
SELECT
sub1.CID,
sub1.TotalOrders,
sub2.TotalDollarAmount
FROM
(
SELECT
CID,
Count(Order.OrderID) AS TotalOrders
FROM [Order]
GROUP BY CID
) AS sub1
INNER JOIN
(
SELECT
CID,
Sum(OrderItem.Quantity*OrderItem.SalePrice)
AS TotalDollarAmount
FROM OrderItem INNER JOIN [Order]
ON OrderItem.OrderID = [Order].OrderID
GROUP BY CID
) AS sub2
ON sub1.CID = sub2.CID
ORDER BY sub1.TotalOrders DESC;