SELECT * FROM questions WHERE test_id - mysql

I have two SQL tables:
Tests
id | name
1 | simple
2 | advanced
3 | professional
Questions
id | Question | answer a | answer b | test_id
1 | Working? | Yes | no | 1,3
2 | Do you smoke? | Yes | no | 2,3
3 | You have a driving license? | Yes | No | 2
Questions should be displayed only if in "test_id" is the id of the desired test, for example, test 2 should contain only questions 2 and 3
SELECT * FROM questions WHERE... (array "test_id" contains 2)
It isn't duplicate, because in suggested question is query like "WHERE ... value=(1,2,3,4)", but in my problem, I have query like "... WHERE (1,2,3,4,5)=value" it's opposite and method "in" is not correct.

You should normalize your database, creating a third table to link questions with the tests they appear on:
Tests:
id | name
1 | simple
2 | advanced
3 | professional
Questions:
id | question | answer a | answer b
1 | Working? | Yes | No
2 | Do you smoke? | Yes | No
3 | You have a driving license? | Yes | No
TestQuestions:
id | test_id | question_number | question_id
1 | 1 | 1 | 1
2 | 2 | 1 | 2
3 | 2 | 2 | 3
4 | 3 | 1 | 1
5 | 3 | 2 | 2
You can then fetch the questions for an individual test by joining the TestQuestions table with Questions:
SELECT Questions.*
FROM TestQuestions
INNER JOIN Questions ON Questions.id = TestQuestions.question_id
WHERE TestQuestions.test_id = 2
ORDER BY TestQuestions.question_number
If you are unable to modify your database tables, then you could also determine the questions in a test using MySQL's FIND_IN_SET function:
SELECT * From Questions WHERE FIND_IN_SET('2', test_id) > 0
It is preferable to use the normalized database structure though since this allows the ordering of questions on a test to be expressed and indexes can also be created to allow better performance.
To evaluate the FIND_IN_SET query, the database must consider every row in the Questions table to see if it matches. By adding the following index to the normalized database, the database would be able to seek directly to the relevant questions for a test:
CREATE UNIQUE INDEX test_id_question_number ON TestQuestions (test_id, question_number)

You really should include another table that maps the question to the test. The way you have it now, the questions cannot be easily linked to the test. For example, if you try to use use a LIKE comparison you may get questios back you didn't intend. Say you are looking for questions to test 2
SELECT * FROM questions WHERE test_id LIKE %2%;
You would also retrieve items for tests 12, anything in the 20s and 32, 42, etc.
You should create a new table called testquestions (for example). This would have two fields
test_id
question_id
And then this data (using what you showed above)
test_id | question_id
1 | 1
2 | 2
2 | 3
3 | 1
3 | 2
Now you can run a query like this
SELECT
tests.name,
questions.question,
questions.`answer a`,
questions.`answer b`
FROM (
tests LEFT JOIN testquestions ON test.id=testquestion.test_id
) LEFT JOIN
questions ON questions.id = testquestion.question_id
WHERE
tests.id = 2;

If you need to search one test id at a time you can use
SELECT * FROM questions WHERE (',' + test_id + ',') like '%,1,%'

i think the best solution offer you Phil Ross (only think i would change will be that i would store this Yes/No answer as true/false OR small int and 0/1)... But if you want to keep your table like they are you can do something like this
SELECT q.* FROM Questions q
INNER JOIN tests t
ON t.id = 2
AND q.test_id regexp(CONCAT('(^|,)',t.id, '(,|$)'));
Here is SQL Fiddle to see how it's work...
P.S. The query connect and the first table so you can select value from there if it's needed...
GL!
EDIT: Fixed on Phil suggestion... Tnx Phil :)

Related

Joining multiple tables SQL

