Something like forEach in SQL? - mysql

So i need to make a query that displays how many times each item has been sold, i have two tables that look something like this
Products
+-----------+-------+-----------+
| productID | price | inventory |
+-----------+-------+-----------+
| 31f9d | 21 | 109 |
| M21xZ | 80 | 87 |
| 56JmZ | 35 | 48 |
+-----------+-------+-----------+
orderDetails
+---------+-------+----------+
| orderId | item | Quantity |
+---------+-------+----------+
| 1 | 31f9d | 2 |
| 2 | 31f9d | 5 |
| 2 | 56JmZ | 3 |
+---------+-------+----------+
Thanks in advance!

If you just want the number of sales per product, you can aggregate the details table:
select item, count(*) as cnt_sales, sum(quantity) as sum_quantity
from orderdetails
group by item
If you want the details of each products too, then you can join.
select p.*, d.cnt_sales, d.sum_quantity
from products p
left join (
select item, count(*) as cnt_sales, sum(quantity) as sum_quantity
from orderdetails
group by item
) d on d.item = p.productid
The left join allows products without any sale; it will bring null values in columns cnt_sales and sum_quantity in the resultset.

Join both tables together, then simply group by itemId:
select p.productID, sum(o.Quantity) from Products p
left join orderDetails o on p.productID = o.item
group by p.productID

Related

How can I count the number of all invoice's products?

I have a query with selects a specific invoice like this:
SELECT i.*
FROM invoices i
WHERE i.id = 15
Also I need to select all products that are belong to this invoice. I do that using this query:
SELECT i.*, p.*
FROM invoices i
LEFT JOIN pivot pi
ON pi.invoice_id = i.id
LEFT JOIN products p
ON pi.product_id = p.id
WHERE i.id = 15
Now I want to know, how can I use count() and calculate the number of products for the invoice?
I want a output like this:
+------+--------------+------+-----------+---------+
| i.id | i.number | p.id | p.name | count() |
+------+--------------+------+-----------+---------+
| 15 | 123456 | 3 | SX12 | 5 |
| 15 | 123456 | 17 | MGS2 | 5 |
| 15 | 123456 | 14 | BFE12-3 | 5 |
| 15 | 123456 | 32 | LG2-1 | 5 |
| 15 | 123456 | 6 | VBDS2 | 5 |
+------+--------------+------+-----------+---------+
This is a job for GROUP BY.
Try something like this:
SELECT i.id, i.number, p.id, p.name, IFNULL(COUNT(*),0) product_count
FROM invoices i
LEFT JOIN pivot pi ON pi.invoice_id = i.id
LEFT JOIN products p ON p.id = pi.product_id
WHERE (whatever you need)
GROUP BY i.id, i.number, p.id, p.name
I guess from your use of LEFT JOIN operations that you want the invoice to appear in your result set even if it has no associated products. tHE IFNULL() function makes those rows contain 0 rather than null.

Fetch count of product_id from orderdetails Table according to match order id

