create single query to fetch data from tables as seperate rows - mysql

i am thinking if there is a better way to query product, product options and product option choices from database
my database structure is like:
product table (product_id, product_name)
product_options table (option_id, option_name, product_id)
product_optionChoices (Choice_id, choice_name, Option_id)
i want a single query that return all product and options in seperate rows like:
--------------------------------------------------------------------------------
| product_id | product_name | option_id | option_name | choiceId | choice_name |
| 1 | shoes | | | | |
| 1 | | 1 | choose color| | |
| | | 1 | | 1 | red |
| | | 1 | | 2 | blue |
--------------------------------------------------------------------------------
right now i am using 3 sepearte queries to get product, product option and product_optionChoices and appending the query together using QueryAddRow in coldfusion.
Thanks

Maybe this solution helps you even if i don't like it.
This returns exactly what you want (UPDATED).
SELECT p.product_id, p.product_name, '' as option_id, '' as option_name, '' as choiceId, '' as choice_name FROM `product` p
UNION ALL
SELECT pr.product_id as product_id, '' as product_name, o.option_id, o.option_name, '' as choiceId, '' as choice_name FROM `product_options` o
INNER JOIN product pr
ON pr.product_id = o.product_id
UNION ALL
SELECT '' as product_id, '' as product_name, op.option_id as option_id, '' as option_name, c.choice_Id, c.choice_name FROM `product_optionChoices` c
INNER JOIN product_options op
ON op.option_id = c.option_id

This works for SQL Server
SELECT P.product_id, P.product_name, PO.option_id, PO.option_name, POC.choiceId, POC.choice_name
FROM product P LEFT OUTER JOIN product_options PO on P.product_id = PO.product_id
LEFT OUTER JOIN product_optionChoices POC on POC.option_id = PO.Option_id

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.

Match only if all elements of a column are in another table

