My issue is that I would like to query several different columns from 2 different tables, it is for a search feature where I want the results to show a mix of categories and products in the array.
Correct me if Im wrong, but I cant use JOIN because the tables are not related in any way.
I cant use UNION because there is no consistency in the amount of columns I want choose from per table.
And I tried simply selecting the combination of columns from the combination of tables but that multiplies everything. However Ill show you the code for the last option as perhaps there is a way of tweaking it to get it to work...
SELECT pd.product_id, pd.name, pd.meta_keyword, pd.description, cd.category_id,
cd.name AS 'category_name'
FROM oc_product_description AS pd, oc_category_description AS cd
WHERE pd.name LIKE '%$word%'
OR cd.name LIKE '%$word%';
This returns the entire list of oc_category_description category_name values for every successful hit of pd.name whereas I ONLY want successful results of either the product name or the category name.
If you want UNION, you have to add all columns from the various tables and NULL values in the columns that are not supplied in other tables:
SELECT pd.product_id, -- pd columns
pd.name ,
pd.meta_keyword,
pd.description,
NULL AS category_id, -- cd columns
NULL AS category_name
FROM oc_product_description as pd
WHERE pd.name LIKE '%$word%'
UNION ALL
SELECT NULL,
NULL,
NULL,
NULL,
cd.category_id ,
cd.name AS category_name
FROM oc_category_description as cd
WHERE cd.name LIKE '%$word%' ;
Related
I have a table containing attributes with the following structure:
id: bigint unsigned autoincrement
product_id: bigint foreign key
attribute_id: bigint foreign key
value: varchar(100)
I can query one criteria in the following fashion:
SELECT DISTINCT product_id FROM product_attributes WHERE attribute_id = ? AND value = ?
However I need to be able to find products that match multiple such criteria and would like to avoid multiple database queries for performance reasons. Simply adding more criteria with AND won't work since they will involve the same columns so for example:
SELECT DISTINCT product_id FROM product_attributes WHERE attribute_id = 1 AND value = 'Blue'
INTERSECT
SELECT DISTINCT product_id FROM product_attributes WHERE attribute_id = 2 AND value = '36'
INTERSECT
SELECT DISTINCT product_id FROM product_attributes WHERE attribute_id = 3 AND value = 'slim'
I have read about the INTERSECT statement which seems like it might work but I've read that MySQL doesn't support it, a search through MySQL 8 documentation produced no relevant result and the query above which I assume is correct produces an error on MySQL.
I've also read that something similar could be achieved with an inner join, but all the examples I've found involve multiple tables. There might also be an even better or simpler way to write the query that hasn't occurred to me. Or perhaps it's actually better to just send multiple queries and calculate the intersection outside of MySQL (though I would be very surprised) I appreciate greatly any help from anyone who has done something similar in the past.
You need to use aggregation to count the number of matching rows to the set of conditions and assert that it is equal to the number of conditions:
SELECT product_id
FROM product_attributes
WHERE (attribute_id, value) IN ((1, 'Blue'), (2, '36'), (3, 'slim'))
GROUP BY product_id
HAVING COUNT(*) = 3
This is the key / value store problem.
It's a slight pain in the neck to do what you want. Use JOIN operations to pivot the values into a row. Like this.
SELECT p.product_id,
color.value AS color,
size.value AS size,
cut.value AS cut
FROM ( SELECT DISTINCT product_id FROM product_attributes ) p
LEFT JOIN product_attributes color ON color.product_id = p.product_id
AND color.attribute_id = 1
LEFT JOIN product_attributes size ON size.product_id = p.product_id
AND size.attribute_id = 2
LEFT JOIN product_attributes cut ON cut.product_id = p.product_id
AND cut.attribute_id = 3
This generates a resultset with one row per product/color/size/cut combination
Then you can filter that resultset like this
SELECT *
FROM (
SELECT p.product_id,
color.value AS color,
size.value AS size,
cut.value AS cut
FROM ( SELECT DISTINCT product_id FROM product_attributes ) p
LEFT JOIN product_attributes color ON color.product_id = p.product_id
AND color.attribute_id = 1
LEFT JOIN product_attributes size ON size.product_id = p.product_id
AND size.attribute_id = 2
LEFT JOIN product_attributes cut ON cut.product_id = p.product_id
AND cut.attribute_id = 3
) combinations
WHERE color='Blue' AND size='36' AND cut='slim'
MySQL's query planner is smart enough that this doesn't run as slowly as you might guess, given the proper indexes.
The FROM clause generates a comprehensive list of product ids, from your product_attributes table to join to the specific attributes. If you have some other table for products, use that instead of the SELECT DISTINCT....
I'm creating a product filter for e-commerce store. I have a product table, characteristics table and a table in which I store product_id, characteristic_id and a single filter value.
shop_products - id, name
shop_characteristics - id, values (json)
shop_values - product_id, characteristic_id, value
I can build a query to get all the products by a single value like this:
SELECT `p`.* FROM `shop_products` `p`
LEFT JOIN `shop_values` `fv` ON `p`.`id` = `fv`.`product_id`
WHERE ((`fv`.`characteristic_id`=3) AND (`fv`.`value`='outdoor'))
It works fine. Also, I can modify this query and get all the products by multiple values that belong to the very same characteristics group (have identical characteristics_id) like this:
SELECT `p`.* FROM `shop_products` `p`
LEFT JOIN `shop_values` `fv` ON `p`.`id` = `fv`.`product_id`
WHERE ((`fv`.`characteristic_id`=3) AND (`fv`.`value`='outdoor'))
OR ((`fv`.`characteristic_id`=3) AND (`fv`.`value`='indoor'))
but when I try to create a query for multiple conditions with different characteristic_id I get nothing
SELECT `p`.* FROM `shop_products` `p`
LEFT JOIN `shop_values` `fv` ON `p`.`id` = `fv`.`product_id`
WHERE ((`fv`.`characteristic_id`=3) AND (`fv`.`value`='outdoor'))
AND ((`fv`.`characteristic_id`=5) AND (`fv`.`value`='white'))
My guess it does not work because of AND operator that I am using wrong in this case due to there are no records in shop_values table that have both characteristic_id 3 and 5.
So my question is how to combine or modify my query to get all related products or maybe it is a flaw to store data like this and I need to create a different kind of shop_values table?
Use aggregation. You can also use tuples with the in clause. So:
SELECT p.*
FROM shop_products p JOIN
shop_values v
ON p.id = v.product_id
WHERE (v.characteristic_id, v.value) IN ( (3, 'outdoor'), (5, 'white'))
GROUP BY p.id
HAVING COUNT(DISTINCT v.characteristic_id) = 2;
Notes:
Unnecessarily escaping column and table aliases (with backticks) just makes the query harder to write and to read.
In general, using SELECT p.* and GROUP BY p.id is really, really bad form. The one exception is when you are grouping by a unique or primary key. This latter form is actually supported in the ANSI standard.
A LEFT JOIN is not needed. You need to find matches between the tables for the logic to work.
The use of AND and OR is fine for the WHERE clause. MySQL happens to support tuples with IN, which somewhat simplifies the logic.
I have three tables (SQLFiddle with tables created)
Orange text is what I need to get by comparing Products.name with Filters.filter.
I figured out that substring match can be done like this:
on Products.name LIKE CONCAT('%',Filters.filter,'%');
I only need the first filter match to be used. So "Mushroom soup" will match "Soup" and not "Mushroom".
What is best approach for this task?
A tested SQLFiddle link, if possible, please.
What I tried: (feel free to skip everything below)
Tried double join:
update Products
left join Filters on Products.name LIKE CONCAT('%',Filters.filter,'%')
join Categories on Filters.category_name = Categories.name
set Products.category_id = Categories.id, Products.filter = Filters.filter;
But this doesn't result in first match being used, because first join returned all filter matches (multiple lines per 1 product), and after second join categories are from last match.
For example: Mushroom soup got into "Mushroom-Ingredients" filter\category, instead of "Soup\Meals".
Also tried join + order by:
select Products.name, Filters.*
from Products
left join Filters on Products.name LIKE CONCAT('%',Filters.filter,'%')
group by Products.name;
This returns the first match per product as I needed, but I can't do a second left-join in the same query, neither can I get "group by" to work after "update" function instead of "select".
For when sqlfiddle collapses:
CREATE TABLE Products
(
`name` varchar(30),
`category_name` varchar (30),
`category_id` int
);
INSERT INTO Products (`name`)
VALUES ('Mushrooms'), ('Can of mushrooms'),
('Can of soup'), ('Mushroom soup'),
('Tomato soup'), ('Tomato');
CREATE TABLE Filters
(
`filter` varchar(30),
`category_name` varchar(30)
);
INSERT INTO Filters (`filter`, `category_name`)
VALUES ('Can of', 'Canned food'), ('Soup', 'Meals'),
('Mushroom', 'Ingredients'), ('Tomato', 'Ingredients');
CREATE TABLE Categories
(
`id` int,
`name` varchar(30)
);
INSERT INTO Categories (`id`, `name`)
VALUES (1, "Canned food"), (2, 'Ingredients'), (3, 'Meals');
You made the task quite easy with your sqlfiddle as well as your attempt to solve the problem with Select query.
It works the way you want I guess and all I need to do is to add one more left join with category table (IDK why you were unable to join Category as it's working properly).
So. I've edited your select query as follows:
select Products.name, Filters.*,Categories.id from Products
left join Filters
on Products.name LIKE CONCAT('%',Filters.filter,'%')
left join Categories
on Categories.name = Filters.category_name
GROUP BY Products.name;
You will get the results you want with this query.
Now, in order to update Products table with the result of this query, you can do following:
update Products
left join Filters
on Products.name LIKE CONCAT('%',Filters.filter,'%')
left join Categories
on Categories.name = Filters.category_name
set Products.category_name = Filters.category_name,
Products.category_id = Categories.id;
Click here for Working Demo
Hope it helps!
I am trying to fetch some combined result from two separate individual tables.
The transaction_fact table has around 3.6 million rows and translation_table has around 300000 rows.
Now i want a sum of amount for all transactions grouped by location and the product within that location. But as the fact table has only location id and product id and i would like the names in the result , I am using sub query.
My query is as follows:
SELECT
( SELECT translation
FROM translation_table
WHERE dim_name LIKE 'location_dim'
AND lang_id LIKE 'es'
AND dim_id LIKE CAST(o.loc_id AS CHAR(50))
AND field_name LIKE 'city') AS Location
, ( SELECT product_name
FROM prod_dim
WHERE prod_id = o.prod_id) AS Product
, SUM(amount)
FROM transaction_fact o
GROUP
BY loc_id
, prod_id
ORDER
BY loc_id
, prod_id;
But this query is not returning anything , just keeps on processing.
I waited for about one and half hour but still no result.
Please tell me what might be going wrong.
Joining the tables should eliminate the need for subqueries and give some performance boost. If not you may need to provide more details on the table structure before we can help. Something like this should get you started:
SELECT t.translation AS Location, p.product_name AS Product, SUM(o.amount) AS Total
FROM transaction_fact o
INNER JOIN translation_table t ON CAST(o.loc_id AS char(50)) = t.dim_id
INNER JOIN prod_dim p ON p.prod_id = o.prod_id
WHERE t.dim_name = 'location_dim'
AND t.lang_id = 'es'
AND t.field_name = 'city'
GROUP BY t.translation, p.product_name
ORDER BY o.loc_id, o.prod_id;
Notes: I've changed the LIKEs to =, as LIKE is for when you want to match on a pattern that includes wildcards.
The CAST that is used in the join to translation_table is not ideal. If you could do away with that you'd get better performance.
So here is a sample query illustrating a simplified version of the problem I am having:
SELECT max_discount, product_name
FROM products
LEFT JOIN (
SELECT IF(MAX(discount_percentage) IS NOT NULL, MAX(discount_percentage), 5) AS max_discount,
product_id,
order_id
FROM orders
LEFT JOIN discounts ON discounts.id = orders.discount_id
GROUP BY order_id
) AS alias ON alias.product_id = products.product_id
GROUP BY alias.order_id
In this case, I can have multiple "discounts" (vouchers) related to the same order. But if there are multiple discounts related to a single order, I only want to actually apply the highest valued discount only. For example if you have a voucher for 50% off and another voucher for 20% off, in this case they would both be stored in the database. But I don't want to give you 70% off, I want to give you 50% off (the higher of the two). Thus, the subquery that selects the MAX() value.
If you don't have any discount vouchers, I still want to give you 5% off, because that's our promotion right now. Therefore the other part of the IF tries to set it to 5.
The problem here is that, even though my condition says MAX() IS NOT NULL, and I am trying to set it to 5 when it is NULL, it still comes up as NULL in the results.
When I do an IF condition with a simple field that can contain a NULL value, for example SELECT name, IF(age IS NULL, 'Unknown', age) AS age FROM people, then it works as expected. So why doesn't it work with a MAX() subquery?
Have you tried the COALESCE function - returns the first non-null value :
SELECT COALESCE(MAX(discount_percentage), 5) ......
You want the COALESCE() at the outer level. But you have a bigger problem in the query, because product_id is not in the subquery.
SELECT COALESCE(max_discount, 5), product_name
FROM products p LEFT JOIN
(SELECT MAX(discount_percentage) AS max_discount,
product_id, order_id
FROM orders o LEFT JOIN
discounts d
ON d.id = o.discount_id
GROUP BY o.order_id, o.product_id
) od
ON od.product_id = p.product_id
GROUP BY od.order_id
I'm still not sure this is correct, because the structure seems odd -- discounts on orders and not on products. Aggregating by order_id in the outer query, but not including that in the SELECT. Without sample data and desired results, it is hard to tell what you really need.