Using AND (OR) to select based on multiple row values - mysql

I want to create filter query which uses AND and OR on many to many relation table.
Table consists only of 2 columns id_product and id_category
My query I'm using, but its not working:
SELECT id_product FROM id_category WHERE ( id_category = 1) AND (id_category = 2)
AND ( id_category = 3 OR id_category = 4 OR id_category = 5) GROUP BY id_product
I would like to retrieve products that are in categories at the same time.
Only IDs of products which are in category 1 AND 2 AND (3 or 4 or 5)

You must group by id_product and put these conditions in a HAVING clause:
SELECT id_product
FROM tablename
GROUP BY id_product
HAVING
SUM(id_category = 1) > 0
AND
SUM(id_category = 2) > 0
AND
SUM(id_category IN (3, 4, 5)) > 0

Just for fun, to use a single condition in the HAVING clause:
SELECT id_product
FROM yourTable
WHERE id_category BETWEEN 1 AND 5
GROUP BY id_product
HAVING
COUNT(DISTINCT CASE WHEN id_category IN (3, 4, 5)
THEN 3 ELSE id_category END) = 3;
Demo
The logic here is to first restrict the query to only id_category values (1,2,3,4,5). Then, we assert but first map id_category values of (3,4,5) to the same single value 3. The assertion is that the distinct count of mapped id_category values is 3, which would imply that 1, 2, and (3,4,5) all occur for that product.

Related

mysql find records against multiple row condition

So here I have multiple table and joined them but here I have put simple example like:
here i am able to find all those items who has category_id 1 and 3 any one using in query, but now i need and condition here like where category 1 and 3 both matches.
here i need all those items who has category_id 1 and 3 both.
So how to do that?
mysql in query is matching anyone exist, so is there any other function which matches multiple values?
Using INNER JOIN with a subquery to get the items that have both category_id 1 and 3. The subquery would select all the item_id's that have category_id 1 or 3 and the main query would join with the subquery on the item_id.
SELECT p1.item_id, p1.product_id, p1.category_id
FROM PRODUCT p1
INNER JOIN (
SELECT item_id
FROM PRODUCT
WHERE category_id IN (1, 3)
GROUP BY item_id
HAVING COUNT(DISTINCT category_id) = 2
) p2 ON p1.item_id = p2.item_id
WHERE p1.category_id IN (1, 3);
Sample:
-- create
CREATE TABLE PRODUCT (
id INTEGER PRIMARY KEY,
item_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
category_id INTEGER NOT NULL
);
-- insert
INSERT INTO PRODUCT VALUES (3862, 297,0, 1);
INSERT INTO PRODUCT VALUES (3861, 297,0, 3);
INSERT INTO PRODUCT VALUES (3860, 23, 0,1);
Output:
item_id product_id category_id
297 0 3
297 0 1

SQL - Different conditions on the same column

I'm trying to achieve something very similar to SQL - Multiple conditions where clause the same column
The difference is that I want two separate WHERE conditions:
WHERE tag_id IN (1,2)
OR tag_id IN (3,2)
GROUP BY other_id HAVING COUNT(tag_id) = 2
This gives me all the records that I need, but also gives me records that have the tags 1 or 3 even when that record doesn't have tag_id = 2. I know that my problem is in the OR condition, because if I do (1,2) and (3,2) separately, I get the expected results for each query.
What am I missing?
You could use:
SELECT other_id
FROM yourTable
GROUP BY other_id
HAVING COUNT(tag_id) = 2 AND -- 2 tags
(SUM(tag_id NOT IN (1, 2)) = 0 OR -- either (1, 2)
SUM(tag_id NOT IN (3, 2)) = 0); -- or (3, 2)

I want to get the records of multiple values for the same column