I've made a little database in SQL that as 2 tables Product (Name, Ingredient and Available (Ingredient):
| Product | Available |
| Name | Ingredient | Ingredient |
| 1 | a | a |
| 1 | b | c |
| 2 | a |
| 2 | c |
I want the name of a product only if ALL its ingredients are inside the Available table.
For the previous example, the result should be: Product "2"
and not Product "1", because I don't have the ingredient "b" in the Available table.
Thanks for the help
You can try with left join (to figure out which Products don't have necessary Ingredients) and group by + having to filter Products that have at least one missing Ingredient:
select p.Name
from Products p
left join Available a on a.Ingredient = p.Ingredient
group by p.Name
having sum(a.Ingredient is null) = 0
You can try something like this also:
WITH TEMP_PRODUCTS AS
(
SELECT NAME, COUNT(1) AS NUMBER_OF_INGREDIENTS
FROM PRODUCT
GROUP BY PRODUCT
)
SELECT PRD.NAME, COUNT(1) AS NUMBER_OF_AVAILABLE_INGREDIENTS
FROM PRODUCT PRD
JOIN TEMP_PRODUCTS TMP ON PRD.NAME = TMP.NAME
WHERE EXISTS (SELECT 1
FROM INGREDIENT ING
WHERE ING.INGREDIENT = PRD.INGREDIENT)
GROUP BY PRD.NAME
HAVING COUNT(1) = TMP.NUMBER_OF_INGREDIENTS;

MYSQL GROUP_CONCAT GOT AN ERROR IN MORE THAN 2 TABLES

Hi i need to fetch the product information from the backend ..
I have a
productTable
productInformation
productAttributes
productCategories
productImages
My database query is ,
select
p.id,
p.name as entityName,
pi.name,
pi.description,
pi.language,
GROUP_CONCAT(im.image SEPARATOR ';') as images,
ci.name as categoryname
from products p,
productInformation pi,
product_attributes pa,
images im,
categoryInformation ci,
categories c
where
ci.category=c.id and
ci.language='en' and
c.id = p.category and
p.category in (1,2) and
im.entityid=p.id and
im.type='product' and
pi.language='en' and
pi.productid=p.id and
pa.attributevalueid in (2) and
p.id=pa.productid and
p.name like '%p%' and
p.price between 0 and 15 ;
The query was working fine before ,
After i added the GROUP_CONCAT in the query
There is an error
ERROR 1140 (42000): In aggregated query without GROUP BY, expression
1 of SELECT list contains nonaggregated column 'sampleCart.p.id'; this is incompatible with sql_mode=only_full_group_by
I checked with just image table and product table ,GROUP_CONCAT was working with linking these 2 tables . but this is happening after linking more than 2 tables .
QUERY WITH OUT GROUP_CONCAT
select p.id,p.name as entityName,
pi.name,
pi.description,
pi.language,
im.image,
ci.name as categoryname
from products p,
productInformation pi,
product_attributes pa,
images im,
categoryInformation ci,
categories c
where ci.category=c.id
and ci.language='en'
and c.id = p.category and p.category in (1,2)
and im.entityid=p.id and im.type='product' and pi.language='en'
and pi.productid=p.id and pa.attributevalueid in (2)
and p.id=pa.productid and p.name like '%p%' and p.price between 0 and 15 ;
RESULT WITH OUT GROUP_CONCAT
+----+------+-------+----------+----+-----------+------+---------------+----------+-------------+
| id | name | price | category | id | productid | name | description | language | image |
+----+------+-------+----------+----+-----------+------+---------------+----------+-------------+
| 1 | pen | 10 | 2 | 1 | 1 | pen | this is a pen | en | 12939.jpg |
| 1 | pen | 10 | 2 | 1 | 1 | pen | this is a pen | en | 2932929.jpg |
+----+------+-------+----------+----+-----------+------+---------------+----------+-------------+
EXPECTED RESULT
+----+------+-------+----------+----+-----------+------+---------------+----------+------------------------------------+
| id | name | price | category | id | productid | name | description | language | image |
+----+------+-------+----------+----+-----------+------+---------------+----------+------------------------------------+
| 1 | pen | 10 | 2 | 1 | 1 | pen | this is a pen | en | 12939.jpg;2983.jpg;30940.jpg |
+----+------+-------+----------+----+-----------+------+---------------+----------+------------------------------------+
Please help .
Thanks in Advance
There is few problem in your question :
1- To join the tables it's better to use Inner Join or Left Join like this :
SELECT p.id,
p.name as entityName,
pi.name,
pi.description,
pi.language,
im.image,
ci.name as categoryname
FROM products p
INNER JOIN productInformation pi ON pi.productid=p.id
INNER JOIN images im ON im.entityid=p.id
INNER JOIN categories c ON c.id = p.category
INNER JOIN categoryInformation ci ON ci.category=c.id
WHERE ci.language='en'
and p.category in (1,2)
im.type='product' and pi.language='en'
pa.attributevalueid in (2)
p.name like '%p%' and p.price between 0 and 15
It's more "readable" in my opinion and you are following the SQL recommendation.
2- In your output and the expected result, you are not using the same columns in the select
So I won't take care of your output and just according to your query :
SELECT p.id,p.name as entityName,
pi.name,
pi.description,
pi.language,
GROUP_CONCAT(im.image, ";"),
ci.name as categoryname
FROM products p
INNER JOIN productInformation pi ON pi.productid=p.id
INNER JOIN images im ON im.entityid=p.id
INNER JOIN categories c ON c.id = p.category
INNER JOIN categoryInformation ci ON ci.category=c.id
WHERE ci.language='en'
and p.category in (1,2)
im.type='product' and pi.language='en'
pa.attributevalueid in (2)
p.name like '%p%' and p.price between 0 and 15
GROUP BY p.id,
entityName,
pi.name,
pi.description,
pi.language,
categoryname
#4givN Query is fixed now
Updated Query
select p.id,p.name as entityName,pi.description,pi.language,GROUP_CONCAT(im.image SEPARATOR ';') as images,ci.name as categoryname from products p,productInformation pi,product_attributes pa,images im,categoryInformation ci,categories c where ci.category=c.id and ci.language='en' and c.id = p.category and p.category in (1,2) and im.entityid=p.id and im.type='product' and pi.language='en' and pi.productid=p.id and pa.attributevalueid in (2) and p.id=pa.productid and p.name like '%p%' and p.price between 0 and 15 group by p.id, p.name, pi.name, pi.description, pi.language,ci.name;
RESULT
+----+------------+---------------+----------+--------------+--------------+
| id | entityName | description | language | images | categoryname |
+----+------------+---------------+----------+--------------+--------------+
| 1 | pen | this is a pen | en | 12.jpg;2.jpg | laptops |
+----+------------+---------------+----------+-----------------------+-----+

MySQL Join table row based on lowest cell value

I have two tables in a MySQL database like this:
PRODUCT:
product_id | product_name
-----------+-------------
1 | shirt
2 | pants
3 | socks
PRODUCT_SUPPLIER: (id is primary key)
id | supplier_id | product_id | part_no | cost
----+---------------+--------------+-----------+--------
1 | 1 | 1 | s1p1 | 5.00
2 | 1 | 2 | s1p2 | 15.00
3 | 1 | 3 | s1p3 | 25.00
4 | 2 | 1 | s2p1 | 50.00
5 | 2 | 2 | s2p2 | 10.00
6 | 2 | 3 | s2p3 | 5.00
My goal is a query that joins the tables and outputs a single row for each product joined with all fields from the corresponding supplier row with the lowest cost like this:
product_id | product_name | supplier_id | part_no | cost
-----------+---------------+---------------+------------+---------
1 | shirt | 1 | s1p1 | 5.00
2 | pants | 2 | s2p2 | 10.00
3 | socks | 2 | s3p3 | 5.00
At present I do have the following query written which seems to work but I'd like to know from any of the more experienced SQL users if there is a cleaner, more efficient or otherwise better solution? Or if there is anything essentially wrong with the code I have?
SELECT p.product_id, p.product_name, s. supplier_id, s.part_no, s.cost
FROM product p
LEFT JOIN product_supplier s ON
(s.id = (SELECT s2.id
FROM product_supplier s2
WHERE s2.product_id = p.product_id
ORDER BY s2.cost LIMIT 1));
I would run:
select p.product_id, p.product_name, s.supplier_id, s.part_no, s.cost
from product p
join product_supplier s
on p.product_id = s.product_id
join (select product_id, min(cost) as min_cost
from product_supplier
group by product_id) v
on s.product_id = v.product_id
and s.cost = v.min_cost
I don't see the point in an outer join. Is every product is on the product_supplier table? If not then the outer join makes sense (change the join to inline view aliased as v above to a left join if that is the case).
The above may run a little faster than your query because the subquery is not running for each row. Your current subquery is dependent and relative to each row of product.
If you want to eliminate ties and don't care about doing so arbitrarily you can add a random number to the end of the results, put the query into an inline view, and then select the lowest/highest/etc. random number for each group. Here is an example:
select product_id, product_name, supplier_id, part_no, cost, min(rnd)
from (select p.product_id,
p.product_name,
s.supplier_id,
s.part_no,
s.cost,
rand() as rnd
from product p
join product_supplier s
on p.product_id = s.product_id
join (select product_id, min(cost) as min_cost
from product_supplier
group by product_id) v
on s.product_id = v.product_id
and s.cost = v.min_cost) x
group by product_id, product_name, supplier_id, part_no, cost
If for some reason you don't want the random # to come back in output, you can put the whole query above into an inline view, and select all columns but the random # from it.

join SQL query problem

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.