SQL sum values from many to many - mysql

There's my sql data:
id name prod_id price
------------------------
9 A 23 4100
94 B 40 1500
94 B 36 1500
I would ideally like to finally have:
id name prod_id price
------------------------
9 A 23 4100
94 B 40,36 1500
Here's my code I've tried using GROUP BY
SELECT
company.id,
company.name,
order_product.product_id,
SUM(orders.price)
FROM
orders
INNER JOIN users ON orders.user_id = users.id
INNER JOIN order_product ON orders.id = order_product.order_id
INNER JOIN customer ON orders.customer_id = customer.id
INNER JOIN company ON customer.company_id = company.id
GROUP BY
company.id,

group_concat should do the trick
SELECT
company.id,
company.name,
group_concat(order_product.product_id9,
orders.price
FROM
orders
INNER JOIN users ON orders.user_id = users.id
INNER JOIN order_product ON orders.id = order_product.order_id
INNER JOIN customer ON orders.customer_id = customer.id
INNER JOIN company ON customer.company_id = company.id
group by
company.id,
company.name,
orders.price

Related

Getting parent categories sum(price) formed in different tables

I am trying to get the total prices of all parent categories in a specific month and year. A parent category is any category with a parent_id == 0. My query is like below:
SELECT
ROUND(SUM(od.total_price)) as price,
c.parent_id as pId,
c1.name
FROM a_orders o
INNER JOIN a_order_details as od on o.id = od.order_id
INNER JOIN a_product as p on p.id = od.product_id
INNER JOIN a_category as c on c.id = p.category_id
LEFT OUTER JOIN a_category c1 ON c.parent_id = c1.id
WHERE YEAR(order_date) = 2018
AND o.STAT = 'Y'
AND MONTH(order_date) = 6
GROUP BY c.parent_id;
I'm getting parent categories just which have a price but I need to get all parent categories, if there is no price result should be 0.
My sqlfiddle is -> http://sqlfiddle.com/#!9/b9f4c1/1
My result is like this :
price pId name
410 1 T-SHIRT
400 2 JEANS
But should be like this :
price pId name
410 1 T-SHIRT
400 2 JEANS
0 6 SHOES
To do this you need switch your logic around. First select all of your parent categories, then join your order data. This way you can ensure all of the desired categories will be included in the results.
There are a bunch of ways you could approach this — here I created a subquery with all of the order data and then a LEFT JOIN with the category table:
SELECT
IFNULL(ROUND(SUM(orders.total_price)),0) AS price,
c.id AS pId,
c.name
FROM a_category c
LEFT JOIN (
SELECT od.total_price, c.parent_id
FROM a_orders o
INNER JOIN a_order_details od ON o.id = od.order_id
INNER JOIN a_product p ON p.id = od.product_id
INNER JOIN a_category c ON c.id = p.category_id
WHERE YEAR(order_date) = 2018
AND o.STAT = 'Y'
AND MONTH(order_date) = 6
) orders ON orders.parent_id = c.id
WHERE c.parent_id = 0
GROUP BY c.id;
http://sqlfiddle.com/#!9/b9f4c1/3

How to select records where all joined records aren't a match for criteria

I've a setup with the following tables (using MySQL):
orders, which have many:
a join table order_items, which have one from the:
products table
I've written a query to select orders where all their products are of a certain type:
SELECT orders.* FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
WHERE products.type = 'FooProduct'
AND (
NOT EXISTS (
SELECT null
FROM products
INNER JOIN order_items ON order_items.product_id = products.id
WHERE order_items.order_id = orders.id
AND products.type != 'FooProduct'
)
)
I run similar a couple of times: firstly to get orders comprised of all FooProducts, and again to get orders with all BarProducts.
My sticking point has been generating a third query to get all other orders, i.e. where all their products' types are not exclusively FooProducts, or exclusively BarProducts (aka a mix of the two, or other product types).
So, my question is how can I get all records where all product types aren't exclusively FooProducts or exclusively BarProduct.
Here's a little example data, from which I'd like to return the orders with the IDs 3 and 4:
- orders
id
1
2
3
4
-- order_items
id order_id product_id
1 1 1
2 1 1
3 2 2
4 2 2
5 3 3
6 3 4
7 4 1
8 4 2
-- products
id type
1 'FooProduct'
2 'BarProduct'
3 'OtherProduct'
4 'YetAnotherProduct'
I've attempted this, awfully so placing as a subtext, with the following in place of the existing AND (even the syntax is way off):
NOT HAVING COUNT(order_items.*) = (
SELECT null
FROM products
INNER JOIN order_items ON order_items.product_id = products.id
WHERE order_items.order_id = orders.id
AND products.type IN ('FooProduct', 'BarProduct')
)
Instead of using Correlated subqueries, you can use Having and conditional aggregation function based filtering.
products.type IN ('FooProduct', 'BarProduct') will return 0 if a product type is none of them. We can use Sum() function on it, for further filtering.
Try the following instead:
SELECT orders.order_id
FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
GROUP BY orders.order_id
HAVING SUM(products.type IN ('FooProduct', 'BarProduct')) < COUNT(*)
For the case, where you are looking for orders which has only FooProduct type, you can use the following instead:
SELECT orders.order_id
FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
GROUP BY orders.order_id
HAVING SUM(products.type <> 'FooProduct') = 0
Another possible approach is:
SELECT orders.order_id
FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
GROUP BY orders.order_id
HAVING SUM(products.type = 'FooProduct') = COUNT(*)
You can use aggregation and a having clause for this:
SELECT o.*
FROM orders o INNER JOIN
order_items oi
ON oi.order_id = o.id INNER JOIN
products p
ON p.id = oi.product_id
GROUP BY o.id -- OK assuming `id` is the primary key
HAVING SUM(p.type NOT IN ('FooProduct', 'BarProduct')) > 0; -- at least one other product
Actually, that is not quite right. This gets orders that have some other product, but it doesn't pick up orders that are mixes only of foo and bar. I think this gets the others:
HAVING SUM(p.type = 'FooProduct') < COUNT(*) AND
SUM(p.type = 'BarProduct') < COUNT(*)
This is a basic solution, not so efficient but easy:
SELECT * FROM orders WHERE id NOT IN (
SELECT orders.id FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
WHERE products.type = 'FooProduct'
AND (
NOT EXISTS (
SELECT null
FROM products
INNER JOIN order_items ON order_items.product_id = products.id
WHERE order_items.order_id = orders.id
AND products.type != 'FooProduct'
)
)
) AND id NOT IN (
SELECT orders.id FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
WHERE products.type = 'BarProduct'
AND (
NOT EXISTS (
SELECT null
FROM products
INNER JOIN order_items ON order_items.product_id = products.id
WHERE order_items.order_id = orders.id
AND products.type != 'BarProduct'
)
)
)
I would suggest using count(distinct) in joined subselect like this:
SELECT orders.*
FROM orders
inner join (
SELECT orderid, max(products.type) as products_type
FROM order_items
INNER JOIN products ON products.id = order_items.product_id
GROUP BY orderid
-- distinct count of different products = 1
-- -> all order items are for the same product type
HAVING COUNT(distinct products.type ) = 1
-- alternative is:
-- min(products.type )=max(products.type )
) as tmp on tmp.orderid=orders.orderid
WHERE 1=1
-- if you want only single type product orders for some specific product
and tmp.products_type = 'FooProduct'
This is a relational division problem.
One solution to find orders where all products are of a given type is this:
SELECT *
FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
WHERE orders.id IN (
SELECT order_items.order_id
FROM order_items
INNER JOIN products ON products.id = order_items.product_id
GROUP BY order_items.order_id
HAVING COUNT(CASE WHEN products.type = 'FooProduct' THEN 1 END) = COUNT(*)
)
Tweak the above just a little to find orders where all products are from a list of given types is this:
HAVING COUNT(CASE WHEN products.type IN ('FooProduct', 'BarProduct') THEN 1 END) = COUNT(*)
And to find all orders where all products match all types from a given list is this:
HAVING COUNT(CASE WHEN products.type IN ('FooProduct', 'BarProduct') THEN 1 END) = COUNT(*)
AND COUNT(DISTINCT products.type) = 2
DB Fiddle with tests

MYSQL Queries - combination

I need to find the first and last name of the customer who has purchased the most expensive product.
Schema
Orders
orderID
ordertypeID
customerID
quantity
purchasedate
Customers
customerID
state
postcode
streetNumber
streetAddress
customerLn
customerFn
Order_Contents
orderID
productID
quantity
Products
productID
productCategory
productName
productDescription
unitPrice
inStock
How can i write this query?
You can use sub queries as follows.
select
Customers.customerFN,
Customers.customerLN
from
Customers
inner join Orders on Orders.customerID = Customers.customerID
inner join Order_Contents on Order_Contents.orderID = Orders.orderID
where
Order_Contents.productID in
(select Products.productID from Products where Products.unitPrice = (select max(Products.unitPrice) from Products))
Something like this seems like it should work as a jumping off point (completely naive approach here)
SELECT CustomerFn, CustomerLn FROM Customers c
LEFT JOIN Orders o on o.customerID = c.customerID
LEFT JOIN Order_Contents oc on oc.orderID = o.orderID
LEFT JOIN Products p on p.productID = oc.productID
WHERE oc.productID IN (
SELECT productID FROM Products WHERE unitPrice = MAX(unitPrice)
);
select c.customerFn, c.customerLn
from customers c join orders o where o.customerID = c.customerID
join order_contents l where l.orderId = o.orderId
join products p where p.productId = l.productId
order by p.unitPrice desc
limit 1

MySQL Query get total number of guests per company

select
first_name,
last_name,
c.name as company_name,
sc.`date` as screening_date
from
guests g
inner join
user_guest_group ugs on ugs.guest_id = g.id
inner join
companies c on c.id = g.company_id
inner join
screening_date_guest sdg on sdg.guest_id = g.id
inner join
screening_dates sc on sc.id = sdg.screening_date_id
where
sdg.attending = 1
and
screening_date_id = 1
group by
first_name,
last_name
Results:
Peter, M, Bell Media (ctv), 2015-05-18 00:00:00
Adam, D, Highway Entertainment, 2015-05-18 00:00:00
Todd, F., Multichoice, 2015-05-18 00:00:00
John, D, Talpa, 2015-05-18 00:00:00
Maria, F, UK TV, 2015-05-18 00:00:00
John, L, WBDTD, 2015-05-18 00:00:00
Albert, P, WBDTD, 2015-05-18 00:00:00
My query returns that resulset.
Now, I want to see another column with total guests per company.
In this case, we have 2 guests from WBTDT so it should say total_guest = 2
Can someone help me ?
Thanks
One way to do this is to get the count per company in a correlated subquery, so maybe this is what you want?
select
first_name,
last_name,
c.name as company_name,
sc.date as screening_date,
(
select count(*) from guests
inner join
user_guest_group on user_guest_group.guest_id = guests.id
inner join
companies on companies.id = guests.company_id
inner join
screening_date_guest on screening_date_guest.guest_id = guests.id
inner join
screening_dates on screening_dates.id = screening_date_guest.screening_date_id
where
screening_date_guest.attending = 1
and
screening_date_id = 1 and company_id = c.id
) total_guests
from
guests g
inner join
user_guest_group ugs on ugs.guest_id = g.id
inner join
companies c on c.id = g.company_id
inner join
screening_date_guest sdg on sdg.guest_id = g.id
inner join
screening_dates sc on sc.id = sdg.screening_date_id
where
sdg.attending = 1
and
screening_date_id = 1
group by
first_name,
last_name,
c.id,
c.name,
sc.date
select
first_name,
last_name,
c.name as company_name,
sc.`date` as screening_date,
count(g.id)
from
guests g
inner join
user_guest_group ugs on ugs.guest_id = g.id
inner join
companies c on c.id = g.company_id
inner join
screening_date_guest sdg on sdg.guest_id = g.id
inner join
screening_dates sc on sc.id = sdg.screening_date_id
where
sdg.attending = 1
and
screening_date_id = 1
group by
first_name,
last_name,
c.name

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