join SQL query problem - mysql

I have following query which returns the product and the lowest sell price found with the quantity of that sell price. Everything works perfectly until I want to get a product that does not have any prices in the product_price table. How can I let return it the product data and NULLS for sellPrice and quantity?
SELECT p.*, MIN(pp.sellPrice) as sellPrice, pp.quantity FROM `product` as p
LEFT JOIN `product_price_group` as ppg ON ppg.productId = p.`id`
LEFT JOIN `product_price` as pp ON pp.priceGroupId = ppg.`id`
WHERE p.`id` = 1 AND p.`active` = 1
Output of an product that has a price available:
+----+--------------+--------+--------------+--------------+-----------+----------+
| id | name | active | sortSequence | creationDate | sellPrice | quantity |
+----+--------------+--------+--------------+--------------+-----------+----------+
| 1 | product_id_1 | 1 | 1 | 1287481220 | 22.00 | 10 |
+----+--------------+--------+--------------+--------------+-----------+----------+
Output of an product that does not have a pricing avaialble
+----+------+--------+--------------+--------------+-----------+----------+
| id | name | active | sortSequence | creationDate | sellPrice | quantity |
+----+------+--------+--------------+--------------+-----------+----------+
| NULL | NULL | NULL | NULL | NULL | NULL | NULL |
+----+------+--------+--------------+--------------+-----------+----------+
Desired output:
+----+--------------+--------+--------------+--------------+-----------+----------+
| id | name | active | sortSequence | creationDate | sellPrice | quantity |
+----+--------------+--------+--------------+--------------+-----------+----------+
| 2 | product_id_2 | 1 | 1 | 1287481220 | NULL | NULL |
+----+--------------+--------+--------------+--------------+-----------+----------+
Update
It seems that I was selecting oN product items that don't exist! Very stupid.

What about using LEFT OUTER JOIN for product_price table?
SELECT p.*, MIN(pp.sellPrice) as sellPrice, pp.quantity FROM `product` as p
LEFT JOIN `product_price_group` as ppg ON ppg.productId = p.`id`
LEFT OUTER JOIN `product_price` as pp ON pp.priceGroupId = ppg.`id`
WHERE p.`id` = 1 AND p.`active` = 1
Is this what you want?
UPDATE: Revision - Like others say, LEFT JOIN = LEFT (OUTER) JOIN so it will not help you in this case...

I MAY be incorrect, but my understanding of a LEFT JOIN has always been the table reference in the equality test as written in the SQL statement... which is in addition, how I write queries... Start with the table I'm expecting FIRST (left), joined to the OTHER (right) table second... Keep the join condition ALSO respective of that relationship...
select from x left join y where x.fld = y.fld
instead of
select from x left join y where y.fld = x.fld
So I would adjust your query as follows
SELECT
p.*,
MIN(pp.sellPrice) as sellPrice,
pp.quantity
FROM
product as p
LEFT JOIN product_price_group as ppg
ON p.id = ppg.productId
LEFT JOIN product_price as pp
ON ppg.id = pp.priceGroupId
WHERE
p.id = 1
AND p.active = 1
Additionally, you can wrap your min() and quantity with a IFNULL( field, 0 ) to prevent NULLS from showing but instead have actual zero values.

Related

How do I join multiple (four) tables using sql with conditions?

