SQL One result per match - sql

I've created a query that gives prices for every product ordered by creation date.
What I now need is to only return one row per product, i.e. one price per product.
SELECT p.product_id, p.price, p.creation_date
FROM PRICE p
INNER JOIN PRODUCT pr
ON p.product_id = pr.product_id
AND p.filter_id = 3
AND (p.store_id IN (1,2,3,4) OR p.price_type = 'A')
ORDER BY p.creation_date DESC
Currently this query returns several prices per product, since several match, but I only want the newest one of those. The ORDER BY p.creation_date DESC gives the price I want as the first in the list, I want that first entry to be the only result for that product.
I've tried rownum = 1, but that only gives one result for the whole query.
Please note that this is an Oracle Database, so as far as I know TOP does not work, same goes for LIMIT.
I've treid googling it, but I can't find examples showing exactly this issue.
I forgot to mention: There are some prices which have the same creation date for the same product, so there has to be a limit of only giving the first entry for those too

Use the row_numer() windowing function to get only the latest row:
select * from (
SELECT
p.product_id,
p.price,
row_number() over (partition by product_id order by p.creation_date desc) rn,
p.creation_date
FROM PRICE p
INNER JOIN PRODUCT pr
ON p.product_id = pr.product_id
AND p.filter_id = 3
AND (p.store_id IN (1,2,3,4) OR p.price_type = 'A')
) where rn = 1

you can try this to get the price list for products
SELECT p.product_id, p.price ,first_value(p.price) over (partition by product_id
order by p.creation_date desc) new_price, p.creation_date
FROM PRICE p
INNER JOIN PRODUCT pr
ON p.product_id = pr.product_id
AND p.filter_id = 3
AND (p.store_id IN (1,2,3,4) OR p.price_type = 'A')

I don't see why you need the join on product, as... you don't use any row of product in your returned rows...
You might try :
SELECT p.product_id, MAX(p.price), p.creation_date
FROM PRICE p
INNER JOIN
(SELECT pm.product_id, MAX(pm.creation_date)
FROM PRICE pm
GROUP BY pm.product_id) most_recent_price
ON most_recent_price.product_id = p.product_id
WHERE p.filter_id = 3
AND (p.store_id IN (1,2,3,4) OR p.price_type = 'A')
GROUP BY p.product_id, p.creation_date

Related

SQL NESTED JOIN NOT USING WHERE

What is the current price of each product? Display product code, product description, unit, and its current price. Always assume that NOT ALL products HAVE unit price BUT you need to display it even if it has no unit price on it. without using WHERE script
Product Table
prodCode
description
unit
PriceHist Table
prodCode
effDate
unitPrice
This is my work... Please Help me to improve my anwswer.
SELECT p.prodCode, p.description, p.unit, MAX(ph.unitPrice) "Current Price"
FROM product p
INNER JOIN priceHist AS ph
ON p.prodCode = ph.prodCode
GROUP BY p.prodCode, p.description, p.unit
ORDER BY MAX(ph.unitPrice);
Someone said I NEED TO USE PRICEHIST TWICE
User window functions and left join:
select . . . -- whatever columns you want
from products p left join
(select ph.*,
row_number() over (partition by p.prodcode order by effdate desc) as seqnum
from pricehist ph
) ph
on ph.prodcode = p.prodcode and ph.seqnum = 1;
Try this:
SELECT a.prodCode, a.description, a.unit, SUM(b.unitPrice)
FROM ProductTable a
LEFT JOIN PriceHistTable b ON a.prodCode = b.prodCode
GROUP BY a.prodCode, a.description, a.unit, b.unitPrice
Assuming you want to select unitPrice for prodCode with latest effDate, below query should work. Explanation in comments.
select x.prodCode, x.description, x.unit, y.unitPrice
from Product x
left join (
--- Create a table y with one row per (prodCode, effDate)
--- choosing the latest effDate per prodCode
select a.prodCode, a.effDate, a.unitPrice
from PriceHist a
join (
--- Create b as (prodCode, effDate)
select prodCode, max(effDate) as maxEffDate
from PriceHist
group by prodCode
) b
on a.prodCode = b.prodCode and a.effDate = b.maxEffDate
) y
on x.prodCode = y.prodCode

Get max value from another query

