Mysql find distinct values from rows matching multiple values in another field - mysql

In a table I have records as follows:
ID, ID1, ID2
1, 2, 3
2, 2, 4
3, 2, 5
4, 3, 3
4, 3, 4
4, 4, 3
4, 4, 4
4, 4, 5
I want to be able to find all ID1 values which exist in the table which have ALL of the ID2 values 3, 4 AND 5
So in this case I would want some SQL to pull out only ID1 = 2 and ID1 = 4, but not ID1 = 3 because there exist only ID2=3 and ID2=4 for ID1=3... so it's missing a row for ID2=5 and hence I do not want it included in my result set.
Is there an efficient way to do this?
TY!

You will want to use the following which selects all rows that have an id2 with a value of 3, 4 or 5 and then applies a group by with a having clause to make sure that you return 3 distinct id2 values:
select id1
from yourtable
where id2 in (3, 4, 5)
group by id1
having count(distinct id2) = 3
See SQL Fiddle with Demo.
This type of query is known as relational division.

Related

Selecting according to the sum of a certain's row's column conditionally in MySQL

Assume we got a table like this:
id other_id limit
......................
1 1 4
2 1 5
3 2 3
4 2 2
Assume we got a total limit of 5 and other_id 1, is there any way I can get IDs 3,4 from the table above?
More conditions:
total limit = 3, other_id = 1 => IDs of 1 rows return
total limit = 9, other_id = 1 => IDs of 1,2 rows return
total limit = 10, other_id = 1,2 => IDs of 1,2,3 rows return
total limit = 15, other_id = 1,2 => IDs of 1,2,3,4 rows return
total limit = 3, other_id = 2 => IDs of 3 rows return
total limit = 4, other_id = 2 => IDs of 3,4 rows return
The third one is obviously unexpected, but I would be able to handle the error in a different way (total limit being more than the total of limits in the table)
You want a rolling total. MySql supports window functions since version 8.0. Limit is a reserved word, using climit for the column name
select id, climit
from (
select *, sum(climit) over(order by id) s
from mytable
) t
where s - climit < myLimitParameter;
in mysql 5.x and above you can do a correlated query
CREATE TABLE tab1( id int, `other_id` int, limits int);
INSERT INTO tab1 VALUES
(1, 1, 4),
(2, 1, 5),
(3, 2, 3),
(4, 2, 2)
SELECT GROUP_CONCAT(id)
FROM
(SELECT t.id, t.other_id
, (SELECT SUM(limits) FROM tab1 WHERE limits > t.limits OR (id < t.id AND limits = t.limits)
OR (id = t.id)) required
FROM tab1 t
HAVING required <= '12'
ORDER BY id,other_id ASC) t1
| GROUP_CONCAT(id) |
| :--------------- |
| 1,2,3 |
db<>fiddle here

SQL JSON functions

I'm getting a query output as follows from a table
RowId QuestionGroupId QuestionId Answer
1 2 1 Single
2 2 2 With Kids
3 2 3 Not At All
4 3 1 Single
5 3 2 With Kids
6 3 3 Occasionally Smoke
But the result I would Like to get is as something follows
RowId QuestionGroupId ValueSet
1 2 [{QuestionId:1,Answer:Single},QuestionId:2,Answer:WithKids},QuestionId:3,Answer:Nt at all}]
2 3 [{QuestionId:1,Answer:Single},QuestionId:2,Answer:WithKids},QuestionId:3,Answer:Occasionally Smoke}]
So how to I convert the rows of record to JSON using SQL JSON ability.
Using FOR JSON AUTO given me all in one row of record.Your help is appreciated.
I don't really get what you're doing with the RowId column, but you should be able to do something like this with a correlated subquery to get what you're after
;with src
(
RowId,
QuestionGroupId,
QuestionId,
Answer
) as
(
select 1, 2, 1, 'Single'
union all select 2, 2, 2, 'With Kids'
union all select 3, 2, 3, 'Not At All'
union all select 4, 3, 1, 'Single'
union all select 4, 3, 2, 'With Kids'
union all select 4, 3, 3, 'Occasionally Smoke'
)
select
QuestionGroupId,
(
select i.QuestionId, i.Answer
from src i
where o.QuestionGroupId = i.QuestionGroupId
order by QuestionId
for json auto
) as ValueSet
from src o
group by QuestionGroupId

How to copy some columns and change one column at the same time?

