Who sell with lowest price - mysql

I have a database with tables:
suppliers:
fid , name
1 | 'Andrey'
2 | 'lucas'
products:
pid , name
1 | 'X'
2 | 'Y'
prdtFrn:
pid , fid , price
---------------- supplier 'andrey'
1 | 1 | 19.00
2 | 1 | 16.00
----------------- supplier 'lucas'
1 | 2 | 14.00
2 | 2 | 18.00
Querying the products, I get all products that have registred, but I need know who sells current product of 'product table' as lowest price, I don't know how to write the query

select p.*
from (
select pid, min(price) as MinPrice
from prdtFrn
group by pid
) pm
inner join prdtFrn p on pm.pid = p.pid and pm.MinPrice = p.price
You will see multiple rows per product where two suppliers are selling a product at the same price.

As with any complex SQL, build it up, step-by-step.
SELECT s.name AS s_name, p.name AS p_name, sp.price
FROM suppliers AS s
JOIN prdtFrn AS sp ON s.fid = sp.fid
JOIN products AS p ON p.pid = sp.pid
This gives you the list of seller, product and price. Now, presumably, for each product, you want the details of the supplier name who sells the product for the smallest price:
SELECT p_name, MIN(price) AS min_price
FROM (SELECT s.name AS s_name, p.name AS p_name, sp.price
FROM suppliers AS s
JOIN prdtFrn AS sp ON s.fid = sp.fid
JOIN products AS p ON p.pid = sp.pid
) AS spp
This gives you the minimum price for each product. Now you need to join that result with the first query, to yield the answer:
SELECT spp.p_name, spp.s_name, min_price
FROM (SELECT s.name AS s_name, p.name AS p_name, sp.price
FROM suppliers AS s
JOIN prdtFrn AS sp ON s.fid = sp.fid
JOIN products AS p ON p.pid = sp.pid
) AS spp
JOIN (SELECT p_name, MIN(price) AS min_price
FROM (SELECT s.name AS s_name, p.name AS p_name, sp.price
FROM suppliers AS s
JOIN prdtFrn AS sp ON s.fid = sp.fid
JOIN products AS p ON p.pid = sp.pid
) AS spp
) AS mp
ON mp.p_name = spp.p_name AND mp.min_price = spp.price;
Then, when it's working, you can optimize...for example, the minimum price calculation doesn't need the supplier information:
SELECT spp.p_name, spp.s_name, mp.min_price
FROM suppliers AS s
JOIN prdtFrn AS sp ON s.fid = sp.fid
JOIN (SELECT pid, MIN(price) AS min_price
FROM prdtFrn
GROUP BY pid
) AS mp
ON mp.pid = sp.pid AND mp.min_price = sp.price;
My first query went awry because I went for more information than was needed in the very first query; this is much simpler. So, the lessons here are:
Build up the query piece-by-piece.
Don't be satisfied with the first draft.

Related

Find customers that never placed an order with an employee

We have the following details from the output of a query a supplier id, a category name, and product count,
so 3 columns. Each supplier holds a certain category of products, and for each
category, the third column lists the number of products in that particular category.
How to write a query using this table which returns -
supplier if, category name, prodcount*100/totalnumberofproducts
Basically it should list the % of product in that category
The initial query that give the product count for each category is:
SELECT s.SupplierID, s.CompanyName AS Supplier, cat.CategoryName,
COUNT(*) AS CatProductCount
FROM suppliers AS s
JOIN products AS p ON s.SupplierID = p.SupplierID
JOIN categories AS cat ON p.CategoryID = cat.CategoryID
GROUP BY s.SupplierID, s.CompanyName, cat.CategoryName;
My Thoughts:
So if the initial query is S.
We could do
select supplierId, sum(productCount) as total from S groupby supplierID
This will tell us the total number of product for each supplier.
Let this query be called S2
select S.supplierId, (S.productCount*100)/S2.total from S inner join S2 on S.supplierID=S2.supplierId
I think I have the correct idea but the exact syntax I am using will not work.
you could use these two possibilities
As joined table
SELECT s.SupplierID, s.CompanyName AS Supplier, cat.CategoryName,
COUNT(*) * 100/ total as percentage_count
FROM suppliers AS s
JOIN products AS p ON s.SupplierID = p.SupplierID
JOIN categories AS cat ON p.CategoryID = cat.CategoryID
JOIN (select supplierId, sum(productCount) as total from suppliers group by supplierID ) S1 ON S1.supplierId = S,suppliers
GROUP BY s.SupplierID, s.CompanyName, cat.CategoryName;
Or as subselct
SELECT s.SupplierID, s.CompanyName AS Supplier, cat.CategoryName,
COUNT(*) * 100 / (select sum(productCount) from suppliers S1 WHERE S1.suppliers = S.supplierID ) as percentage_count
FROM suppliers AS s
JOIN products AS p ON s.SupplierID = p.SupplierID
JOIN categories AS cat ON p.CategoryID = cat.CategoryID
GROUP BY s.SupplierID, s.CompanyName, cat.CategoryName;
you should test both to see what is faster i would guess the second

