I want to write a SQL query in laravel/php, to fetch top 10 distinct products purchased by customer.
My table structure looks like this:
Orders table (customer id, product id, etc....)
Products table (product id, product name, etc....)
This is my query attempt:
SELECT products.*
FROM products
WHERE products.id = [SELECT DISTINCT (products.id)
FROM orders
WHERE customer.id=id->list(10)]
I can see where you're going with your attempted query but unfortunately that won't give you top 10. Instead, you might not get any result at all:
SELECT products.*
FROM products
WHERE products.id=[SELECT DISTINCT (products.id)
^^^ FROM orders
WHERE customer.id=id->list(10)]
The = means that you're looking for an exact match and your subquery suppose to returns 10 rows of data, which if you go with this operation, you'll receive this error.
Subquery returns more than 1 row
But if you change that to IN, you might receive this error instead
This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
With your current attempt, your option is to do a JOIN. However, I'm wondering how do you get your top 10? I can see that you're looking for top 10 of products but base on what? Sales amount? Quantity ordered?
On that note, here's an example of top 10 products by quantity ordered.
SELECT P.*
FROM Products P
JOIN ( SELECT product_id
FROM Orders
GROUP BY product_id
ORDER BY SUM(Qty) DESC
LIMIT 10) O
ON P.id=O.product_id;
The subquery is not necessary but I'm imitating what you tried with a subquery albeit not exactly the same process. Here it is without subquery:
SELECT P.*
FROM Orders O
JOIN Products P ON O.product_id=P.id
GROUP BY product_id
ORDER BY SUM(Qty) DESC
LIMIT 10;
Or perhaps you're looking for top 10 by sales amount?
SELECT P.*
FROM Orders O
JOIN Products P ON O.product_id=P.id
GROUP BY product_id
ORDER BY SUM(UnitPrice*Qty) DESC
LIMIT 10;
Demo fiddle
Related
This is my homework task:
Products which were ordered along with the 5 most ordered products more than once and the count of orders they were included in. (Do not include the 5 most ordered products in the final result)
Products and orders are in same table. Order detail contain Order detail ID, order id, product id, quantity.
I've tried everything but I'm struggling with "along with" statement in the query.
Here is a query I have tried:
select
productid,
count
(
(select productid from orderdetails)
and
(select productid from orderdetails order by quantity desc limit 5)
) as ORDERS
from orderdetails
group by productid
order by ORDERS desc
You select from orderdetails, aggregate to get one result row per product and you count. It is very common to count rows with COUNT(*), but you can also count expressions, e.g. COUNT(mycolumn) where you just count those that are not null. You are counting an expresssion (because it is not COUNT(*) but COUNT(something else) that you are using). The expression to test for null and count is
(select productid from orderdetails)
and
(select productid from orderdetails order by quantity desc limit 5)
This, however is not an expression that leads to one value that gets counted (when it's not null) or not (when it's null). You are selecting all product IDs from the orderdetails table and you are selecting all the five product IDs from the orderdetails table that got ordered with the highest quantity. And then you apply AND as if these were two booleans, but they are not, they are data sets. Apart from the inappropriate use of AND which is an operator on booleans and not on data sets, you are missing the point here that you should be looking for products in the same order, i.e. compare the order number somehow.
So all in all: This is completely wrong. Sorry to say that. However, the task is not at all easy in my opinion and in order to solve it, you should go slowly, step by step, to build your query.
Products which were ordered along with the 5 most ordered products more than once
Dammit; such a short sentence, but that is deceiving ;-) There is a lot to do for us...
First we must find the 5 products that got ordered most. That means sum up all sales and find the five top ones:
select productid
from orderdetails
group by productid
order by sum(quantity) desc
limit 5
(The problem with this: What if six products got ordered most, e.g. products A, B, and C with a quantity of 200 and products D, E, and F with a quantity of 100? We would get the top three plus two of the top 4 to 6. In standard SQL we would solve this with a ties clause, but MySQL's LIMIT doesn't feature this.)
Anyway. Now we are looking for products that got ordered with these five products along. Does this mean with all five at once? Probably not. We are rather looking for products that were in the same order with at least one of the top five.
with top_5_products as
(query above)
, orders_with_top_5 as
(select orderid
from orderdetails
where productid in (select productid from top_5_products)
)
, other_products_in_order as
(select productid, orderid
from orderdetails
where orderid in (select orderid from orders_with_top_5)
and productid not in (select productid from top_5_products)
And once we've got there, we must even find products that got ordered with some of the top 5 "more than once" which I interpret as to appear in at least two orders containing top 5 products.
with <all the above>
select productid
from other_products_in_order
group by productid
having count(*) > 1;
And while we have counted how many orders the products share with top 5 products, we are still not there, because we are supposed to show the number of orders the products were included in, which I suppose refers to all orders, not only those containing top 5 products. That is another count, that we can get in the select clause for instance. The query then becomes:
with <all the above>
select
productid,
(select count(*) from orderdetails od where od.productid = opio.productid)
from other_products_in_order opio
group by productid
having count(*) > 1;
That's quite a lot for homework seeing that you are struggling with the syntax still. And we haven't even addressed that top-5-or-more ties problem yet (for which analytic functions come in handy).
The WITH clause is available since MySQL 8 and helps getting such a query that builds up step by step readable. Old MySQL versions don't support this. If working with an old version I suggest you upgrade :-) Else you can use subqueries directly instead.
I'm trying to merge these two statements into one query to get the a list of product names(or ids) against the average of their TTFF data, and I'm stuck.
select AVG(TTFF) from TTFFdata group by product_id
select product.product_name, count(*) from product join TTFFdata on product.product_id = TTFFdata.product_id
I've looked into using a temporary table (CREATE TEMPORARY TABLE IF NOT EXISTS averages AS (select AVG(TTFF) from TTFFdata group by product_id)) but couldn't get that to work with a join.
Anyone able to help me please?
You need to understand the components. Your second query is missing a group by. This would seem to be what you want:
select p.product_name, count(t.product_id), avg(t.TTFF)
from product p left join
TTFFdata t
on p.product_id = t.product_id
group by p.product_name
It is better to do group by on product_id, product_name for two reasons. One is, you can select product id along with product name. Second reason is, If the product name is not unique then it may give wrong results(this may be a rare scenario like product name is same but it differs based on other columns like version or model). The below is the final query.
select Product.product_id,
product_name,
AVG(TTFF) as Avg_TTFF
from Product
inner join
TTFFdata
on Product.product_id = TTFFdata.product_id
group by Product.product_id,Product.product_name
TTFFdata:
product:
Output:
I am trying to write a mysql query for an app I'm developing for android.
I have a database that has a bill_content table and a products table
I want to select top 10 most sold products.
This is a minimal version of what I have, but it's all I need to get an answer here.
bill_content table has the columns: id, id_product, quantity (id_product and quantity here can be duplicate because this table is larger, containing id_bill and other information)
products table has the columns: id, name
SELECT products.name AS Product,
bill_content.quantity AS Quantity
FROM bill_content, products
WHERE bill_content.id = products.id
ORDER BY bill_content.quantity DESC
LIMIT 10
Of course this returns a table of 2 rows containing all the products and their quantity in the bill_content table, but there are duplicates and I need to make sum of their quantity and display them as a single row.
Thank you in advance.
ANSWERED
This could be done using GROUP BY as Gordon Linoff said.
You want a group by. You should also learn to use proper explicit join syntax:
SELECT p.name AS Product,
SUM(bc.quantity) AS Quantity
FROM bill_content bc JOIN
products p
ON bc.id = p.id
GROUP BY p.name
ORDER BY SUM(bc.quantity) DESC
LIMIT 10;
I have 3 tables: products, orders and orderLines(order_id, product_id).
I have an sql query to figure out which seems nearly impossible to do in only one query.
Is there a way to have in only one query:
All the products but showning a specific order's products first;
which means that: for an order A: show product1, product2.. present in orderA's orderLines first, than the following products (not ordered) are shown next.
PS:
I know it's possible to achieve this with a union of two queries, but it would be better to have it done in only one query.
You can put a subquery in the order by clause. In this case, an exists subquery is what you need:
select p.*
from products p
order by (exists (select 1
from orderlines ol
where p.productid = ol.productid and o.orderid = ORDERA
)
) desc;
Is there a way of limiting the result of a subquery? The sort of thing I'm trying to achieve can be explained by the query below:
SELECT *
FROM product p
JOIN (
SELECT price
FROM supplierPrices sp
ORDER BY price ASC
LIMIT 1
) ON (p.product_id = sp.product_id)
The idea would be to get only the lowest price for a particular product from a table that had all the price data in it. LIMIT 1 is limiting the entire result set, whereas excluding it would result in a row being returned for each price, with duplicated product data. I tried GROUP BY price as well to no avail.
Once the limit is working I need to apply IFNULL as well, so that if there is no price found at all for any supplier it can return a supplied string, such as "n/a" rather than NULL. I assume that would just mean modifying the SELECT as below, and changing the JOIN to a LEFT JOIN?
SELECT *, IFNULL(price,'n/a')
Just to expand on Wolfy's answer slightly, and bearing in mind this is untested:
SELECT *
FROM product p
LEFT JOIN (
SELECT product_id, MIN(price)
FROM supplierPrices sp
GROUP BY product_id
) x ON (p.product_id = x.product_id)
And, as you say, it should just be a matter of doing an IFNULL on that column to replace it with something sensible.