I want to copy the third and the forth column but change the second column. What should I do? I want to know how to write the sql query. Thank you.
for example:
table1:
1, 1, aaa, bbb
2, 1, ads, bff
3, 1, awq, bcc
and I want table1 finally to be:
1, 1, aaa, bbb
2, 1, ads, bff
3, 1, awq, bcc
4, 2, aaa, bbb
5, 2, ads, bff
6, 2, awq, bcc
(the first column is id)
This should work:
insert into table1 (column2,column3,column4)
select 2,column3, column4
from table1 where column2 = 1
As you suggest:
select (#cnt:=#cnt + 1) as RowNumber,a.c1,a.c2,a.c3 from
(select 1 as id, 1 as c1, 'aaa' as c2, 'bbb'as c3 union all
select 2, 1, 'ads', 'bff' union all
select 3, 1, 'awq', 'bcc'
union all
select 1 as id, 1 as c1, 'aaa' as c2, 'bbb'as c3 union all
select 2, 1, 'ads', 'bff' union all
select 3, 1, 'awq', 'bcc') as a
cross JOIN
(select #cnt:=0) as tmp
RESULT:
1 1 aaa bbb
2 1 ads bff
3 1 awq bcc
4 1 aaa bbb
5 1 ads bff
6 1 awq bcc

Need MySql query that should return result by ordering values in particular field

Need MySql query that should return result by ordering values in particular field.
From below my result set should contain order like parent_id (1,4,6) should come first parent_id(2,3,7) come next and other should come last.
d data parent_id
----------------------
1 a1 1
2 abc 3
3 abcd 4
4 xyz 2
5 zxyy 6
2 abc 8
3 abcd 9
4 xyz 2
5 zxyy 15
Use a CASE expression in your ORDER BY clause:
SELECT d, data, parent_id
FROM yourTable
ORDER BY CASE WHEN parent_id IN (1, 4, 6) THEN 1
WHEN parent_id IN (2, 3, 7) THEN 2
ELSE 3 END,
parent_id
Follow the link below for a running demo:
SQLFiddle
Use below mentioned query
SELECT d, data, parent_id FROM table_name ORDER BY FIELD(parent_id,1,4,6,2,3,7);

MYSQL - calculating differences within a single table

I'm looking for a way to calculate differences between integers within a single table.
I'm planning a MYSQL table that looks like this:
user question answer
1 1 3
1 2 3
1 3 2
1 4 5
1 5 1
2 1 2
2 2 3
2 3 1
2 4 5
2 5 3
3 1 3
3 2 3
3 3 4
3 4 5
3 5 3
4 1 5
4 2 3
4 3 2
4 4 5
4 5 1
Each user (in this example) has answered 5 questions, giving an answer on a scale of 1 to 5.
What I'm looking to work out is which of the users 2, 3 and 4 have given answers that are most similar to those provided by user 1.
What I have in mind is calculating the difference between the answers given by each user for each question, in comparison to those of user 1, and then adding up those differences.
The user with the lowest number after that addition would be most similar to user 1.
I'm sorry to say that I don't really know where to begin constructing a query that does this efficiently and was wondering if anyone could point me in the right direction?
I'm also open to any suggestions for any better or more logical way to build the same results.
SELECT SUM(ABS(t2.answer - t1.answer)) AS total_diff, t2.user
FROM my_table AS t1
LEFT JOIN my_table AS t2 USING(question)
WHERE t1.user = 1 AND t2.user != t1.user
GROUP BY t2.user
ORDER BY total_diff ASC
result:
total_diff user
2 4
4 2
4 3
SELECT
yt1.user,
SUM(CASE WHEN yt1.answer = yt2.answer THEN 1 ELSE 0 END) AS howMuchAnswersInCommon
FROM yourTable yt1
INNER JOIN yourTable yt2 ON yt1.question = yt2.question
WHERE yt2.user = 1 AND yt1.user != 1
GROUP BY yt1.user
ORDER BY howMuchAnswersInCommon DESC
;
This will give you the one with the most common answers to user 1 on top.
Test data:
/*
create table yourTable (user int, question int, answer int);
insert into yourTable values
(1, 1, 3),
(1, 2, 3),
(1, 3, 2),
(1, 4, 5),
(1, 5, 1),
(2, 1, 2),
(2, 2, 3),
(2, 3, 1),
(2, 4, 5),
(2, 5, 3),
(3, 1, 3),
(3, 2, 3),
(3, 3, 4),
(3, 4, 5),
(3, 5, 3),
(4, 1, 5),
(4, 2, 3),
(4, 3, 2),
(4, 4, 5),
(4, 5, 1);
*/
OUTPUT:
user howMuchAnswersInCommon
4 4
3 3
2 2