php - Check same column twice in mysqli query

I have 3 tables in my php based system.
Those tables are product, category, product_categories.
Product
pid | product_name | price
1 | Nike T-Shirt | 23
Category
cid | category_name
1 | Men
2 | Women
Product_categories
pcid | cid | pid
1 | 1 | 1
2 | 2 | 1
That means, 1 product may be in both multiple categories.
Now I am developing the product search section with filter.
If a user select both categories, all the products in selected categories should display.
Example : If a user select both Men & Women, Nike T-Shirt should be displayed.
The query I used:
select p.*
from products p
left join product_categories pc on pc.pid=p.pid
WHERE pc.cid ='1' AND pc.cid = '2'
But it not returning correct products.
Where is the error?
You want conditions that span over multiple rows, which suggests aggregation. You can join the tables, group by product, and use the having clause for filtering:
select p.id, p.product_name, p.price
from product p
inner join product_category pc on pc.pid = p.pid
inner join category c on c.cid = pc.cid
where c.category_name in ('Men', 'Women')
group by p.pid, p.product_name, p.price
having max(c.category_name = 'Men') = 1 and max(c.category_name = 'Women') = 1
If you can filter by category id rather than by category name, then you need one less join:
select p.id, p.product_name, p.price
from product p
inner join product_category pc on pc.pid = p.pid
where pc.cid in (1, 2)
group by p.pid, p.product_name, p.price
having max(pc.cid= 1) = 1 and max(pc.cid = 2) = 1
Here are few alternatives:
-- All products that are at lest in one of the desired categories
select * from product where product.pid = any (select product_category.pid from product_category where cid = 1 or cid = 2);
-- Products that are in both categories
select * from product where product.pid = any (
select product_category.pid from product_category where cid = 1 or cid = 2 group by pid having count(cid) = 2
);
-- Products with additional information "in how many desired categories they are"
-- You can order by it and/or filter on it
with product_category_matches as (
select pid, count(*) as category_count from product_category where cid in (1, 2) group by pid
)
select *, product_category_matches.category_count from
product inner join product_category_matches on product.pid = product_category_matches.pid
where product_category_matches.category_count > 0 -- Product must be at least in one desired category
order by product_category_matches.category_count desc

Filtering query by passing idProduct, quantity

I want to create an Order_List with different ITEMS from a table called: Products
Inside table Products there are duplicates because a product is sell in different supermarkets, with differents prices.
I want the user to enter the desired product inside List table and get the lowest price. DO NOTE that every user has a different zipcode and every product belongs to a different supermarket with a different zipcode. The idea is to get the lowest price ONLY if the item has the same customer zipcode.Also, the quantity that was inserted in List table must be validated against Stock table.
This is what i have:
http://sqlfiddle.com/#!9/f9f73a/1
This is my example:
image
This is what i tried so far:
select p.idProduct, name, price, min(price)
from product p
inner join market m
on p.idMarket = m.idMarket
inner join stock s
on p.idProduct = s.idProduct
inner join list l
on p.idProduct = l.idProduct
where p.idProduct = 14
and exists (select 1
from stock s
where p.idProduct = s.idProduct
and l.quantity <= s.quantity)
group by p.idProduct, name, price
Could you please help me to solve this mess?
select p.idProduct, p.name, p.price, min(p.price)
from product p
inner join market m
on p.idMarket = m.idMarket
inner join stock s
on p.idProduct = s.idProduct
inner join list l
on p.idProduct = l.idProduct
where p.idProduct = 14
and exists (select 1
from stock s1
where p.idProduct = s1.idProduct
and l.quantity <= s1.quantity)
and p.price = (select min(price) from product p2 where p2.idProduct = p.idProduct)
group by p.idProduct, p.name, p.price