i am trying to retrieve columns from below three data-tables in mysql expected output is :
count (number of time the product_id exists in orderdetails table)
id (from products table)
name (from products table)
Orders Table
+-----+--------+
| id | status |
+-----+--------+
| 124 | unpaid |
| 125 | paid |
| 126 | paid |
| 127 | paid |
| 128 | unpaid |
+-----+--------+
orderdetails table
+-----+------------+----------+
| id | product_id | order_id |
+-----+------------+----------+
| 332 | 1 | 125 |
| 333 | NULL | 125 |
| 334 | 144 | 125 |
| 335 | 162 | 126 |
| 336 | 144 | 126 |
| 337 | 162 | 127 |
+-----+------------+----------+
products Table
+-----+------------------------------------------------------+
| id | name |
+-----+------------------------------------------------------+
| 1 | Chaacoca Argan Oil Daily Moisture Repair Conditioner |
| 144 | Sandalwood 8oz Hand and Body Lotion |
| 162 | Mayumi Squalane Skin Oil - 2.17 fl oz |
+-----+------------------------------------------------------+
Expected output should look like this:
+------------+----------+-----------------------------------------------------+
| product_id |count_prod|Product_name |
+------------+----------+-----------------------------------------------------+
| 1 | 1 |Chaacoca Argan Oil Daily Moisture Repair Conditioner |
| 144 | 2 |Sandalwood 8oz Hand and Body Lotion |
| 162 | 2 |Mayumi Squalane Skin Oil - 2.17 fl oz |
+------------+----------+-----------------------------------------------------+
Any suggested query will be helpful.
Thank you (in advance)!
you can use joins for this like :
select a.*,count(b.order_id) as total_order,GROUP_CONCAT(c.id) as product_ids,GROUP_CONCAT(c.name) as product_names
from Orders a
left outer join orderdetails b on a.id = b.order_id
left outer join products c on b.product_id = c.id
group by a.id
Assuming all orders have at least one detail, then you don't need the orders table. You can do:
select od.order_id, count(*) as num_details,
group_concat(od.product_id) as product_ids,
group_concat(p.name) as product_names
from orderdetails od left join
products p
on od.product_id = p.id
group by od.order_id;
If products can be duplicated in orderdetails for a single order, then you can use distinct to avoid duplication:
select od.order_id, count(distinct od.product_id) as num_details,
group_concat(distinct od.product_id) as product_ids,
group_concat(distinct p.name) as product_names
from orderdetails od left join
products p
on od.product_id = p.id
group by od.order_id;
EDIT:
Based on the revised question, you want the count by products. This is simple:
select p.id, p.name, count(od.product_id) as num_details
from products p join
orderdetails od
on od.product_id = p.id
group by p.id, p.name;
If you want to include products with no order details, then use left join instead of join.
Please try
select p.id,od.count_prod,p.name
from products as p,
(select product_id ,count(*) as count_prod from orderdetails group by product_id) as od
where p.id = od.product_id
Query Snapshot:

MySQL use DISTINCT with GROUP BY on same column

I currently have a query similar to:
select customers.customerId, items.itemName, sum(orders.quantity) as boughtTotal
from customers join orders on customers.customerId = orders.customerId
join items on items.itemId = orders.itemId
group by customers.customerId, items.itemName
order by boughtTotal desc;
The purpose of the query is to relate customers to items they bought, which may span over many orders, and total the amount of each unique item bought. This being achieved with what I have. I want to take this a step further now and select the most popular item for each customer. Since this is ordered with most bought items at the top, I figured I'd just have to add distinct next to customers.customerId in the select statement to have duplicates removed. However, adding distinct seems to do nothing. I'd appreciate help in knowing why distinct is seemingly doing nothing here, but also how to achieve what I'm trying to do - remove duplicates besides a customer's most popular item.
Tables:
customers
customerId | name
1 | John
2 | Jane
orders
orderId | customerId | quantity | itemId
1 | 1 | 11 | 1
2 | 2 | 13 | 2
3 | 1 | 4 | 2
4 | 2 | 14 | 1
5 | 1 | 1 | 1
items
itemId | itemName
1 | dog
2 | cat
So from this data the current query will return the following:
customerId | itemName | boughtTotal
2 | dog | 14
2 | cat | 13
1 | dog | 12
1 | cat | 4
And what I'd like to have is the following:
customerId | itemName | boughtTotal
2 | dog | 14
1 | dog | 12
Try this;)
select t1.*
from (
select customers.customerId, items.itemName, sum(orders.quantity) as boughtTotal
from customers join orders on customers.customerId = orders.customerId
join items on items.itemId = orders.itemId
group by customers.customerId, items.itemName) t1
inner join (
select max(boughtTotal) as boughtTotal, customerId
from (
select customers.customerId, items.itemName, sum(orders.quantity) as boughtTotal
from customers join orders on customers.customerId = orders.customerId
join items on items.itemId = orders.itemId
group by customers.customerId, items.itemName)t
group by customerId) t2 on t1.customerId = t2.customerId and t1.boughtTotal = t2.boughtTotal
DEMO HERE

Get SUM of two fields in 2 different related tables

