I'm probably approaching this the wrong way, but here's the background. I have a table of products and a table of relations between the products - so, for instance, the relationship table might look like this:
ID PRODUCT 1 PRODUCT 2
1 1 2
2 1 3
3 3 4
So, in the above example, product id 1 is related to products 2 & 3, product id 2 is related to 1 and product id 3 is related to 1 & 4 (since the relationship can be in either column 1 or 2)
So, I'm on the product page and want to find the details of products that are related to the current product. My instinct tells me to do a query like this:
SELECT p.* FROM products p
WHERE p.id IN (
(SELECT product_1 AS related FROM relations WHERE product_2 = :this_product)
UNION
(SELECT product_2 AS related FROM relations WHERE product_1 = :this_product)
)
But that gives me a syntax error because of the UNION.
So, am I doing this all wrong and should be taking a completely different approach or have I just made a minor syntax boo-boo with my query?
To get the list of products, you can do something like this:
select (case when :this_product = product_1 then product_2 else product1 end)
from relations r
where :this_product in (product_1, product_2)
Then you can join products back in.
Alternatively, use exists. It is more efficient:
select p.*
from products p
where exists (select 1
from relations r
where r.product_1 = :this_product and p.id = r.product_2
) or
exists (select 1
from relations r
where r.product_2 = :this_product and p.id = r.product_1
);
You need to remove the brackets used around union like below:
SELECT p.* FROM products p
WHERE p.id IN (
(SELECT product_1 AS related FROM relations WHERE product_2 = :this_product
UNION
SELECT product_2 AS related FROM relations WHERE product_1 = :this_product)
)
Check the fiddle here
Below is the example, you can also try this
SELECT p.*,p1.product_1
FROM products p
LEFT JOIN relations as rel
ON p.id = relational_field
WHERE p.id IN ( product_2, product_1 )
Related
I have a table named Records that shows products. I also have a table named Categories that shows the categories for each individual product (if one exists).
The Categories table is structured liked:
id category_id
-- -----------
1 1
1 3
3 1
3 2
5 4
The query I run to pull record ID and category ID(s) is:
SELECT
Records.id,
(SELECT
GROUP_CONCAT(C.category_id)
FROM `Categories` C
WHERE Records.id = C.id) AS 'CategoryName'
FROM
Records
The output will return:
id CategoryName
-- ------------
1 1,3
2 NULL
3 1,2
4 4
5 NULL
I have an area of my website where users can filter records by category. Let's say user wants to filter for category = 1 or 2. I was thinking I just tack on a WHERE FIND_IN_SET(1,CategoryName) OR FIND_IN_SET(2,CategoryName) but this does not work because of the MySQL execution order and CategoryName column does not exist yet.
What is the best way to filter for category_id? The input for categories will be comma separated but I can use PHP to explode() the string to separate them.
You can rewrite the query with a LEFT join of Records to Categories:
SELECT r.id,
GROUP_CONCAT(c.category_id) AS CategoryName
FROM Records r LEFT JOIN Categories c
ON c.id = r.id
GROUP BY r.id
and if you want to use the same query for filtering all you have to do is add at the end a HAVING clause:
HAVING FIND_IN_SET(1, CategoryName) OR FIND_IN_SET(2, CategoryName)
Or, you can filter first and then aggregate:
SELECT r.id,
GROUP_CONCAT(c.category_id) AS CategoryName
FROM Records r INNER JOIN Categories c
ON c.id = r.id
WHERE c.category_id IN (1, 2)
GROUP BY r.id
I have 3 table in my database: Categories, Category_relationships and Article.
Category_relationships is like
id | category | article
---+----------+--------
1 | 2 | 3
2 | 4 | 3
Im getting a url like this: filter.php?category[]=2&category[]=4
I want to list article number 3 with one SQL query.
How can I do that?
And sorry for my English :)
There could be multiple approaches to get the desired results, Following queries are based on to match if article has these 2 categories, if there are 3 categories and you need apply matching criteria 3 times with different category id for each match
Using aggregation
select a.id, a.name
from article a
join category_relationships cr on a.id = cr.article_id
join category c on c.id = cr.category_id
group by a.id, a.name
having count(case when c.id = 2 then 1 else null) > 0
and count(case when c.id = 4 then 1 else null) > 0
or
select a.id, a.name
from article a
join category_relationships cr on a.id = cr.article_id
join category c on c.id = cr.category_id
where c.id in(2,4)
group by a.id, a.name
having count(c.id) = 2
Here 2 is variable depends upon how many categories to match, Also if there is chance of duplicates like there are more than one entries per article with same category id then use having count(distinct c.id) = 2
Using EXISTS
select a.id, a.name
from article a
where exists(
select 1
from category_relationships
where a.id = article_id
and category_id = 2
) and exists(
select 1
from category_relationships
where a.id = article_id
and category_id = 4
)
I have three tables:
products:
id name
1 juice
2 chips
3 water
orders:
id product_id order_id
1 1 special1
2 3 special1
3 2 special1
4 1 special2
5 2 special2
final_orders:
id order_id date
1 special1 25-3-2017
2 special2 25-3-2017
I want to select all products names in every order using order_id to show:
ID: Special1
Date: 25-3-2017
Products List:
juice
water
chips
ID: Special2
Date: 25-3-2017
Products List:
juice
chips
I use this:
$sql = "select * from products,orders where products.id = orders.product_id";
but it doesn't work and show me duplicated results.
thank you.
You need to join with final_orders as well:
SELECT *
FROM final_orders AS f
JOIN orders AS o ON f.order_id = o.order_id
JOIN products AS p ON p.id = o.product_id
ORDER BY f.order_id
To prevent duplication in the output, your loop that prints the output should only show the information from final_orders when it changes. See How can i list has same id data with while loop in PHP?
If you want to see one final order per record in your result set, then you will have to aggregate the products which appear in each order. One option then is the following query which aggregates order products into CSV using MySQL's GROUP_CONCAT():
SELECT t1.order_id,
t1.date,
t2.products
FROM final_orders t1
INNER JOIN
(
SELECT a.order_id, GROUP_CONCAT(b.name) AS products
FROM orders a
INNER JOIN products b
ON a.product_id = b.id
GROUP BY a.order_id
) t2
ON t1.order_id = t2.order_id
Demo here:
Rextester
here's my data structure:
category_main
id name
---------------------
1 catmain1
2 catmain2
category_sub
id name id_catmain
---------------------
1 catsub1 1
2 catsub2 1
3 catsub3 2
images
id name catsub
---------------------
1 image1 1
2 image2 1
3 image3 2
4 image4 3
desired output:
id_catmain catmain images_total
--------------------------------------------------
1 catmain1 3
2 catmain2 1
the problem is getting the total amount of images per main category ..
i tried something like (as a view)
select categories.*, group_concat(id) as all_cat from categories group by id_catmain
then querying that view using FIND_IN_SET .. but i think there must be a better way using one query only. any ideas?
thanks
Something along these lines should work I think:
SELECT c.id, c.name, COUNT(*) AS images_total FROM images i
JOIN category_sub cs ON cs.id = i.catsub
JOIN category_main c ON c.id = cs.id_catmain
GROUP BY c.id
What you are basically doing there is tying all the image table's rows to the subcategories they represent, then tying the main categories to the same row through the subcategory id. Then you can simply count all the rows, grouping the amount of counted rows by all the different main category ids.
You can solve this in many ways. One of these would be to use a subquery:
SELECT c.id AS id_catmain, c.name AS catmain,
(SELECT COUNT(i.id) AS totalImages
FROM images i
INNER JOIN category_sub s ON i.catsub = s.id
WHERE s.id_catmain = c.id) as totalImages
FROM category_main c
ORDER BY c.name ASC;
Sample here: http://sqlfiddle.com/#!9/d78e2/5
This is not better than Bun's answer, it's just to say there are other ways of doing this.
I have 2 table with following structure
products
[id] [name] [setting_id]
1 pendrive 6
2 laptop 3
3 mobile 2
4 vallet 7
5 clothes 4
store_product
[id] [name] [storeid]
2 laptop_store 1
3 mobile_mobile 1
5 clothes_store 4
8 new1_store 2
9 new2_store 3
I want result like this
[id] [name]
1 pendrive
2 laptop_store
3 mobile_mobile
4 vallet
5 clothes_store
8 new1_store
9 new2_store
I have tried this query but it gives only but there is no new1_store and new1_store
SELECT products.id , CASE WHEN store_product.name IS NOT NULL AND store_product.name!='' THEN store_product.name ELSE products.name END AS name
from products
left join store_product on store_product.id = products.id
As I know from left join it is not possible but can please suggest any optimized query (Avoid union of left join and right join) .If possible than please answer
Thanks
In MS-SQL Server, I would resolve this with the following query:
Select Id, Name from Products P
Where Not Exists (Select * from Store_Product SP where SP.Id = P.Id)
Union All
Select Id, Name from Store_Product
For MySQL, I don't know.
I'd do something like this below.
First part selects ids and names for products existing in products table and possibly in store_product table.
Second part fills the ids and names of rows with ids existing only in store_product table.
(SELECT products.id, COALESCE(store_product.name, products.name) as name
FROM products
LEFT JOIN store_product ON store_product.id = products.id)
UNION
(SELECT store_product.id, store_product.name as name
FROM store_product
LEFT JOIN products ON store_product.id = products.id
WHERE products.id IS NULL)
ORDER BY id