MySQL - Find result that have children values - mysql

I have this table structure:
product_skus table
| id |
| 1 |
...
product_sku_values table
| product_sku_id | value_id |
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
...
I need the query to find the product_sku_id, having the three values ID's (1, 2, and 3).
I'm trying with this query:
select product_sku_id from product_sku_values
where product_sku_values.value_id = 1
or product_sku_values.value_id = 2
or product_sku_values.value_id = 3
group by product_sku_id
having product_sku_id = 1
How can I do that? I'm trying lot of possibilities but no one give me the ID that I need. Can somebody help me?
Thanks.

This is a canonical method:
select psv.product_sku_id
from product_sku_values psv
where psv.value_id in (1, 2, 3)
group by psv.product_sku_id
having count(distinct psv.value_id) = 3;
If you know that product_sku_values have no duplicates, then use count(*) in the having clause.

You can simply use group by clause to get all the possible values, e.g.:
select product_sku_id, group_concat(value_id)
from product_sku_values
group by product_sku_id;
If you are only interested in value_id 1,2 and 3 then you can add one where clause, e.g:
select product_sku_id, group_concat(value_id)
from product_sku_values
where value_id in (1,2,3)
group by product_sku_id;

Related

Mysql IN function

class_table
+----+-------+--------------+
| id |teac_id| student_id |
+----+-------+--------------+
| 1 | 1 | 1,2,3,4 |
+----+-------+--------------+
student_mark
+----+----------+--------+
| id |student_id| marks |
+----+----------+--------+
| 1 | 1 | 12 |
+----+----------+--------+
| 2 | 2 | 80 |
+----+----------+--------+
| 3 | 3 | 20 |
+----+----------+--------+
I have these two tables and i want to calculate the total marks of student and my sql is:
SELECT SUM(`marks`)
FROM `student_mark`
WHERE `student_id` IN
(SELECT `student_id` FROM `class_table` WHERE `teac_id` = '1')
But this will return null, please help!!
DB fiddle
Firstly, you should never store comma separated data in your column. You should really normalize your data. So basically, you could have a many-to-many table mapping teacher_to_student, which will have teac_id and student_id columns.
In this particular case, you can utilize Find_in_set() function.
From your current query, it seems that you are trying to getting total marks for a teacher (summing up marks of all his/her students).
Try:
SELECT SUM(sm.`marks`)
FROM `student_mark` AS sm
JOIN `class_table` AS ct
ON FIND_IN_SET(sm.`student_id`, ct.`student_id`) > 0
WHERE ct.`teac_id` = '1'
In case, you want to get total marks per student, you would need to add a Group By. The query would look like:
SELECT sm.`student_id`,
SUM(sm.`marks`)
FROM `student_mark` AS sm
JOIN `class_table` AS ct
ON FIND_IN_SET(sm.`student_id`, ct.`student_id`) > 0
WHERE ct.`teac_id` = '1'
GROUP BY sm.`student_id`
Just in case you want to know why, The reason it returned null is because the subquery returned as '1,2,3,4' as a whole. What you need is to make it returned 1,2,3,4 separately.
What your query returned
SELECT SUM(`marks`)
FROM `student_mark`
WHERE `student_id` IN ('1,2,3,4')
What you expect is
SELECT SUM(`marks`)
FROM `student_mark`
WHERE `student_id` IN (1,2,3,4)
The best way is it normalize as #madhur said. In your case you need to make the teacher and student as one to many link
+----+-------+--------------+
| id |teac_id| student_id |
+----+-------+--------------+
| 1 | 1 | 1 |
+----+-------+--------------+
| 2 | 1 | 2 |
+----+-------+--------------+
| 3 | 1 | 3 |
+----+-------+--------------+
| 4 | 1 | 4 |
+----+-------+--------------+
If you want to filter your table based on a comma separated list with ID, my approach is to
append extra commas at the beginning and at the end of a list as well as at the beginning and at the end of an ID, eg.
1 becomes ,1, and list would become ,1,2,3,4,. The reason for that is to avoid ambigious matches like 1 matches 21 or 12 in a list.
Also, EXISTS is well-suited in that situation, which together with INSTR function should work:
SELECT SUM(`marks`)
FROM `student_mark` sm
WHERE EXISTS(SELECT 1 FROM `class_table`
WHERE `teac_id` = '1' AND
INSTR(CONCAT(',', student_id, ','), CONCAT(',', sm.student_id, ',')) > 0)
Demo
BUT you shouldn't store related IDs in one cell as comma separated list - it should be foreign key column to form proper relation. Joins would become trivial then.

Find multiple totals by adding values from mysql table

I need to create a number adding all the values i can find in the db related to a specific customer.
Ex.
| Cust. | Value |
| 1 | 3 |
| 2 | 1 |
| 1 | 1 |
| 2 | 1 |
| 3 | 5 |
The result i want is : Customer #1 = 4, Customer #2 = 2; Customer #3 = 5.
There is a way to do that right into the mysql query?
Try Below query.
Select CONCAT('Customer #' , cust) as customer , sum(Value)
FROM customer_table
Group By cust
You want to SUM the values with a specific GROUP BY clause. Think of the GROUP BY as dividing rows into buckets and the SUM as aggregating the contents of those buckets into something useful.
Something like:
SELECT SUM(Value) FROM table GROUP BY Cust

MySQL count unique and duplicate values