MySQL One-To-Many SUM of COUNTs

After 2 days of searching and trying similar questions, it's got to the point where I need to ask the question!
I have the following database structure (simplified)..
mt_product | mt_sku | mt_price
========== | ====== | ========
id | brand_id | mpn | id | product_id | retailer_id | sku | id | sku_id | price | date
For instance...
* A can of Coca-Cola is ONE product.
* It can be sold in many different retailers, who will all have a SKU for it.
* This SKU will have a price, which can change day-by-day.
I want to list the total number of prices for the product.
To list this I currently have the following query which nearly works...
SELECT
p.id AS pid, p.title AS p_title, p.cat, p.mpn,
b.id AS bid, b.name AS brand,
(SELECT COUNT(s.id) FROM mt_sku AS s WHERE s.pid = p.id) AS num_sku,
(SELECT COUNT(gbp.id) FROM mt_price AS gbp INNER JOIN mt_sku ON mt_sku.id = gbp.sid ) AS num_price
FROM mt_product AS p
INNER JOIN mt_brand b ON p.bid = b.id
INNER JOIN mt_sku s ON p.id = s.pid
num_sku returns as expected, however when I introduce the second sub query for num_price (and I have revised this many times) I either get...
* no duplications of the pid but the num_price is the total number of prices to SKUs, not the amount of prices for this product_id (as query above) eg1_img
* the correct number of num_price, but instead of totalling up the total num_price, the pid is duplicated in the table (as query below) - therefore as the pid is duplicated, this does not give me the result I want. I added DISTINCT as it helped an earlier version of the query, it now makes no difference. eg2_img
SELECT
DISTINCT(p.id) AS pid, p.title AS p_title, p.cat, p.mpn,
b.id AS bid, b.name AS brand,
(SELECT COUNT(s.id) FROM mt_sku AS s WHERE s.pid = p.id) AS num_sku,
(SELECT COUNT(gbp.id) FROM mt_price AS gbp WHERE s.id = gbp.sid) AS num_price
FROM mt_product AS p
INNER JOIN mt_brand b ON p.bid = b.id
INNER JOIN mt_sku s ON p.id = s.pid
I'm pretty sure the key to this is that
product can have multiple SKUs, of which a SKU has multiple price history.
Any help or ideas of the schema would be superb.
Try this:
SELECT
p.id AS pid, p.title AS p_title, p.cat, p.mpn,
b.id AS bid, b.name AS brand,
COUNT(DISTINCT s.id) AS num_sku,
COUNT(gbp.id) AS num_price
FROM mt_product AS p
INNER JOIN mt_brand b ON p.brand_id = b.id
INNER JOIN mt_sku s ON p.id = s.product_id
INNER JOIN mt_price gbp ON s.id = gbp.sku_id
GROUP BY b.id, p.id
The products that don't have SKUs defined will not appear in the result set. Use LEFT JOIN mt_sku to make them appear in the result set (having 0 for num_sku and num_price):
LEFT JOIN mt_sku s ON p.id = s.product_id
In both variants of the query, the products that do not have prices defined will not appear in the result set. Use LEFT JOIN mt_price to include them into the result set (having 0 for num_price):
LEFT JOIN mt_price gbp ON s.id = gbp.sku_id
Take a look at the MySQL documentation for JOINs, GROUP BY and GROUP BY aggregate functions.
If you want to list the total prices then you need correlations.
Your first count is fine, because it is correlated to the outer query. The second has no correlation, so that seems strange. The following fixes the num_price subquery:
SELECT p.id AS pid, p.title AS p_title, p.cat, p.mpn,
b.id AS bid, b.name AS brand,
(SELECT COUNT(s2.id) FROM mt_sku s2 WHERE s2.pid = p.id) AS num_sku,
(SELECT COUNT(gbp.id) FROM mt_price gbp WHERE s.id = gbp.sid ) AS num_price
FROM mt_product p INNER JOIN
mt_brand b
ON p.bid = b.id INNER JOIN
mt_sku s
ON p.id = s.pid;
I'm also not sure why you have all the joins in the outer query. I assume that a given product is going to have multiple rows, and you want the multiple rows to have the same num_sku and num_price values.

