SQL - Selecting rows based on priority level - sql

Given the following two tables:
PRODUCT_CATEGORIES
PRODUCT_CATEGORY PRIOTITY
BOOK 100
VIDEO 200
CD 300
PRODUCT_TYPES
PRODUCT_TYPE_ID PRODUCTID PRODUCT_CATEGORY
1 1001 VIDEO
2 1001 CD
3 1002 BOOK
4 1002 VIDEO
5 1003 BOOK
How can i join the two tables so that i select all the rows from PRODUCT_TYPES and where there are duplicate products i only select the one with the highest priority.
For example, for PRODUCTID 1002, i only want the row with a category of VIDEO because VIDEO has a higher priority than BOOK (which is the category of the other 1002 product in the table).
Here is what i have tried but i only seem to get duplicates.
select product_type_id, productid, product_category
from product_types pt
join product_categories pc on (pc.product_category=pt.product_category)
One other way of doing it is to order by product_category in descending order and select the first row where rownum=1 but i suspect that is not the best solution.

WITH recordList
AS
(
SELECT a.Product_TypeID,
a.ProductID,
a.Product_Category,
b.Priority,
ROW_NUMBER() OVER (PARTITION BY a.ProductID
ORDER BY b.Priority) RN
FROM Product_Types a
INNER JOIN Product_Categories b
ON a.Product_Category = b.Product_Category
)
SELECT Product_TypeID,
ProductID,
Product_Category,
Priority
FROM recordList
WHERE RN = 1
SQLFiddle Demo

Try the following query:
SELECT PT.*
FROM PRODUCT_CATEGORIES PC INNER JOIN PRODUCT_TYPES PT
ON PC.PRODUCT_CATEGORY = PT.PRODUCT_CATEGORY
WHERE PC.PRIOTITY = (
SELECT MAX(PRIOTITY) FROM PRODUCT_CATEGORIES IPC
, PRODUCT_TYPES IPT
WHERE IPC.PRODUCT_CATEGORY = IPT.PRODUCT_CATEGORY
AND IPT.PRODUCTID = PT.PRODUCTID)

select
product_type_id,
productid,
product_category
from
PRODUCT_TYPES
where
product_type_id in (
select
max(product_type_id) keep (dense_rank first order by priotity desc)
from
PRODUCT_TYPES
join PRODUCT_CATEGORIES using(product_category)
group by
productid
)
SqlFiddle

Related

Select same column from multiple tables with condition and LEFT JOIN

I have two tables with fiew similar columns :
One is for SellOrders
OrderID (PK)| id_seller| id_product| placement_date
And the other is for BuyOrders
OrderID (PK)| id_buyer| id_product| placement_date
With another table Customer concerning customers informations (Buyers and sellers)
id_customer(PK) | name_customer
And a Product table :
id(PK) | name_product
I want to write an SQL statement that will select the first placement_date when the a buy order or sell order was placed for the product and the correspending name_customer + the name_product.
I wrote a query that select the same logic but for only the selling side and I am looking to select the data for both buy and sell side :
SELECT p.name_product, s.placement_date, c.name_customer
FROM Product p
OUTER APPLY (SELECT TOP 1 placement_date, id_seller
FROM Selling
WHERE id_product = p.id
ORDER BY placement_date, OrderID ASC) s
LEFT JOIN Customer c
ON c.id_customer = s.id_seller
From what I have seen UNION SELECT seems to be the way to do this. I added UNION to the OUTER APPLY:
OUTER APPLY (SELECT TOP 1 placement_date, id_seller
FROM Selling
UNION
SELECT TOP 1 placement_date, id_buyer
WHERE id_product = p.id
ORDER BY placement_date, OrderID ASC) s
But I get stuck at the LEFT JOIN with the table Customer.
Any help ?
If I've got it right you want first of union of buy/sell orders
SELECT p.name_product, s.placement_date, c.name_customer
FROM Product p
OUTER APPLY (
SELECT TOP 1 placement_date, id_cust
FROM (
SELECT placement_date, OrderID, id_seller id_cust
FROM SellOrders
WHERE id_product = p.id
UNION
SELECT placement_date, OrderID, id_buyer
FROM BuyOrders
WHERE id_product = p.id
) t
ORDER BY placement_date, OrderID ASC
) s
LEFT JOIN Customer c
ON c.id_customer = s.id_cust

select single row of multiple records in One to Many relation tables

