Join with alternating possibilities - sql

There is a Select statement
Select i.ItemID, s.Price, s.Qty, s.Company From Item i
Inner Join Sku s ON s.ItemID = i.ItemID
Which returns this:
ItemID | Price | Qty | Company
1 | $50.00 | 0 | Abc inc.
1 | $45.00 | 5 | Def inc.
1 | $35.00 | 15 | Xyz inc.
2 | $36.00 | 4 | Abc inc.
2 | $45.00 | 5 | Def inc.
2 | $35.00 | 1 | Xyz inc.
3 | $20.00 | 2 | Abc inc.
3 | $45.00 | 0 | Def inc.
3 | $35.00 | 5 | Xyz inc.
But there needs to be the following logic:
Show the row with the lowest Price and Qty > 0, including the referring Company to that Result.
Else...
Show the row with the lowest price, including the referring Company to that Price.
Which would look something like this:
ItemID | Price | Qty | Company
1 | $35.00 | 15 | Xyz inc.
2 | $35.00 | 1 | Xyz inc.
3 | $20.00 | 2 | Abc inc.
I haven't tried anything because I honestly don't know what to try or what to even ask in my question. Any suggestions?

Here is one way to do it:
SELECT I.ItemId
, S.Price
, S.Qty
, S.Company
FROM dbo.Item I
CROSS APPLY (
SELECT MIN(Price) Price
FROM dbo.Sku MP
WHERE I.ItemId = MP.ItemId
AND Qty > 0
) MP
CROSS APPLY (
SELECT
TOP 1 Price
, Qty
, Company
FROM dbo.Sku S
WHERE S.ItemId = I.ItemId
AND S.Price = MP.Price
) S

How about something like this, using row number:
select
i.ItemId, s.Price, s.Qty, s.Company
from
item i
inner join
(
select
t.ItemId,
t.Price,
t.Qty,
t.Company,
RowNumber = row_number() over (PARTITION BY t.ItemId order by t.price asc, t.qty desc)
from
Sku t
) s on s.itemid = i.ItemId
where
s.RowNumber = 1
The row numbers "partition by" and "order by" are important here to ensure the correct results and this works even if all quantities for an item are 0.

Here is the query,
Select i.ItemID, s.Price, s.Qty, s.Company
From Item i
Inner Join Sku s
ON s.ItemID = i.ItemID
Inner Join (
select min(price) as Price, ItemID
from Sku
where Qty > 0
group by ItemId
)a
on a.ItemId = s.ItemId
and a.Price = s.Price
If you need only ItemId from Item table, you can remove the join with Item table itself as ItemId is present in Sku.

Select i.ItemID, min(s.Price) as price, s.Qty, s.Company From Item i
Inner Join Sku s ON s.ItemID = i.ItemID and s.qty>0
group by i.itemid, s.company, s.qty

Select i.ItemID, Min(s.Price) as Price, s.Qty, s.Company
From Item i Inner Join Sku s ON s.ItemID = i.ItemID
Where s.qty > 0
Group by i.itemid, s.qty, s.Company

Basically, you want the first sku record, ordered first by whether or not it has any stock (items with stock appearing first), and then by price, ascending. In the following query the common table expression returns the required SKU fields, along with the RowNum calculated field. RowNum is partitioned by ItemID (i.e. resets to 1 for each new ItemID) and ordered by the presence of stock first, and then by price. The main query then selects the first record from each ItemID:
WITH cte AS (
SELECT ItemID, Price, Qty, company,
ROW_NUMBER() OVER (PARTITION BY ItemId ORDER BY
CASE WHEN Qty > 0 THEN 1 ELSE 0 END DESC, Price ASC) AS RowNum
FROM #sku
)
SELECT ItemID, price, qty, Company FROM cte WHERE RowNum = 1

Related

SQL selecting the maximum value of multiple items with all the columns