I'm trying to get values from the database based on two values of my own, the values must match the id of the database
id contactid flag flag_type
-----------------------------------
1 99 Volunteer 1
2 99 Uploaded 2
3 100 Via Import 3
4 100 Volunteer 1
5 100 Uploaded 2
So from here I would want to get the rows with the id's 1 and 2, and ignore the rest of the values. But say for example that the row with the id 2 does not exist the statement would not return any row.
I've tried the following statement, but it doesn't seem to work :
SELECT * FROM docs WHERE id IN (1) AND id IN (2);
You should use OR
SELECT * FROM docs
WHERE id IN (1) OR id IN (2);
or
SELECT * FROM docs
WHERE id = 1
OR id = 2;
or if you need the records for contactid with both id (1,2) then
select * from docs
inner join (
select contactid
from docs
where id IN (1, 2)
having count(distinct id ) = 2
) t on t.contactid = docs.contactid
you need subquery
select id from table_name where contactid in (
select contactid
from table_nae
group by contactid
having count(*)=2
)
subquery will pick only those contactid whose count is two and in condition of main query will help to pick your desired id
If you want the contacts with exactly those flags, you can do:
select contactid
from t
group by contactid
having sum(flag = 1) > 0 and -- has 1
sum(flag = 2) > 0 and -- has 2
sum(flag not in (1, 2)) = 0; -- has nothing else
There are various ways to get the original rows -- using in or exists or join:
select t.*
from t join
(select contactid
from t
group by contactid
having sum(flag = 1) > 0 and -- has 1
sum(flag = 2) > 0 and -- has 2
sum(flag not in (1, 2)) = 0 -- has nothing else
) tt
on t.contactid = tt.contactid;

How to select limited rows based on column