I have problems with some query. I need to get max value and product_name from that query:
select
products.product_name,
sum(product_invoice.product_amount) as total_amount
from
product_invoice
inner join
products on product_invoice.product_id = products.product_id
inner join
invoices on product_invoice.invoice_id = invoices.invoice_id
where
month(invoices.invoice_date) = 2
group by
products.product_name
This query returns a result like this:
product_name | total_amount
--------------+--------------
chairs | 70
ladders | 500
tables | 150
How to get from this: ladders 500?
Select product_name,max(total_amount) from(
select
products.product_name,
sum(product_invoice.product_amount) as total_amount
from product_invoice
inner join products
on product_invoice.product_id = products.product_id
inner join invoices
on product_invoice.invoice_id = invoices.invoice_id
where month(invoices.invoice_date) = 2
group by products.product_name
) outputTable
You can use order by and fetch first 1 row only:
select p.product_name,
sum(pi.product_amount) as total_amount
from product_invoice pi inner join
products p
on pi.product_id = p.product_id inner join
invoices i
on pi.invoice_id = i.invoice_id
where month(i.invoice_date) = 2 -- shouldn't you have the year here too?
group by p.product_name
order by total_amount
fetch first 1 row only;
Not all databases support the ANSI-standard fetch first clause. You may need to use limit, select top, or some other construct.
Note that I have also introduced table aliases -- they make the query easier to write and to read. Also, if you are selecting the month, shouldn't you also be selecting the year?
In older versions of SQL Server, you would use select top 1:
select top (1) p.product_name,
sum(pi.product_amount) as total_amount
from product_invoice pi inner join
products p
on pi.product_id = p.product_id inner join
invoices i
on pi.invoice_id = i.invoice_id
where month(i.invoice_date) = 2 -- shouldn't you have the year here too?
group by p.product_name
order by total_amount;
To get all rows with the top amount, use SELECT TOP (1) WITH TIES . . ..
If you are using SQL Server, then TOP can offer a solution:
SELECT TOP 1
p.product_name,
SUM(pi.product_amount) AS total_amount
FROM product_invoice pi
INNER JOIN products p
ON pi.product_id = p.product_id
INNER JOIN invoices i
ON pi.invoice_id = i.invoice_id
WHERE
MONTH(i.invoice_date) = 2
GROUP BY
p.product_name
ORDER BY
SUM(pi.product_amount) DESC;
Note: If there could be more than one product tied for the top amount, and you want all ties, then use TOP 1 WITH TIES, e.g.
SELECT TOP 1 WITH TIES
... (the same query I have above)

Sql Table Left outer join result Group By

Product1
Purchase quantity from 3 invoices are 50+100+50 = 200 and
Sale quantity from 1 invoice is 10
I am using below code for getting the result as
Total Purchase - Total Sale = Closing Qty
200 - 10 = 290
but I am getting the wrong result as shown in the attached image:
Please guide me to correct my code
SELECT
P.PRODUCT as PRODUCTNAME,
P.QUANTITY AS PURCHASE,
ISNULL(S.QUANTITY, 0) AS SALE,
ISNULL(P.QUANTITY, 0) - ISNULL(s.QUANTITY, 0) AS CLOSINGQTY
FROM
[PurchaseData] P
LEFT OUTER JOIN
[DeliveryData1] S ON P.Product = s.PRODUCT
When working with aggregates from different tables, don't join the tables and aggregate then, but aggregate first and join the aggregates:
select
p.product as productname,
p.sum_quantity as purchase,
coalesce(s.sum_quantity, 0) as sale,
p.sum_quantity - coalesce(s.sum_quantity, 0) as closingqty
from
(
select product, sum(quantity) as sum_quantity
from purchasedata
group by product
) p
left join
(
select product, sum(quantity) as sum_quantity
from deliverydata1
group by product
) s on s.product = p.product;
I've replaced ifnull with coalesce, so the query is fully standard SQL compliant.
If you want the final result you should use sum and group by
SELECT
P.PRODUCT as PRODUCTNAME,
sum(P.QUANTITY AS PURCHASE),
sum(isnull(S.QUANTITY,0)) AS SALE,
sum(isnull(P.QUANTITY,0))-sum(isnull(s.QUANTITY,0)) AS CLOSINGQTY
FROM [PurchaseData] P LEFT OUTER JOIN [DeliveryData1] S ON P.Product = s.PRODUCT
GROUP BY P.PRODUCT
If I understand correctly, you have a problem because you have multiple purchase records for a given product. If that is the case, then just aggregate before the JOIN:
SELECT p.*, COALESCE(S.QUANTITY, 0) AS SALE,
COALESCE(P.QUANTITY, 0)-COALESCE(s.QUANTITY, 0) AS CLOSINGQTY
FROM (SELECT p.product, p.quantity
FROM PurchaseData P
GROUP BY p.product
) p LEFT OUTER JOIN
DeliveryData1 S
ON P.product = s.producct;
Actually, I'm unclear which of the two tables has the duplicates -- or even if both do. So, you might need to do something similar for S.