The data:
My query:
SELECT
itemcode, whsecode, MAX(quantity)
FROM
inventoryTable
GROUP BY
itemcode;
This returns this error:
Column 'inventoryTable.whsecode' is invalid in the select list because
it is not contained in either an aggregate function or the GROUP BY
clause.
When I put the whsecode in the GROUP BY clause, it just returns all the data in the table.
The output that I want is to return the whsecode with the highest quantity of the item in it.
The output that it supposed to have is:
whsecode|itemcode|quantity
WHSE2 | SS585 | 50
WHSE2 | SS586 | 50
WHSE1 | SS757 | 30
Eventually I will put that query inside this another query:
SELECT
A.mrno, A.remarks,
B.itemcode, B.description, B.uom, B.quantity,
C.whsecode, C.whseqty, D.rate
FROM
Mrhdr A
INNER JOIN
Mrdtls B ON A.mrno = B.mrno
INNER JOIN
(
SELECT itemcode, whsecode, MAX(quantity) AS whseqty
FROM inventoryTable
GROUP BY itemcode, whsecode
) C ON B.itemcode = C.itemcode
INNER JOIN
Items D ON B.itemcode = D.itemcode
WHERE
A.mrno = #MRNo AND B.quantity < C.whseqty;
with the whsecode inside the GROUP BY clause the output is:
But as I said earlier, the problem is it returns multiple rows of the same itemcode. The output that it supposed to have is:
mrno | remarks| itemcode| description | uom |quantity|whsecode|whseqty| rate
MR211100003008 | SAMPLE | FG 4751 | LONG DRILL 3.4 X 200 L550 | PCS. | 50.00 | WHSE3 | 100 | 0.0000
MR211100003008 | SAMPLE | FG 5092 | T-SPIRAL TAP M3.0 X 0.5 L6904 | PCS | 20.00 | WHSE1 | 80 | 0.0000
I am not sure if the B.quantity < C.whseqty should be there but it eliminates the other values that are not the maximum value.
There are many ways to solve this. For example, by using the ROW_NUMBER function:
SELECT
itemcode,
whsecode,
quantity As whseqty
FROM
(
SELECT
itemcode,
whsecode,
quantity,
ROW_NUMBER() OVER (PARTITION BY itemcode ORDER BY quantity DESC) As RN
FROM
inventoryTable
)
WHERE
RN = 1
;
Edit:
select whsecode,A.itemcode,qty from inventoryTable
join (SELECT itemcode, MAX(quantity) as qty FROM inventoryTable GROUP BY itemcode) as A on A.itemcode = inventoryTable.itemcode and A.qty = inventoryTable.quantity

sum result is being multiplied by number of rows

I'm trying to get 2 sums from my tables, one is a table of ordered items and another is a table of received items. With my current query i'm getting a sum of 2014 which is from 106(the actual sum I want to get) multiplied by 19(the number of records)
select POD.PO_No, SUM(POD.Qty) as Qty, RRD.RR_No, SUM(RRD.QtyRcvd) as QtyReceived
from tbl_PODetail POD inner join tbl_RRDetail RRD on POD.PO_No = RRD.PO_Reference
where POD.PO_No = 'PO-000001'
group by POD.PO_No, RRD.RR_No
What causes this and how can I correct it?
Sample Table
PO No | Item | Qty
-------------------------
PO-0000001 | Item A | 5
PO-0000001 | Item B | 7
PO-0000001 | Item B | 3
RR No | Item | Qty | PO_Reference
----------------------------------------
RR-0000001 | Item A | 5 | PO-0000001
RR-0000001 | Item B | 7 | PO-0000001
RR-0000001 | Item B | 3 | PO-0000001
Expected Output:
PO No | Qty | RR No | Qty Rcvd
-----------------------------------------
PO-0000001 | 15 | RR-0000001 | 15
What I get
PO No | Qty | RR No | Qty Rcvd
-----------------------------------------
PO-0000001 | 45 | RR-0000001 | 45
You missed the POD.Item = RRD.Item on join, that's why you are getting multiplied records.
select POD.PO_No, SUM(POD.Qty) as Qty, RRD.RR_No, SUM(RRD.QtyRcvd) as QtyReceived
from tbl_PODetail POD
inner join tbl_RRDetail RRD on POD.PO_No = RRD.PO_Reference and POD.Item = RRD.Item
where POD.PO_No = 'PO-000001'
group by POD.PO_No, RRD.RR_No
Try aggregating the values in subqueries and joining them afterwards
Select *
From (
Select po_no, sum(qty) qty
From tbl_PODetail
Group by po_no
) p join (
Select rr_no, po_reference, sum(qty) qty_rcvd
From tbl_RRDetail
Group by rr_no, po_reference
) r on p.po_no = r.po_reference
Where p.po_no = 'PO-0000001'
Try this also. Hope this could help.
select * into #table1 from (
select 'PO-0000001' as PO_No,'Item A' as Item,5 as QTY union
select 'PO-0000001' as PO_No,'Item B' as Item,7 as QTY union
select 'PO-0000001' as PO_No,'Item C' as Item,3 as QTY
)t
select * into #table2 from (
select 'RR-0000001' as RR_No,'Item A' as Item,5 as QTY,'PO-0000001' as PO_Referrence union
select 'RR-0000001' as RR_No,'Item B' as Item,7 as QTY,'PO-0000001' as PO_Referrence union
select 'RR-0000001' as RR_No,'Item C' as Item,3 as QTY,'PO-0000001' as PO_Referrence
)t
select t1.PO_No,sum(t2.qty) as 'Qty' ,t2.RR_no,sum(t1.qty) as 'QTY Rcvd'
from #table1 t1
inner join #table2 t2
on t1.PO_no=t2.PO_Referrence and t1.item=t2.item
group by t1.PO_No,t2.RR_no
This answers reflects what my understanding of your problem is. It appears that you want to aggregate the tbl_PODetail by PO number and compute a sum as quantity. Then, you want to aggregate a second table tbl_RRDetail by two columns, and join the first aggregated result to that using only the PO number. If this be correct, then one approach would be to use two separate subqueries for the aggregation and join them together.
WITH cte1 AS (
SELECT RR_No, PO_Reference, SUM(QtyRcvd) AS QtyReceived
FROM tbl_RRDetail
GROUP BY RR_No, PO_Reference
),
WITH cte2 AS (
SELECT PO_No, SUM(Qty) AS Qty
FROM tbl_PODetail
GROUP BY PO_No
)
SELECT t2.PO_No, t2.Qty, t1.RR_No, t1.QtyReceived
FROM cte1 t1
INNER JOIN cte2 t2
ON t1.PO_Reference = t2.PO_No
WHERE t1.PO_No = 'PO-000001'