I'd like to get the SUM of the amount column in two related tables.
Invoices Table:
-----------------------------------------
| id | student_id | created | updated |
-----------------------------------------
| 5 | 25 | date | date |
-----------------------------------------
Invoice Items Table:
------------------------------
| id | invoice_id | amount |
------------------------------
| 1 | 5 | 250 |
------------------------------
| 2 | 5 | 100 |
------------------------------
| 3 | 5 | 40 |
------------------------------
Payments Table:
------------------------------
| id | invoice_id | amount |
------------------------------
| 1 | 5 | 100 |
------------------------------
| 2 | 5 | 290 |
------------------------------
Desired Output:
--------------------------------------
| id | invoiceTotal | paymentTotal |
--------------------------------------
| 1 | 390 | 390 |
--------------------------------------
The query I've tried
SELECT
i.id,
sum(ii.amount) as invoiceTotal,
sum(p.amount) as paymentTotal
FROM
invoices i
LEFT JOIN
invoice_items ii ON i.id = ii.invoice_id
LEFT JOIN
payments p ON i.id = p.invoice_id
WHERE
i.student_id = '25'
GROUP BY
i.id
What this seems to do is calculate the sum of the payments properly but the invoice_items.amount appears to have been duplicated by 6 (which is the number of payments there are).
I have read similar questions on SO here and here but the examples are so much more complex than what I'm trying to do and I can't figure out what to put where.
The join causes a problem with cartesian products. If a student has multiple invoice items and payments, then the totals will be wrong.
One approach that works best for all invoices is a union all/group by approach:
select i.id, sum(invoiceTotal) as invoiceTotal, sum(paymentTotal) as paymentTotal
from ((select i.id, 0 as invoiceTotal, 0 as paymentTotal
from invoices i
) union all
(select ii.invoiceId, sum(ii.amount) as invoiceTotal, NULL
from invoiceitems ii
group by ii.invoiceId
) union all
(select p.invoiceId, 0, sum(p.amount) as paymentTotal
from payments p
group by p.invoiceId
)
) iip
group by id;
For a single student, I would recommend correlated subqueries:
select i.id,
(select sum(ii.amount)
from invoiceitems ii
where ii.invoiceid = i.id
) as totalAmount,
(select sum(p.amount)
from payment p
where p.invoiceid = i.id
) as paymentAmount
from invoices i
where i.studentid = 25;

Add column from another table, but not affect count()

I have already a query with multiple JOINs, simple list of reservations
SELECT reservation.reservation_id, customer.customer_id, customer.name, count(ordered_services.reservation_id) AS num_of_ordered_services
FROM reservations
JOIN customers ON reservations.customer_id = customer.customer_id
LEFT JOIN ordered_services ON reservations.reservation_id = ordered_services.reservation_id
GROUP BY reservation.reservation_id, customer.customer_id, customer.name
ORDER BY reservation.reservation_id
which outputs something like
reservation_id | customer_id | name | num_of_ordered_services
1 | 1909091202 | John | 2
2 | 2512541508 | Jane | 3
I would like to add another column with information about payment, but simple JOIN, LEFT JOIN interferes with existing count() column. Like
SELECT reservation.reservation_id, count(payments.reservation_id) AS num_of_payments
FROM reservations
LEFT JOIN payments ON reservations.reservation_id = payments.reservation_id
GROUP BY reservation.reservation_id
ORDER BY reservation.reservation_id
reservation_id | num_of_payments
1 | 0
2 | 2
but in both a single result. How to achieve this?
PS: num_of_payments is not necessary, I only need to know if the payment for certain reservation exists or not (1, 0).
Thank you
tbl structure, nothing special:
reservations
reservation_id | customer_id | added
1 | 1909091202 | 2011-11-04 02:37:28
2 | 2512541508 | 2011-11-04 14:27:01
customers
customer_id | name | personal information columns ...
1909091202 | John | | |
2512541508 | Jane | | |
... | ... | | |
payments
payment_id | reservation_id | customer_id | total | added
1 | 2 | 1909091202 | 199 | 2011-11-04 02:37:28
2 | 2 | 2512541508 | 50 | 2011-11-04 14:27:01
You could use a subselect for the additional field.
SELECT reservation.reservation_id, customer.customer_id, customer.name,
count(ordered_services.reservation_id) AS num_of_ordered_services,
(SELECT count(*) FROM payments WHERE reservation.reservation_id=payments.reservation_id) AS num_of_payments
FROM reservations
JOIN customers ON reservations.customer_id = customer.customer_id
LEFT JOIN ordered_services ON reservations.reservation_id = ordered_services.reservation_id
GROUP BY reservation.reservation_id, customer.customer_id, customer.name
ORDER BY reservation.reservation_id
Something like the following should work:
select
reservation.reservation_id,
(case when exists (select * from payments p1 where p1.reservation_id = reservation.reservation_id) then 1 else 0 end) as one_or_many_payments_made
from reservation
GROUP BY reservation.reservation_id
ORDER BY reservation.reservation_id
But without your data, there is some guesswork here.