I need some help with counting both unique and duplicate values in MySQL. I want to know how many records there are total, and also how many is there two times and three times and so on...
Do I need to use UNION or something? I think SUM would be the best solution for me because of I might use some joins with this in future.
Sample data:
| custId | name |
|--------|--------|
| 1001 | Alex |
| 1001 | Alex |
| 1002 | Daniel |
| 1003 | Mark |
| 1002 | Daniel |
Sample results:
| total | twoTimes | threeTimes |
|-------|----------|------------|
| 3 | 2 | 0 |
Thanks in advance.
Just a basic group by should do it
SELECT YourValue, Count(YourValue)
FROM YourTable
GROUP BY YourValue
If you want only a category, like unique values ADD
HAVING Count(YourValue) = 1
Here is my approach:
http://sqlfiddle.com/#!9/9411dc/3
SELECT c.cnt AS `times`, COUNT(c.name) cnt
FROM (SELECT name, COUNT(custId) cnt
FROM cust
GROUP BY name) c
GROUP BY c.cnt;
that is not exactly what you did ask (you asked for pivot table which is very difficult to realize). So if you want to make it pivot you can read here: MySQL pivot table
And if you are sure that you have very small max of duplicate count your pivot query could be:
http://sqlfiddle.com/#!9/9411dc/5
SELECT
SUM(IF(c.cnt=1,1,0)) AS `Unique`,
SUM(IF(c.cnt=2,1,0)) AS `Two times`,
SUM(IF(c.cnt=3,1,0)) AS `Three times`,
SUM(IF(c.cnt=4,1,0)) AS `Four times`
FROM (SELECT name, COUNT(custId) cnt
FROM cust
GROUP BY name) c

returning rows satisfying multiple conditions for a column from a mysql table

I have mysql table with hospitals and treatments(associated with sub treatments) that they provide. I need to make mysql query on the table which returns hospitals providing all treatment/sub_treatment given in a list. For example:
From table below I need hospitals providing all treatments in list: (tretament_id, sub_treatment_id) = (1-1, 1-2). So result must be hospitals with id 1 and 8.
hospital_id | treatment_id | sub_treatment_id
-------------------------------------------------
1 | 1 | 1
1 | 1 | 2
1 | 1 | 3
_________________________________________________
4 | 1 | 1
4 | 2 | 1
_________________________________________________
8 | 1 | 1
8 | 1 | 2
_________________________________________________
7 | 2 | 1
I tried WHERE IN but it works like OR so returns hospital 4 which satisfies only (1,1). How can I write an sql query like WHERE IN but which works like AND?
Try this:
SELECT hospital_id
FROM mytable
WHERE (treatment_id, sub_treatment_id) IN ((1, 1), (1, 2))
GROUP BY hospital_id
HAVING COUNT(CASE
WHEN (treatment_id, sub_treatment_id) IN ((1, 1), (1, 2))
THEN 1
END) = 2
Demo here
You can do this using group by and having:
select hospital_id
from t
where treatment_id = 1 and sub_treatment_id in (1, 2)
group by hospital_id
having count(*) = 2;
Note: This assumes that there are no duplicates in the table. That is easy enough to fix using count(distinct), but probably not necessary.
Here is a solution using GROUP_CONCAT and JOIN:
select distinct t.hospital_id
from hospitals h and treatments t ON h.id = t.hospital_id
having GROUP_CONCAT(CONCAT(t.treatment_id, '-', t.sub_treatment_id)
ORDER BY t.treatment_id, t.sub_treatment_id)
= '1-1,1-2';

IFNULL with IN statement with mysql

I am Newbie to mysql....it may look dump question....but i have been trying this from 3 hours...here what i am trying to do....
SELECT
MERCHANT_ID,
IFNULL(COUNT(SUBSCRIBE_ID),0)
FROM SUBSCRIBE_TABLE
WHERE
MERCHANT_ID IS NULL OR
MERCHANT_ID IN(1000000000066,1000000000104,1000000000103,1000000000105)
GROUP BY MERCHANT_ID
ORDER BY
FIND_IN_SET(MERCHANT_ID,'1000000000066,1000000000104,1000000000103,1000000000105');
AND the output is...
+------------------+---------------------------------+
| MERCHANT_ID | IFNULL(COUNT(SUBSCRIBE_ID),0) |
+------------------+---------------------------------+
| 1000000000066 | 2 |
| 1000000000103 | 1 |
+------------------+---------------------------------+
but i am expecting in following manner...
+------------------+---------------------------------+
| MERCHANT_ID | IFNULL(COUNT(SUBSCRIBE_ID),0) |
+------------------+---------------------------------+
| 1000000000066 | 2 |
| 1000000000104 | 0 |
| 1000000000103 | 1 |
| 1000000000105 | 0 |
+------------------+---------------------------------+
i tried adding MERCHANT_ID IS NULL... but not able get the result with default value... :(
You will only get records that are actually in SUBSCRIBE_TABLE. If you want to get records for all your ids, you have to "create a temporary table" (or use a subquery with UNION in thise case) with those values first, and then join your results to it.
Your query could look like this:
SELECT
merchant_id,
COUNT(subscribe_id)
FROM
(SELECT 1000000000066 AS merchant_id, 1 AS SortKey
UNION ALL
SELECT 1000000000104 AS merchant_id, 2 AS SortKey
UNION ALL
SELECT 1000000000103 AS merchant_id, 3 AS SortKey
UNION ALL
SELECT 1000000000105 AS merchant_id, 4 AS SortKey
) AS temp
LEFT JOIN subscribe_table USING (merchant_id)
GROUP BY merchant_id
ORDER BY SortKey ASC
I replaced your FIND_IN_SET with the column SortKey in the subquery. COUNT will only count non-null rows and will return 0 if none are found. You don't need the IFNULL around it.
If you have more than those 4 merchant_ids you might want to look into doing the same thing with a temporary table. See here for examples:
Mysql: Create inline table within select statement?