The following query matches userID's to eachother based off of total score difference. I have two tables, survey & users.
I need to join this to the users table that I have that has usernames/photo links.
The columns I need displayed are users.name & users.photo. All tables currently have a unique userID, which is users.id, and survey.id that helps match users across DB's.
Could anyone give me a hand as how I could get this done? I've been having a lot of trouble figuring this out, thanks in advance.
select a.id yourId,
b.id matchId,
abs(a.q1 - b.q1) + abs(a.q2 - b.q2) + abs(a.q3 - b.q3)+ abs(a.q4 - b.q4)+
abs(a.q5 - b.q5)+ abs(a.q6 - b.q6)+ abs(a.q7 - b.q7)+ abs(a.q8 - b.q8)+
abs(a.q9 - b.q9)+ abs(a.q10 - b.q10) scorediff
from surveys as a
inner join surveys as b on a.id != b.id
WHERE a.id=1
order by scorediff asc
Currently this is the results of that query:
| yourID| matchID| scoreDiff|
----------------------------
| 5 | 2 | 14 |
| 5 | 3 | 25 |
| 5 | 1 | 33 |
| 5 | 6 | 34 |
I would like this as the result:
| yourID| matchID| scoreDiff| name | photo |
----------------------------------------------
| 5 | 2 | 14 | john | url
| 5 | 3 | 25 | steve| url
| 5 | 1 | 33 | jane | url
| 5 | 6 | 34 | kelly| url
matchID can be matched to the users.ID column, as they are all unique to the user.
add a new column with a foreign key constraint
ALTER TABLE surveys
ADD COLUMN id_user REFERENCES user(id);
or the opposite if that's what you want. Not sure if that is mysql syntax.
you can then join the tables via
WHERE u.id = s.id_user
This should (also) be a comment, but its a bit long.
on a.id != b.id
Given the logic elsewhere, this means you are going to get each combination of "surveys" listed twice. Why not:
on a.id<b.id
(note that if there is an index on index.id, this could actually result in the qquery going slower than it would in the absence of an index using both the above join expressions)
abs(a.q1 - b.q1) + abs(a.q2 - b.q2)
so you have multiple values represented as different attributes on the same relation. This is not good. It breaks the rules about normalization and makes your life much more difficult. (and ours).
Also, that you are adding the abs of the difference, to my mind, creates a rather distorted picture of the difference between individuals.
Consider:
user q score
george 1 4
symcbean 1 2
george 2 2
symcbean 2 4
Here, by your calculation there is a difference in score of 4 between the 2 users - but I would have interpreted the data above as meaning that the two users had the same score. Is that really what you intended?

Most common values for a group dependent on a select query