I am trying to create an SQL query that conditionally pulls data from multiple tables.
I have four tables:
orders
+------+------------+------------+
| id | date_added | currency |
+------+------------+------------+
| 1 | 2018-07-23 | 1 |
+------+------------+------------+
order_items
+------+------------+------------+---------------+---------------+
| id | order_id | price | product_id | product_type |
+------+------------+------------+---------------+---------------+
| 1 | 1 | 100.00 | 1 | ticket |
+------+------------+------------+---------------+---------------+
order_data
+------+--------------+---------------+
| id | order_id | ext_order_ref |
+------+--------------+---------------+
| 1 | 1 | ABC |
+------+--------------+---------------+
products
+------+------------+------------+
| id | date | product_id |
+------+------------+------------+
| 1 | 2020-03-12 | 1 |
+------+------------+------------+
| 2 | 2020-03-18 | 2 |
+------+------------+------------+
| 3 | 2020-03-20 | 3 |
+------+------------+------------+
I need to output orders with the following conditions:
Each order in a row with total (calculated from order items with matching order id)
The 'ext_order_ref' from the order_data table that matches that order
Only include order items that have a specific product type
Only include orders with products from a particular date range
Preferred output would look like this:
+------------+------------+--------------+
| order_id | total | ext_order_ref|
+------------+------------+--------------+
| 1 | 100 | ABC |
+------------+------------+--------------+
My current query is basically like this; please advise
SELECT
orders.id as order_id,
SUM(order_items.price) as total,
order_data.ext_order_ref
FROM orders
INNER JOIN order_data
ON orders.id = order_data.ext_order_ref
RIGHT JOIN order_items
ON orders.id = order_items.order_id
LEFT JOIN products
ON order_items.product_id = products.product_id
WHERE order_items.product_type = 'ticket' AND products.date BETWEEN '2020-03-12' AND '2020-03-18'
GROUP BY orders.id
It almost works, but not quite. The date particularly is causing issue.
Thanks in advance!
First decide the driving table for the query and form the query based on the driving table.
Driving table is the one primary table from which other tables join.
More information on driving tables from askTom
In your case, the driving table is Orders table. You are switching between RIGHT OUTER JOIN and LEFT OUTER JOIN. This will cause confusion in the resultset.
I have modified the query. See whether it works.
SELECT
orders.id as order_id,
SUM(order_items.price) as total,
order_data.ext_order_id
FROM orders
INNER JOIN order_data
ON orders.id = order_data.ext_order_id
LEFT OUTER JOIN order_items
ON orders.id = order_items.order_id
LEFT OUTER JOIN products
ON order_items.product_id = products.product_id
WHERE order_items.product_type = 'ticket' AND products.date BETWEEN '2020-03-12' AND '2020-03-18'
GROUP BY orders.id
I don't understand why you would want outer joins at all. If I follow the conditions correctly:
SELECT o.id as order_id,
SUM(oi.price) as total,
od.ext_order_id
FROM orders o INNER JOIN
order_data od
ON o.id = od.ext_order_id INNER JOIN
order_items oi
ON o.id = oi.order_id INNER JOIN
products p
ON oi.product_id = p.product_id
WHERE oi.product_type = 'ticket' AND
p.date >= '2020-03-12' AND
p.date < '2020-03-19'
GROUP BY o.id, od.ext_order_id;
Note the use of table aliases so the query is easier to write and read.

Mysql complex select query from 4 tables

I research almost 200 example pages about mysql complex queries but stuck in it.
This is my stucture
Table name: zones
zoneId | zoneName
------------------
Table name: customers
customesId | zoneId | customerName
----------------------------------
Table name: products
productId | productName
-----------------------
Table name: sales
sid | zoneId | customerId | productId | amount
----------------------------------------------
Is it possible to get the following output only with the query?
zoneName | customerName | productName | amount(SUM)
---------------------------------------------------
ZoneX | customerA | productName_1 | 10
| | productName_2 | 0
| | productName_3 | 4
| | productName_4 | 0
ZoneX | customerB | productName_1 | 7
| | productName_2 | 0
| | productName_3 | 4
| | productName_4 | 3
.......
I want to get as "0" even customer or product has no sale
I tried:
SELECT zones.zoneName
, customers.customerName
, products.productName
, SUM(amount) AS amount
FROM customers
INNER JOIN zones
ON customers.zoneId = zones.zoneId
LEFT JOIN sales
ON customers.customerId = sales.customerId
LEFT JOIN products
ON sales.productId = products.productId
You need to cross join all the customers to the products so that each customer has every product listed regardless of sale.
SELECT z.zoneName
, c.customerName
, p.productName
, SUM(coalesce(s.amount,0)) AS amount
FROM customers c
INNER JOIN zones z
ON c.zoneId = z.zoneId
CROSS JOIN PRODUCTS P
LEFT JOIN sales S
ON c.customerId = s.customerId
and s.productID = p.productID
GROUP BY z.zoneName
, c.customerName
, p.productName
You can try this query
SELECT c.zoneId ,c.customesId ,c.customerName,IF(s.amount IS NULL, 0 , s.amount)
FROM customers AS c, products AS p
LEFT JOIN sales AS s ON s.productId = p.productId and s.customersid = c.customersid
Hope this helps.

Mysql query with multiple join

I have the following tables and would like to get the result as follow
Table po
ID | Date
1 | 20-Jun-2016
Table podetails
ID | poid | itemcode | quantity
1 | 1 | SOAP123 | 100
Table poreceived
ID | poid | itemcode | quantity
1 | 1 | SOAP123 | 20
2 | 1 | SOAP123 | 60
Result should be:
PO | Date | itemcode | quantity
1 | 20-Jun-2016 | SOAP123 | 80
What I have done is:
SELECT
po.id, podetails.itemcode, poreceived.quantity
FROM
po
LEFT JOIN
podetails ON podetails.poid = po.id
LEFT JOIN
poreceived ON poreceived.poid = podetails.poid
But the result is not what I expected.
Any help would be appreciate it.
It looks like you need to do an aggregation of the poreceived table by poid. You can use a subquery for this:
SELECT po.id, COALESCE(podetails.itemcode, 'NA'), COALESCE(t.quantity, 0)
FROM po
LEFT JOIN podetails
ON podetails.poid = po.id
LEFT JOIN
(
SELECT poid, itemcode, SUM(quantity) AS quantity
FROM poreceived
GROUP BY poid, itemcode
) t
ON t.poid = podetails.poid AND t.itemcode = podetails.itemcode
Other than the aggregation problem, your query strategy looked correct. I also added COALESCE to the columns from the podetails and poreceived tables in case the id from po does not match to anything.