I'm new in MySQL. This may be easy, but out of my logic
I have a product_table which has 5 columns
id
product_name
product_image
description
category_id
in this table category_id has multiple rows with the same id
for example
category field has 300 rows with category_id '4' and 100 rows with category_id '3' so on....
I want to select only 5 rows per category_id
for example
5 rows for category_id 4 and 5 rows for category_id 3 like this.
SELECT * FROM product_table WHERE category_id = '1' LIMIT 5
UNION
SELECT * FROM product_table WHERE category_id = '2' LIMIT 5
UNION
SELECT * FROM product_table WHERE category_id = '3' LIMIT 5
.
.
.
If there are just a few category, then this should be fine.
select *
from
(SELECT category_id,
id,
CASE WHEN #gr=category_id THEN #rn:=#rn+1 ELSE #rn:=0 END as row_number,
#gr:=category_id
FROM the_table, (select #rn:=0, #gr:=null) as sess
order by category_id, id) sub
WHERE sub.row_number<5
You can introduce session variables and order your data counting member in each group. Then leave only members with group number less than 5

SQL How to use SUM and Group By with multiple tables?

I have three tables.
For each "id" value, I would like the sum of the col1 values, the sum of col2 values & the sum of col3 values listed separately. I am not summing across tables.
table a
num | id | col1
================
1 100 0
2 100 1
3 100 0
1 101 1
2 101 1
3 101 0
table b
idx | id | col2
=================
1 100 20
2 100 20
3 100 20
4 101 100
5 101 100
table c
idx | id | col3
==============================
1 100 1
2 100 1
3 100 1
4 101 10
5 101 1
I would like the results to look like this,
ID | sum_col1 | sum_col2 | sum_col3
====================================
100 1 60 3
101 2 200 11
Here is my query which runs too long and then times out. My tables are about 25,000 rows.
SELECT a.id as id,
SUM(a.col1) as sum_col1,
SUM(b.col2) as sum_col2,
SUM(c.col3) as sum_col3
FROM a, b, c
WHERE a.id=b.id
AND a=id=c.id
GROUP by id
Order by id desc
The number of rows in each table may be different, but the range of "id" values in each table is the same.
This appears to be a similar question, but I can't make it work,
Mysql join two tables sum, where and group by
Here is a solution based on your data. Issue with your query is that you were joining tables on a non-unique column resulting in Cartesian product.
Data
DROP TABLE IF EXISTS A;
CREATE TABLE A
(num int,
id int,
col1 int);
INSERT INTO A VALUES (1, 100, 0);
INSERT INTO A VALUES (2, 100, 1);
INSERT INTO A VALUES (3, 100, 0);
INSERT INTO A VALUES (1, 101, 1);
INSERT INTO A VALUES (2, 101, 1);
INSERT INTO A VALUES (3 , 101, 0);
DROP TABLE IF EXISTS B;
CREATE TABLE B
(idx int,
id int,
col2 int);
INSERT INTO B VALUES (1, 100, 20);
INSERT INTO B VALUES (2, 100, 20);
INSERT INTO B VALUES (3, 100, 20);
INSERT INTO B VALUES (4, 101, 100);
INSERT INTO B VALUES (5, 101, 100);
DROP TABLE IF EXISTS C;
CREATE TABLE C
(idx int,
id int,
col3 int);
INSERT INTO C VALUES (1, 100, 1);
INSERT INTO C VALUES (2, 100, 1);
INSERT INTO C VALUES (3, 100, 1);
INSERT INTO C VALUES (4, 101, 10);
INSERT INTO C VALUES (5, 101, 1);
Solution
SELECT a_sum.id, col1_sum, col2_sum, col3_sum
FROM (SELECT id, SUM(col1) AS col1_sum
FROM a
GROUP BY id ) a_sum
JOIN
(SELECT id, SUM(col2) AS col2_sum
FROM b
GROUP BY id ) b_sum
ON (a_sum.id = b_sum.id)
JOIN
(SELECT id, SUM(col3) AS col3_sum
FROM c
GROUP BY id ) c_sum
ON (a_sum.id = c_sum.id);
Result is as expected
Note: Do outer joins if an id doesnt have to be present in all three tables.
Maybe this will do?
Haven't got a chance to run it, but i think it can do the job.
SELECT sumA.id, sumA.sumCol1, sumB.sumCol2, sumC.sumCol3
FROM
(SELECT id, SUM(col1) AS sumCol1 FROM a GROUP BY id ORDER BY id ASC) AS sumA
JOIN (SELECT id, SUM(col2) AS sumCol2 FROM b GROUP BY id ORDER BY id ASC) AS sumB ON sumB.id = sumA.id
JOIN (SELECT id, SUM(col3) AS sumCol3 FROM c GROUP BY id ORDER BY id ASC) AS sumC ON sumC.id = sumB.id
;
EDIT
SELECT IF(sumA.id IS NOT NULL, sumA.id, IF(sumB.id IS NOT NULL, sumB.id, IF(sumC.id IS NOT NULL, sumC.id,''))),,
sumA.sumCol1, sumB.sumCol2, sumC.sumCol3
FROM
(SELECT id, SUM(col1) AS sumCol1 FROM a GROUP BY id ORDER BY id ASC) AS sumA
OUTER JOIN (SELECT id, SUM(col2) AS sumCol2 FROM b GROUP BY id ORDER BY id ASC) AS sumB ON sumB.id = sumA.id
OUTER JOIN (SELECT id, SUM(col3) AS sumCol3 FROM c GROUP BY id ORDER BY id ASC) AS sumC ON sumC.id = sumB.id
;
I would do the summing first, then union the results, then pivot them round:
SELECT
id,
MAX(CASE WHEN which = 'a' then sumof end) as sum_a,
MAX(CASE WHEN which = 'b' then sumof end) as sum_b,
MAX(CASE WHEN which = 'c' then sumof end) as sum_c
FROM
(
SELECT id, sum(col1) as sumof, 'a' as which FROM a GROUP BY id
UNION ALL
SELECT id, sum(col2) as sumof, 'b' as which FROM b GROUP BY id
UNION ALL
SELECT id, sum(col3) as sumof, 'c' as which FROM c GROUP BY id
) a
GROUP BY id
You could also union, then sum:
SELECT
id,
SUM(CASE WHEN which = 'a' then v end) as sum_a,
SUM(CASE WHEN which = 'b' then v end) as sum_b,
SUM(CASE WHEN which = 'c' then v end) as sum_c
FROM
(
SELECT id, col1 as v, 'a' as which FROM a GROUP BY id
UNION ALL
SELECT id, col2 as v, 'b' as which FROM b GROUP BY id
UNION ALL
SELECT id, col3 as v, 'c' as which FROM c GROUP BY id
) a
GROUP BY id
You cant easily use a join, unless all tables have all values of ID, in which case I'd say you can sum them as subqueries and then join the results together.. But if one of your tables suddenly lacks an id value that the other two tables have, that row disappears from your results (unless you use full outer join and some really ugly coalescing in your ON clause)
Using union in this case will give you a more missing-value-tolerant result set, as it can cope with missing values of ID in any table. Thus, we union the tables together into one dataset, but use a constant to track which table the value came from, that way we can pick it out into its own summation later
If any id value is not present in any table, then the sum for that column will be null. If you want it to be 0, you can change the MAX to SUM or wrap the MAX in a COALESCE