Select Top1 from multiple-column query for each product ID

I have a query in SQLServer that returns the last entry in our stock of a given product, as well as many other columns. Something like:
SELECT
TOP(1) EntryDate,
EntryPrice,
TaxID,
TransportCost,
...
FROM
StockEntries
WHERE
ProductID = #ID
ORDER BY
EntryDate DESC
I cannot use MAX to get the last entry because sometimes it returns duplicate rows (when there are two entries at the same day).
I would like to execute this query for every product we have. I could do this if the query returned only 1 row, such as:
SELECT
ProductID p,
(
SELECT
TOP(1) s.EntryDate
FROM
StockEntries s
WHERE
s.ProductID = p.ProductID
ORDER BY
s.EntryDate DESC
)
FROM
Products p
But as it returns multiple rows, I cannot see a straight way to do this.
Any ideas?
As you have phrased the question, cross apply seems very appropriate:
SELECT p.*, s.*
FROM products p CROSS APPLY
(SELECT TOP(1) s.*
FROM StockEntries s
WHERE s.ProductID = p.ProductID
ORDER BY s.EntryDate DESC
) s;
APPLY also allows you to select other columns from StockEntries.
you can use ROW_NUMBER() to rank each row and then just get the rows with the highest entry date per product.
SELECT *
FROM (SELECT p.productid,
s.EntryDate,
s.EntryPrice,
s.TaxID,
s.TransportCost,
ROW_NUMBER() OVER (PARTITION BY p.productid ORDER BY s.entrydate DESC) rownum
FROM products p
JOIN StockEntries s ON s.ProductID = p.ProductID
) t
WHERE rownum = 1

Could not get the expected query with SQL Server

I´m trying to do a SQL query but I couldn´t get the expected result. I really don´t know what is going wrong.
I have a table Product which contains (product_id, title) and other table Product_Variation which contains (product_variation_id, product_id, description, gender, price)
Basically, I have a product. For each product, have N variations.
e.g
Product: title "I have no idea"
Product_Variation: description "T-Shirt", gender "Male", price "59.90"
What I need is select Product and Product_Variation showing only the product with the lowest price.
I don´t care if a product has t-shirt, jacket or anything else as variation. I just need to get the variation which has the lowest price.
My query is:
SELECT b.product_id, b.title, MIN(b.price) as price, b.gender
FROM (
SELECT p.product_id, p.title, MIN(pv.price) AS price, pv.gender
FROM products p
join product_variation pv ON pv.product_id = p.product_id
GROUP BY p.product_id, p.title, pv.price, pv.gender
) b
GROUP BY b.product_id, b.title, b.price, b.gender
Pls, see my example in SQL Fiddle
Thanks!
Since you're using SQL 2008 you can use ROW_NUMBER to find the row with the lowest price:
SELECT *
FROM products p
INNER JOIN
(SELECT
product_id,
Description,
Gender,
Price,
ROW_NUMBER() OVER(PARTITION BY product_id ORDER BY price) Row
FROM product_variation )
pv ON pv.product_id = p.product_id
AND Row = 1
If you have two variations with the same price you'll get one random row.
-- Solution #1
SELECT *
FROM
(
SELECT p.product_id, p.title,
pv.price, pv.gender,
ROW_NUMBER() OVER(PARTITION BY p.product_id ORDER BY pv.price) AS row_num
FROM products p
join product_variation pv ON pv.product_id = p.product_id
) src
WHERE src.row_num = 1
-- Solution #2
SELECT p.product_id, p.title,
x.price, x.gender
FROM products p
cross apply
(
SELECT TOP(1) pv.price, pv.gender
FROM product_variation pv
WHERE pv.product_id = p.product_id
ORDER BY pv.price
) x
You can use the rank() window function to order stuff within a partition (group), but giving equal items the same rank:
;With b as (
Select
p.product_id,
p.title,
pv.price,
pv.gender,
rank() over(partition by p.product_id order by pv.price) rk
From
products p
Inner Join
product_variation pv
On pv.product_id = p.product_id
)
Select
b.product_id,
b.title,
b.price,
b.gender
From
b
Where
rk = 1
If you only want one per product even if there are equal products, use row_number() instead of rank()
Example Fiddle