Filtering Duplicates from UNION results based on 2 columns

I am getting the UNION of two select queries, one of the tables being queried is a temp table to ensure that records are outputted (so the temp table has the expected output for each date but with a default value of 0 for the Sale column
An example output result would be something like this:
ProductID | Product Desc | TransactionDate | Sale
1011021 | SD DOG | 2017-01-07 | 0
1011021 | SD DOG | 2017-01-07 | $17
1011021 | SD DOG | 2017-01-14 | 0
1011021 | SD DOG | 2017-01-14 | $15
1011021 | SD DOG | 2017-01-21 | $21
1011021 | SD DOG | 2017-01-28 | 0
1011021 | SD DOG | 2017-01-28 | $21
Union removes duplicates based on rows, how can I make it remove duplicates based on the productID and transactionDate column, removing the duplicates where Sale is 0?
--Query below
SELECT transactionProducts.productID, products.productDesc, sum(salePrice) as Sale, transactionDate
FROM products LEFT JOIN transactionProducts on products.productID = transactionProducts.productID
LEFT JOIN transactions ON transactionProducts.transactionID = transactions.transactionID
LEFT JOIN productCategory on productCategory.productID = products.productID
LEFT JOIN categories on categories.categoryID = productCategory.categoryID
WHERE (transactionProducts.productID='123' AND transactions.transactionDate='2017-01-12'
Group by transactionProducts.productID, transactionDate
UNION select * from "temptable" group by productID, productDesc, Sale, transactionDate
ORDER BY transactionDate
Use not exists to select the rows from the temporary table that are not already in the result set:
. . .
UNION ALL
SELECT tt.*
FROM temptable tt
WHERE NOT EXISTS (SELECT 1
FROM transactionproducts tp JOIN
transaction t
ON t.transactionID = tp.transactionID
WHERE tp.productId = tt.productId AND
t.transactionDate = tt.transactionDate
)
GROUP BY productID, productDesc, Sale, transactionDate
ORDER BY transactionDate
How about this:
SELECT transactionProducts.productID, products.productDesc, sum(salePrice) as Sale, transactionDate
FROM products LEFT JOIN transactionProducts on products.productID = transactionProducts.productID
LEFT JOIN transactions ON transactionProducts.transactionID = transactions.transactionID
LEFT JOIN productCategory on productCategory.productID = products.productID
LEFT JOIN categories on categories.categoryID = productCategory.categoryID
WHERE (transactionProducts.productID='123' AND transactions.transactionDate='2017-01-12'
AND sum(salePrice) > 0 )
Group by transactionProducts.productID, transactionDate
UNION select * from "temptable" group by productID, productDesc, Sale, transactionDate
ORDER BY transactionDate
Note, I've added " AND sum(salePrice) > 0 " to the first SELECT WHERE clause.
HTH

How to calculate max(column) row from a result set grouped by different items

SELECT T1.* FROM
(
SELECT
MAX(no_of_orders) [no_of_orders],
ord.customer_id [customer_id],
ord_dtl.item_id [item_id]
FROM order_main ord
INNER JOIN order_detail ord_dtl
ON ord.order_id = ord_dtl.order_id
GROUP BY
ord.customer_id, ord_dtl.item_id
) T1
INNER JOIN
(
SELECT
MAX(no_of_orders) [no_of_orders],
ord.customer_id
FROM order_main ord
INNER JOIN order_detail ord_dtl
ON ord.order_id = ord_dtl.order_id
GROUP BY
ord.customer_id
) T2
ON
T1.customer_id = T2.customer_id
AND T1.no_of_orders = T2.no_of_orders
I calculated the most ordered item by customer from table order_main and order_detail.
What I did is like I calculated the no of orders grouping by customer id and item id and to reduce the result set to most ordered item by customer, I joined the resultset by same query removing the grouping by item id.
I don't feel this is a good query.
Any better alternative ?
SQL Fiddle
Most ordered item by customer
Query:
SQLFIDDLEExample
SELECT a.no_of_orders,
a.customer_id,
a.item_id
FROM (SELECT
MAX(no_of_orders) [no_of_orders],
ord.customer_id [customer_id],
ord_dtl.item_id [item_id],
ROW_NUMBER() OVER(PARTITION BY ord.customer_id ORDER BY MAX(no_of_orders) DESC) AS rnk
FROM order_main ord
INNER JOIN order_detail ord_dtl
ON ord.order_id = ord_dtl.order_id
GROUP BY
ord.customer_id, ord_dtl.item_id) a
WHERE a.rnk = 1
Result:
| NO_OF_ORDERS | CUSTOMER_ID | ITEM_ID |
---------------|-------------|---------|
| 20 | 2 | 6 |
| 50 | 7 | 6 |
| 1500 | 8 | 7 |
| 10 | 9 | 3 |

select from two tables and conditionally collapse one column

I have a list of Products
upc | name | price | qty
----------------------------
1 | apple | 1.00 | 3
2 | peach | 2.00 | 7
3 | melon | 1.75 | 2
and SaleProducts
upc | price
------------
2 | 1.90
I want to select from Products but also sale price
from SaleProducts (if product is on sale). This is what I came up with:
SELECT t1.upc, t1.name, MIN(t1.price) AS 'price', t1.qty
FROM (
SELECT p.upc, p.name, p.price, p.qty
FROM products p
UNION
SELECT sp.upc, NULL, sp.price, NULL
FROM saleproducts sp
) t1
GROUP BY t1.upc;
Output:
upc | name | price | qty
----------------------------
1 | apple | 1.00 | 3
2 | peach | 1.90 | 7
3 | melon | 1.75 | 2
Can anyone suggest a more elegant way to accomplish this? Im aware of similar question but my goal is to grab whichever price is lower, so COALESCE wouldn't work.
The only restriction is, I must use vanilla SQL, no stored procs or IF's.
Try this instead using CASE:
SELECT p.upc, p.name,
CASE WHEN sp.price IS NOT NULL
THEN CASE WHEN p.price > sp.price
THEN sp.price
ELSE p.price
END
ELSE p.price
END price, p.qty
FROM products p LEFT JOIN saleproducts sp ON p.upc = sp.upc;
It will prefer to use the price from saleproducts when it's available. If there is not a sale price for that product it will instead use the price from products.
EDIT - I've updated my answer so that it always gets the lowest price. FWIW I can't imagine why you'd bother having a sale price which is actually higher than your list price.
This seems more like the job for a left outer join:
SELECT p.upc, p.name,
(case when sp.price is null or p.price < sp.upc then p.price
else sp.price
end) as price,
p.qty
FROM products p left outer join
salesproducts sp
on p.upc = sp.upc;