I'm breaking my head over how to do this one in SQL. I have a table:
| User_id | Question_ID | Answer_ID |
| 1 | 1 | 1 |
| 1 | 2 | 10 |
| 2 | 1 | 2 |
| 2 | 2 | 11 |
| 3 | 1 | 1 |
| 3 | 2 | 10 |
| 4 | 1 | 1 |
| 4 | 2 | 10 |
It holds user answers to a particular question. A question might have multiple answers. A User cannot answer the same question twice. (Hence, there's only one Answer_ID per {User_id, Question_ID})
I'm trying to find an answer to this query: For a particular question and answer id (Related to the same question), I want to find the most common answer given to OTHER question by users with the given answer.
For example, For the above table:
For question_id = 1 -> For Answer_ID = 1 - (Question 2 - Answer ID 10)
For Answer_ID = 2 - (Question 2 - Answer ID 11)
Is it possible to do in one query? Should it be done in one query? Shall I just use stored procedure or Java for that one?
Though #rick-james is right, I am not sure that it is easy to start when you do not not how the queries like this are usually written for MySQL.
You need a query to find out the most common answers to questions:
SELECT
question_id,
answer_id,
COUNT(*) as cnt
FROM user_answers
GROUP BY 1, 2
ORDER BY 1, 3 DESC
This would return a table where for each question_id we output counts in descending order.
| 1 | 1 | 3 |
| 1 | 2 | 1 |
| 2 | 10 | 3 |
| 2 | 11 | 1 |
And now we should solve a so called greatest-n-per-group task. The problem is that in MySQL for the sake of performance the tasks like this are usually solved not in pure SQL, but using hacks which rest on knowledge how the queries are processed internally.
In this case we know that we can define a variable and then iterating over the ready table, have knowledge about the previous row, which allows us to distinguish between the first row in a group and the others.
SELECT
question_id, answer_id, cnt,
IF(question_id=#q_id, NULL, #q_id:=question_id) as v
FROM (
SELECT
question_id, answer_id, COUNT(*) as cnt
FROM user_answers
GROUP BY 1, 2
ORDER BY 1, 3 DESC) cnts
JOIN (
SELECT #q_id:=-1
) as init;
Make sure that you have initialised the variable (and respect its data type on initialisation, otherwise it may be unexpectedly casted later). Here is the result:
| 1 | 1 | 3 | 1 |
| 1 | 2 | 1 |(null)|
| 2 | 10 | 3 | 2 |
| 2 | 11 | 1 |(null)|
Now we just need to filter out rows with NULL in the last column. Since the column is actually not needed we can move the same expression into the WHERE clause. The cnt column is actually not needed either, so we can skip it as well:
SELECT
question_id, answer_id
FROM (
SELECT
question_id, answer_id
FROM user_answers
GROUP BY 1, 2
ORDER BY 1, COUNT(*) DESC) cnts
JOIN (
SELECT #q_id:=-1
) as init
WHERE IF(question_id=#q_id, NULL, #q_id:=question_id) IS NOT NULL;
The last thing worth mentioning, for the query to be efficient you should have correct indexes. This query requires an index starting with (question_id, answer_id) columns. Since you anyway need a UNIQUE index, it make sense to define it in this order: (question_id, answer_id, user_id).
CREATE TABLE user_answers (
user_id INTEGER,
question_id INTEGER,
answer_id INTEGER,
UNIQUE INDEX (question_id, answer_id, user_id)
) engine=InnoDB;
Here is an sqlfiddle to play with: http://sqlfiddle.com/#!9/bd12ad/20.
Do you want a fish? Or do you want to learn how to fish?
Your question seems to have multiple steps.
Fetch info about "questions by users with the given answer". Devise this SELECT and imagine that the results form a new table.
Apply the "OTHER" restriction. This is probably a minor AND ... != ... added to SELECT #1.
Now find the "most common answer". This probably involves ORDER BY COUNT(*) DESC LIMIT 1. It is likely to
use a derived table:
SELECT ...
FROM ( select#2 )
Your question is multi conditional, you have to get first Questions with their asking user from Question table:
select question_id,user_id from question
Then insert the answer to the asked question and make some checks in your Java code like (is user has answered to this same question as the user who is asking this question, is user answered this question for multiple times).
select question_id,user_id from question where user_id=asking-user_id // gets all questions and show on UI
select answer_id,user_id from answer where user_id=answering-user_id // checks the answers that particular user

How to structure database for Tinder functionality [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I'm building a functionality similar to Tinder. People can 'like' or 'skip' photo's of someone else, if both people 'like' each other then there is a match.
What is the best approach of a database structure for this functionality? I want to be able to get a list of all matches and all matches per Person.
Approach 1:
Person | JudgedPerson | Like
------ | ------------ | ----
1 | 2 | yes
2 | 1 | yes
1 | 3 | yes
3 | 1 | no
2 | 3 | yes
This looks like a logical approach, but it is difficult to create a MySql query to discover matches. Or is there a simple way to discover it?
Approach 2
Person1 | Person2 | P1LikesP2 | P2LikesP1
------- | ------- | --------- | ---------
1 | 2 | yes | yes
1 | 3 | yes | no
2 | 3 | yes | null
It's easy to create queries to get matches, but the datamodel might be not the best.
What is the best approach?
If approach 1 is the best approach, what mysql queries can I use to discover the matches?
I don't have a formal reason for why I prefer the first option, but it is clear that the second option is not completely normalized.
To query the first table and find pairs of people who like each other, you can try the following self join:
SELECT DISTINCT LEAST(t1.Person, t1.JudgedPerson) AS Person1,
GREATEST(t1.Person, t1.JudgedPerson) AS Person2
FROM yourTable t1
INNER JOIN yourTable t2
ON t1.JudgedPerson = t2.Person AND
t1.Person = t2.JudgedPerson
WHERE t1.Like = 'yes' AND
t2.Like = 'yes'
Note: I added DISTINCT along with LEAST/GREATEST to the SELECT clause because each match will actually come in the form of a duplicate. The reason for this is that, e.g. 1 -> 2, 2 -> 1 would be one matching record, but also 2 -> 1, 1 -> 2 would also be a second record.
Personally, I would consider adding another option to the presented ones: having 2 tables - likes and matches:
Matches
Person1 | Person2
------ | --------
1 | 2
1 | 3
2 | 1
3 | 1
Likes
Who | Whom | Likes
--- | -----|---------
2 | 3 | 'no'
Getting matches would be a simple query:
SELECT p.*
FROM Persons p
INNER JOIN Matches m ON p.Id = m.Person2
WHERE m.Person1 = #judgedPersonId
The idea is to precompute matches instead of resolving them on each query (either in background process or during Like operation - to remove two-way likes and add records to matches tables).
This way one gets faster and easier queries when selecting matches, but the approach involves additional complexity computing "matches" and doing related queries (e.g. finding people who are not yet matched and not disliked).

How to create virtual column with multiple value using MySQL SELECT?

I can add virtual columns as
SELECT '1' as id
| id |
-------
| 1 |
But I want add multiple values, example:
SELECT ('1','2','3') as id
| id |
-------
| 1 |
| 2 |
| 3 |
But this don't work
Like Marc B said in a comment you can't have a single query split a single row into multiple rows, but you can have multiple queries, each producing one of the values, by chaining them together with union.
SELECT 1 id
UNION
SELECT 2
UNION
SELECT 3
As the answer was provided in a couple of comments I'll post it as a community wiki.

mutual non-mutual friend query mysql

Hello everyone I have been trying this for ages now.
I have read many questions here and tried adapting the varied solutions to my needs but without results.
History:
for an event there are many participants.
the participants all meet one another at the event and give out "likes" to all the other participants they actually like.
At the end of the event the admin inserts all the likes for each participant of THAT event, and the system will find the mutual likes (friendship)
Problem:
While inserting the likes i would like (pun) the system to detect weather a friendship is already established (from other events also) and if so avoid to display that user name when setting the likes.
Here are the tables that I'm using (mysql)
wp_fd_users
id | user_name | user_gender | .. etc
wp_fd_matches
id | event_id | event_user_id | event_user_match_id | ... etc
Example of the match table
1 | 1 | 1 | 3 | ...
2 | 1 | 1 | 4 | ...
3 | 1 | 2 | 6 | ...
4 | 1 | 3 | 1 | ...
where you can clearly see that 1 <-> 3 have a mutual relationship and 1 likes 4 but not mutually.
I would need a query that returns all results that AVOID relationships that have been established in one single event.
An occurance like this:
1 | 1 | 1 | 3 | ...
2 | 1 | 1 | 4 | ...
3 | 1 | 2 | 6 | ...
4 | 2 | 3 | 1 | ...
would not trigger the like because it happens in two separate events
Hope it's clear
YOur question is a little unclear. I am going by: "I would need a query that returns all results that AVOID relationships that have been established in one single event."
The following self join accomplishes this:
select m1.*
from wp_fd_matches m1 left outer join
wp_fd_matches m2
on m1.event_id = m2.event_id and
m1.event_user_id = m2.event_user_match_id
m1.event_user_match_id = m2.event_user_id
where m2.id is null
It looks for the matching record. However, by using a left outer join, it is getting all records. It then filters out the ones with a match.