In MSSQL, I have two tables with one to many relation.
Tables:
tb_Products:
ID
ProductName
tb_Images:
ProductID
MidPath
In Stored Procedure, shows products with their images (also shows null records),
And if there is multiple productIDs in tb_Images, it shows a single record.
I can make this work by this Query:
declare #rowIndexToFetch int
set #rowIndexToFetch=0
select p.ID,p.ProductName,p.CategoryID,c.ParentId,
pi.MidPath
from tb_Products p
join tb_Categories c
on p.CategoryID=c.ID
left outer join tb_Images pi
on pi.ProductID=p.ID
where c.ID=3 or c.ParentId=3
order by pi.ID desc
offset #rowIndexToFetch rows
fetch next 1 rows only
But, if I use offset and fetch, I no longer can retrieve NULL records from tb_Images. Left outer join does not work anymore.
Example record returns without offset-fetch:
ID ProductName CategoryID ParentId MidPath
154 Chef Ceket 33 3 /cdn/www.sample.com/x
154 Chef Ceket 33 3 /cdn/www.sample.com/y
154 Chef Ceket 33 3 /cdn/www.sample.com/z
1 Eldiven 3 3 NULL
with offset-fetch:
ID ProductName CategoryID ParentId MidPath
154 Chef Ceket 33 3 /cdn/www.sample.com/x
expected to return:
ID ProductName CategoryID ParentId MidPath
154 Chef Ceket 33 3 /cdn/www.sample.com/x
1 Eldiven 3 3 NULL
The problem is, I cannot retrieve null records when I use offset-fetch statement. How can I fix this?
According to your comment I think you misanderstand the behaviour of OFFSET FETCH construction. Take another look at manuals.
If you want to get the result described above you could use such a query
WITH query AS (
SELECT
p.ID,
p.ProductName,
p.CategoryID,
c.ParentId,
pi.MidPath,
rn = ROW_NUMBER() OVER( PARTITION BY p.ID ORDER BY pi.MidPath )-- in your code ORDER set by pi.ID but there's no evidence it exists
FROM tb_Products p
JOIN tb_Categories c ON p.CategoryID = c.ID
LEFT JOIN tb_Images pi ON pi.ProductID = p.ID
WHERE c.ID = 3 or c.ParentId = 3
)
SELECT ID, ProductName, CategoryID, ParentId, MidPath
FROM query
WHERE rn = 1
select distinct p.ID,p.ProductName,p.CategoryID,c.ParentId,
pi.MidPath
from tb_Products p
join tb_Categories c
on p.CategoryID=c.ID
left outer join tb_Images pi
on pi.ProductID=p.ID
where c.ID=3 or c.ParentId=3
where c.ID=3 or c.ParentId=3
Try this code, in productImage is max. 1 value for each productID.
WITH productImage
AS ( SELECT MAX(pi.ID) AS ID
, pi.ProductID
FROM tb_Images pi
GROUP BY pi.ProductID
)
SELECT p.ID
, p.ProductName
, p.CategoryID
, c.ParentId
, pi.MidPath
FROM tb_Products p
JOIN tb_Categories c ON p.CategoryID = c.ID
LEFT OUTER JOIN productImage ON productImage.ProductID = p.ID
LEFT OUTER JOIN tb_Images pi ON pi.ID = productImage.ID
WHERE c.ID = 3
OR c.ParentId = 3;

How to get the most expensive item in a category using 2 tables?

