mysql INNER/LEFT JOIN with UNION ALL - mysql

I have a question regarding the use of UNION ALL and INNER JOIN.
I found some posts regarding this issue, for example here or there but I did not manage to apply it to my issue.
What I am trying to do is to list 1) the products and 2) the difference between orders and deliveries.
I have 3 table:
product (id, name)
orders (id, product, value)
deliveries (id, product, value)
I managed to get (almost) what I want using the following:
SELECT product
, sum(total)
FROM (
SELECT product
, SUM(value) as total
FROM orders
GROUP BY product
union all
SELECT product
, -1 * SUM(value) as total
FROM deliveries
GROUP BY product)
as alias
GROUP BY product
ORDER BY sum(total) DESC
I obtain the following:
1 23
2 33
When I would like to get:
computer 23
car 33
Meaning the product name instead of the product id.
Anyone would have a solution for that? (my INNER JOIN OR LEFT JOIN attempts failed so far)
Thanks and regards

Join in the product table:
SELECT p.name, sum(total)
FROM (SELECT o.product, SUM(o.value) as total
FROM orders o
GROUP BY o.product
union all
SELECT d.product, -1 * SUM(d.value) as total
FROM deliveries d GROUP BY d.product
) od JOIN
product p
on od.product = p.id
GROUP BY p.name
ORDER BY sum(total) DESC

Related

mysql how to return 0 if category is not sold in specific month

im trying to return a list of categories sold by month.
My query:
select count(*) counter, monthname(sales.date) month_name, sales.date, models.name, models.code, models.price, models.size, models.quantity, models.image, models.image_type, models.image_name, categories.name, categories.color from sales inner join models on models.code = sales.code inner JOIN categories on models.category_id = categories.id where year(sales.date) = year(curdate()) GROUP by month(sales.date), categories.name
My result:
This work well but as you can see for month "January" category "Sandaletti" is missing.. but i want to return 0 in counter when this appear like counter 0 month_name January name SANDALETTI
I don't know how i can do.. anyone can help please?
Try something like this, starting by selecting all your categories and then applying the aggregated totals for each.
Edited after you tagged as mysql
select x.*, c.name, c.color /* You may need to specify individual columns in order to use isnull() for where there are no category results */
from categories c
left join (
select Count(*) counter, monthname(s.date) month_name, s.date,
m.name, m.code, m.price, m.size, m.quantity, m.image,
m.image_type, m.image_name, m.category_id
from sales s inner join models m on s.code = m.code
where Year(s.date) = Year(curdate())
group by Month(s.date), m.category_id
)x on x.category_id=c.id

SQL counting customers that have ordered the same product at least 7 times

I have the following tables
– Price (prodID, from, price)
– Product (prodID, name, quantity)
– PO (prodID, orderID, amount)
– Order (orderID, date, address, status, trackingNumber, custID, shipID)
– Shipping (shipID, company, time, price)
– Customer (custID, name)
– Address (addrID, custID, address)
I would like to find the names of customers who have bought the same item at least 7 times, if they bought the same item twice in one order I would like to count it as one. Here is my code:
SELECT C.name, COUNT(DISTINCT p.prodId) as prod_count
FROM Product P
INNER JOIN PO
ON PO.prodId = P.prodId
INNER JOIN "Order" O
ON O.orderId = PO.orderId
INNER JOIN Customer C
ON C.custId = O.custId
GROUP BY c.name
HAVING COUNT(DISTINCT p.prodId) > 6;
However, this is returning the number of unique products each customer has ordered which is not what I am looking for.
We can try using two levels of aggregation here. The first level of aggregation is by customer, order, and product, and removes duplicates should a given customer order the same product more than once within a single order. The next level of aggregation is only by customer and product, and it retains only customers who have at least one product which they purchased 7 or more times across different orders. Finally, we do a distinct select to retain each unique matching customer name.
WITH cte1 AS (
SELECT c.name, o.orderId, p.prodId
FROM Customer c
INNER JOIN "Order" o ON o.custId = c.custId
INNER JOIN PO po ON po.orderId = o.orderId
INNER JOIN Product p ON p.prodId = po.prodId
GROUP BY c.name, o.orderId, p.prodId
),
cte2 AS (
SELECT name, prodId
FROM cte1
GROUP BY name, prodId
HAVING COUNT(*) >= 7
)
SELECT DISTINCT name
FROM cte2;
As far as I see , there is no need of joining with Product table,unless you need Product name in your result.
;with CTE as(
SELECT orderId,prodId, COUNT(*) as OrderProd_Count
FROM dbo.PO
GROUP BY orderId,prodId
)
,CTE1 as(
SELECT prodId,count(*) as Prod_Count
from CTE
group by prodId
having count(*)>7
)
select c1.productid,ca.Name from CTE1 C1
inner join CTE C on c1.prodId=c1.prodId
inner join dbo.Order O on c.orderid=O.orderId
inner join Customer C on o.custid=C.custid