Find products that are sold at a price less than mine

I have a table of products, suppliers and prdtFrn as follows:
suppliers:
fid , name
1 | 'Andrey'
2 | 'lucas'
products:
pid , name
1 | 'X'
2 | 'Y'
prdtFrn:
pid , fid , price
---------------- supplier 'andrey'
1 | 1 | 19.00
2 | 1 | 16.00
----------------- supplier 'lucas'
1 | 2 | 14.00
2 | 2 | 18.00
And I am looking for a SQL query that will return all products that are sold at a price less than mine (andrey). In this example, I would want to get product "X", because lucas is selling it for less than I am.
A lot of the other answers seem complicated, but the answer is simple:
select distinct p1.*
from prdtfrn p1
join prdtfrn p2 on p1.pid = p2.pid and p2.fid != 1 and p2.price < p1.price
where p1.fid = 1; // fid=1 is known to be 'Audrey'
This query lists all products that are sold cheaper elsewhere.
Just select from the prdtFrn table twice. From there, the query is straightforward.
I've included an untested example below. A corresponds to the competitors' products and B corresponds to yours.
SELECT
suppliers.name,
A.pid,
A.price
FROM
prdtfrn AS A,
prdtfrn AS B,
suppliers
WHERE
A.price < B.price
AND A.pid = B.pid
AND B.fid = 1
AND A.fid = suppliers.fid;
I assumed that you are comparing to many suppliers (not only to lucas) so this is my query. Try this one:
SELECT e.name,
g.name,
f.price
FROM suppliers e INNER JOIN prdtFrn f ON
e.fid = f.fid
INNER JOIN products g ON
f.pid = g.pid
WHERE e.name <> 'Andrey' AND -- this selects all products which is not yours
f.pid IN -- the selected products should match your products
(SELECT c.pid -- this subquery selects all your products
c.name,
b.price
FROM suppliers a INNER JOIN prdtFrn b ON
a.fid = b.fid
INNER JOIN products c ON
b.pid = c.pid
WHERE a.name = 'Audrey') d AND
f.price < d.price -- product is less than yours
Here is a query to get the info you're looking for. As there might be products that other suppliers have on sale and you don't, I thought you might be interested in finding those out too.
This is the query that you're asking for (without the products other suppliers have and you don't):
select sp2.pid, p.name as ProductName, sp2.price, s2.name as SupplierName
from prdtFrn sp2 join (
select sp.pid, sp.price from suppliers s
join prdtFrn sp on sp.fid = s.fid
where s.name = 'Andrey'
) as AndreysProducts
on AndreysProducts.pid = sp2.pid
join products p on sp2.pid = p.pid
join suppliers s2 on s2.fid = sp2.fid
where sp2.price < AndreysProducts.price
Example
This is the query that you might be interested in (with the products other suppliers have and you don't):
select sp2.pid, p.name as ProductName, sp2.price, s2.name as SupplierName
from prdtFrn sp2 left join (
select sp.pid, sp.price from suppliers s
join prdtFrn sp on sp.fid = s.fid
where s.name = 'Andrey'
) as AndreysProducts
on AndreysProducts.pid = sp2.pid
join products p on sp2.pid = p.pid
join suppliers s2 on s2.fid = sp2.fid
where sp2.price < AndreysProducts.price or AndreysProducts.pid is null
Example