How to mysql distinct one of many count fields when GROUP BY doesn't work?

I've a database with products, manufactors and categories of this products and information about name of manufactors, products and if those products have images.
But in this database are duplicated products with different IDs. Only thing I can identify them is their name.
Now I've a query where I want see how many products per manufactorer and per category were in my database and count also the number of products with images.
-----------------------------------------------------
| manufactor | category | products | productsImages |
|------------|----------|----------|----------------|
| manu-1 | cat-1 | 5 | 3 |
|------------|----------|----------|----------------|
| manu-1 | cat-2 | 15 | 8 |
|------------|----------|----------|----------------|
| manu-2 | cat-1 | 11 | 0 |
|------------|----------|----------|----------------|
| manu-3 | cat-2 | 5 | 4 |
|------------|----------|----------|----------------|
| manu-3 | cat-3 | 9 | 4 |
|------------|----------|----------|----------------|
My approach looks like:
SELECT m.`name` AS manufactor,
pg.`name` AS category,
COUNT(p.`name`) AS products,
COUNT(pi.`idImage`) AS productsImages
FROM `product` AS p
LEFT JOIN `product_image` AS pi ON pi.`idProduct` = p.`id`
INNER JOIN `manufacturer` AS m ON m.`id` = p.`idManufacturer`
INNER JOIN `product_groupname` AS pg ON pg.`id` = p.`idProductGroup`
GROUP BY p.`idManufacturer`, p.`idProductGroup`
ORDER BY m.`name`, p.`idProductGroup`;
I can't group by p.name because then I'd get a result row for each product.
Do COUNT(DISTINCT(p.name)) didn't help either.
So any suggestions or will I have do to subqueries?
If you want to count the distinct names where pi.`idImage` IS NOT NULL you can use the following:
SELECT m.`name` AS manufactor,
pg.`name` AS category,
COUNT(DISTINCT p.`name`) AS products,
COUNT(DISTINCT CASE
WHEN pi.`idImage` IS NOT NULL THEN p.`name`
ELSE NULL
END) AS productsImages
FROM `product` AS p
LEFT JOIN `product_image` AS pi ON pi.`idProduct` = p.`id`
INNER JOIN `manufacturer` AS m ON m.`id` = p.`idManufacturer`
INNER JOIN `product_groupname` AS pg ON pg.`id` = p.`idProductGroup`
GROUP BY p.`idManufacturer`, p.`idProductGroup`
ORDER BY m.`name`, p.`idProductGroup`;
See comments on question - distinct on p.name is the solution.
SELECT m.`name` AS manufactor,
pg.`name` AS category,
COUNT(DISTINCT(p.`name`)) AS products,
COUNT(pi.`idImage`) AS productsImages
FROM `product` AS p
LEFT JOIN `product_image` AS pi ON pi.`idProduct` = p.`id`
INNER JOIN `manufacturer` AS m ON m.`id` = p.`idManufacturer`
INNER JOIN `product_groupname` AS pg ON pg.`id` = p.`idProductGroup`
GROUP BY p.`idManufacturer`, p.`idProductGroup`
ORDER BY m.`name`, p.`idProductGroup`;

MySQL subselect in query

I would like to get the following results from my query:
id_product_attribute | id_product | reference | name | total
12 | 1 | 234235 | product_name | 2
14 | 2 | 235435 | product_name | 7
16 | 3 | 235325 | product_name | 4
etc
but when I use this query:
select pa.id_product_attribute, p.id_product, pa.reference, cl.name, sum(od.product_quantity) as total
from ps_product_attribute pa
left join ps_order_detail od on od.product_attribute_id = pa.id_product_attribute
left join ps_product p on pa.id_product = p.id_product
left join ps_category_product cp on cp.id_product = p.id_product
left join ps_category_lang cl on cp.id_category = cl.id_category
where cp.id_category = 141 and cl.id_lang = 6;
it gives me only this results:
id_product_attribute | id_product | reference | name | total
12 | 1 | 234235 | product_name | 13
so in the 'total' column it shows me the total of all, instead of seperate per row.
can anyone tell me what I'm doing wrong in my query?
Your source data would help, but at the very least you forgot to GROUP BY at the end. With the current query, you should add;
GROUP BY pa.id_product_attribute, p.id_product, pa.reference, cl.name
With MySQL, you can choose to GROUP BY less columns and get a random selection of values in the other ones, but if possible, you should GROUP BY all columns that don't have aggregates (like SUM in this case) on them.
Aggregate functions should be used with GROUP BY CLAUSE