How to fetch all orders excluding very first order of each customer in mysql

Here's my orders table:
I want to select all orders excluding very first order of each customer (if customer has placed multiple orders).
So if a customer e.g. 215 has total 8 orders, then I will select his all last 7 orders excluding his very first order 70000 which was placed on 10 July 2017.
But if a customer e.g. 219 had placed only one order 70007, it must be selected by the query.
Using an anti-join approach:
SELECT o1.order_id, o1.customer_id, o1.order_date, o1.order_value
FROM orders o1
LEFT JOIN
(
SELECT customer_id, MIN(order_date) AS min_order_date, COUNT(*) AS cnt
FROM orders
GROUP BY customer_id
) o2
ON o1.customer_id = o2.customer_id AND
o1.order_date = o2.min_order_date
WHERE
o2.customer_site = 1 AND
(o2.customer_id IS NULL OR
o2.cnt = 1);
The idea here is to try to match each record in orders to a record in the subquery, which contains only first order records, for each customer. If we can't find a match, then such an order record cannot be the first.
You can try below -
select order_id,customer_id,order_date,order_Value
from tablename
group by order_id,customer_id,order_date,order_Value
having count(order_id)=1
union all
select order_id,customer_id,order_date,order_Value
from tablename a where order_date not in (select min(order_date) from tablename b
where a.customer_id=b.customer_id)
Solution
Dear #Tim Biegeleisen, your answer almost done. just add HAVING COUNT(customer_id)>1
So the query is below:
SELECT o1.order_id, o1.customer_id, o1.order_date, o1.order_value
FROM orders o1
LEFT JOIN (
SELECT customer_id, MIN(order_date) AS min_order_date
FROM orders
GROUP BY customer_id
HAVING COUNT(customer_id)>1
) o2
ON o1.customer_id = o2.customer_id AND
o1.order_date = o2.min_order_date
WHERE
o2.customer_id IS NULL;

LEFT JOIN with multiple nested select

The following statement surprisingly works, but I'm not sure joining the same table 3 times is efficient. I had to disable ONLY_FULL_GROUP_BY in order for it to work.
There are 2 tables in play. One is the main table with Distributor information, the second is a table of purchases that contains the amount, date, and id of the associated Distributor in the main table (assoc).
There are 3 things I needed. Year to date sales, which SUMS the amount of a certain Distributor's sales from the current year. Last year sales, which does the same for the previous year. Then finally just get the latest purchase date and amount.
The user needs to be able to filter by these values (lys, ytd, etc...) so joining them as variables seems like the way to go. The DB size is about 7,000 records.
SELECT
d.*,
ytd_total,
lys_total,
last_amount,
last_purchase
FROM Distributor as d
LEFT JOIN (
SELECT
assoc, SUM(amount) ytd_total
FROM purchases
WHERE db = 1 AND purchase_date >= '{$year}-01-01'
GROUP BY assoc
) AS ytd
ON ytd.assoc = d.id
LEFT JOIN (
SELECT
assoc, SUM(amount) lys_total
FROM purchases
WHERE db = 1 AND purchase_date BETWEEN '{$lyear}-01-01' AND '{$lyear}-12-31'
GROUP BY assoc
) AS lys
ON lys.assoc = d.id
LEFT JOIN (
SELECT
assoc, amount last_amount, purchase_date last_purchase
FROM purchases
WHERE db = 1
GROUP BY assoc
) AS lst
ON lst.assoc = d.id
WHERE ........
You can do more work in each aggregation query. I think this is more whatyou want:
select d.*, pa.ytd_total, pa.lys_total, pa.last_purchase_date, p.amount
from distributor d left join
(select p.assoc,
sum(case when p.purchase_date >= '{$year}-01-01' then p.amount end) as ytd_total,
sum(case when p.purchase_date BETWEEN '{$lyear}-01-01' AND '{$lyear}-12-31' then p.amount end) as lys_total,
max(p.purchase_date) as last_purchase_date
from purchases p
where p.db = 1
group by p.assoc
) pa left join
purchases p
on pa.assoc = p.assoc and pa.last_purchase_date = p.purchase_date;