I'm trying to get to show only the category that have the most expensive item.
I tried and with the query I have I get all categories and the price of the most expensive item in every category, but I don't know how to do to just get just the one category that have the most expensive.
select categories.category ,max(purchase_price) as dyrast_bok
from categories
inner join books on categories.category_id = books.category_id
group by categories.category;
The tables:
CATEGORIES ( category_id (PK), category )
BOOKS ( book_id (PK), title, publisher_id (FK), published_year,
purchase_price, category_id (FK),purchase_date, pages,
book_type_id (FK) )
select categories.category ,purchase_price as dyrast_bok
from categories
inner join books on categories.category_id = books.category_id
where purchase_price in (select max(purchase_price) from books)
You can try to order by max(purchase_price) desc and take the first one:
select *
from (your query with order by max(purchase_price) desc)
limit 1
You just need to use aliases so you can reuse them in the GROUP BY part, and use top 1 to get only the highest result when you order by max(b.purchase_price).
This is how should be your query:
select top 1 c.category , max(b.purchase_price) as dyrast_bok
from categories c
inner join books b on categories.category_id = books.category_id
order by dyrast_bok
group by c.category;
Simple way: Have a sub-query that returns the max purchase_price.
select c.category, b.purchase_price as dyrast_bok
from categories c
inner join books b on c.category_id = b.category_id
where b.purchase_price = (select max(purchase_price) from books)
Will return both books in case of a tie!
Alternative solution:
select c.category, b.purchase_price as dyrast_bok
from categories c
inner join books b on c.category_id = b.category_id
order by b.purchase_price desc
fetch first 1 row
Will not include ties, only one row will be returned. (AFAIK Postgresql doesn't support FETCH FIRST WITH TIES.)

How can I select lastest purchase price before a selling date?

I have 5 tables in my database, products, purchase_orders, invoice, invoice_details, and product_prices and their schema are like below.
Table: products
id
trade_name
Table: purchase_orders
id
product_id
created
Table: invoices
id
created
Table invoice_details
id
invoice_id
product_id
price_id
Table product_prices
id
retail_price
effective_from
effective_to
I think that I need to somehow join or check created on purchase_orders to created on invoices. So, I started with getting drug id, invoice date.
select d.id as drug_id
, i.created as invoice_created
, dp.retail_price
from drugs d
inner join invoice_details id
on d.id = id.drug_id
inner join invoices i
on i.id = id.invoice_id
inner join drug_prices dp
on dp.id = id.price_id
The next step is to match created on invoice that I have to created on purchase_orders which I haven't figured it out.
inner join (
select drug_id
, purchase_price
, ISNULL(created, CONVERT(DATETIME, '2015-10-07 01:37:12.370')) as created
from purchase_orders po
) as prepared_po
on prepared_po.created <= i.created
How can I get the lasted purchase price for each item that I sold?
Here's a simplified version of the logic you need (I renamed your columns so that it's easier to see which are which without doing all the intermediary joins you've already written yourself):
;With CTE as (select a.productID, a.saledate, b.price, b.purchasedate
, row_number() over (partition by a.productID, a.saledate
order by b.purchasedate desc) RN
from sales a
left join purchases b
on a.productID = b.productID and a.saledate >= b.purchasedate
)
Select productID, saledate, price, purchasedate
from CTE where RN = 1
Basically, you join to get all purchase records up to the sale date, then use row_number to find the latest one.
http://sqlfiddle.com/#!6/a0f68/2/0

Limit results from joined table to one row

Here is a simplified table structure:
TABLE products (
product_id INT (primary key, auto_increment),
category_id INT,
product_title VARCHAR,
etc
);
TABLE product_photos (
product_photo_id (primary key, auto_increment),
product_id INT,
photo_href VARCHAR,
photo_order INT
);
A product can have multiple photos, the first product photo for each product (based on the photo_order) is the default photo.
Now, I only need all of the photos on the product details page, but on pages where I am listing multiple products, for example a product directory page, I only want to display the default photo.
So what I am trying to do, is query a list of products including the default photo for each product.
This obviously doesn't work, it will return all photos with the product info duplicated for each photo:
SELECT p.*, ph.*
FROM products AS p
LEFT JOIN product_photos AS ph
ON p.product_id=ph.product_id
ORDER BY p.product_title ASC
I need to figure out how to do something like this, but I don't know the syntax (or if it is possible)
SELECT p.*, ph.*
FROM products AS p
LEFT JOIN product_photos AS ph
ON p.product_id=ph.product_id **ORDER BY ph.photo_order ASC LIMIT 1**
ORDER BY p.product_title ASC
Edit: I figured out a solution with help from the answers below, thanks all!
SELECT p.*, ph.*
FROM products AS p
LEFT JOIN product_photos AS ph
ON p.product_id=ph.product_id
AND ph.photo_order =
(
SELECT MIN(z.photo_order)
FROM product_photos AS z
WHERE z.product_id=p.product_id
)
GROUP BY p.product_id
ORDER BY p.product_title ASC
SELECT p.*, ph.*
FROM products AS p
INNER JOIN product_photos AS ph
ON p.product_id = ph.product_id
LEFT JOIN product_photos AS ph2
ON p.product_id = ph2.product_id
AND ph2.photo_order < ph.photo_order
WHERE ph2.photo_order IS NULL
ORDER BY p.product_title ASC
Note the how it joins to the product_photos table twice. The WHERE ph2.photo_order IS NULL will throw out all but the lowest photo order. It won't protect you against duplicate product_id / photo_orders combo though, you could add a GROUP BY on p.id if that's the case.
Use:
SELECT p.*,
pp.*
FROM PRODUCTS p
JOIN PRODUCT_PHOTOS pp ON pp.product_id = p.product_id
JOIN (SELECT x.product_id,
MIN(x.photo_order) AS default_photo
FROM PRODUCT_PHOTOS x
GROUP BY x.product_id) y ON y.product_id = pp.product_id
AND y.default_photo = pp.photo_order
SELECT p.*, ph.*
FROM products AS p
LEFT JOIN product_photos AS ph ON p.product_id=ph.product_id
ORDER BY p.product_title ASC, ph.photo_order ASC
GROUP BY p.product_id
LIMIT 0,10
SELECT ...
....
GROUP BY p.product_id