Select from 3 tables with two order by before two group by

I try to get a list of products with each newest and lowest offer price
Table product:
id | name
Table offer:
id | product_id | price | created | dealer_id
Table invalids:
id | offer_id | status
I have tried:
SELECT * FROM product INNER JOIN
(
SELECT offer.product_id , offer.price
FROM offer
LEFT JOIN invalids
ON offer.id = invalids.offer_id
WHERE invalids.id IS NULL
GROUP BY offer.dealer_id
ORDER BY offer.created DESC
) o
ON o.product_id = product.id
ORDER BY product.name
I have tried an sqlfiddle http://sqlfiddle.com/#!9/32658/3 with this offer values:
(`id`, `price`, `dealer_id`, `product_id`, `created`)
(1,12.60,1,1,'2015-05-17 08:44:45'),
(2,13.00,1,1,'2015-08-17 08:44:45'),
(3,20.00,1,1,'2015-08-17 08:45:30'),
(4,10.00,1,1,'2015-08-17 08:45:46'),
(5,4.00,2,1,'2015-05-17 08:44:11'),
(6,11.00,2,1,'2015-08-17 08:44:46'),
(7,5.00,2,1,'2015-08-17 08:45:31'),
(9,110.00,2,2,'2015-08-17 08:46:58'),
(10,11.00,2,2,'2015-08-17 08:47:12');
Expected value for product ID 1 is offer ID 7 with price 5.
These steps I think I must realize:
Order offers by created and group by dealer_id to get newest entries
Take result from step 1 and order it by price to get smallest price.
Make this for all products
Maybe I must use a second SELECT FROM offer with GROUP BY and ORDER BY but how do I get I the product_id from the first (outer) select?
Well I would start by getting the latest date for each product offer like this:
SELECT product_id, MAX(created) AS latestOffer
FROM offer
GROUP BY product_id;
Once you have that, you can join it to the original table to get that offer:
SELECT o.*
FROM offer o
JOIN(
SELECT product_id, MAX(created) AS latestOffer
FROM offer
GROUP BY product_id) tmp ON tmp.product_id = o.product_id AND tmp.latestOffer = o.created;
Here is an SQL Fiddle example.
This query should help you:
SELECT *
FROM product
JOIN (
SELECT product_id, min(price) as minPrice, max(created) as newestOffer
FROM offer
WHERE id NOT IN (SELECT offer_id FROM invalids)
GROUP BY 1
) as b
ON product.id = b.product_id
A shot in the dark based on what I understand you to be after...
lots of nested subqueries.. keep thinking there's got to be a better way...
SELECT OO.ID, OO.Price, OO.Dealer_Id, OO.Product_ID, OO.created, P.name
FROM Offer OO
INNER JOIN (
SELECT Min(Price) as MinP
FROM offer O
INNER JOIN (
SELECT max(OI.created) as LatestOffer, OI.Dealer_ID, OI.Product_ID
FROM Offer OI
LEFT JOIN invalids I
on OI.Id = I.offer_Id
WHERE I.ID is null
GROUP BY OI.Dealer_Id, OI.Product_Id
) B
on O.Dealer_Id = B.Dealer_Id
and O.Product_Id = B.Product_Id
and O.Created = B.LatestOffer
) Z
on OO.Price = Z.MinP
INNER JOIN product P
on P.ID = OO.Product_ID
SQL FIDDLE