Selecting multiple values from multiple tables - mysql

MySQL.
I have two tables, one is "Questions" and the other is "Answers"
The Questions table:
- question_id
- user_id
- question
The Answers table:
- answer_id
- question_id
- user_id
- answer
- correct
The goal is to get all questions (and associative answers) based on a user's id. I've been able to get all of the answers, however I'm only getting one question. I can see why it's only getting a single question, but I don't have any idea how to go about getting the question text for each answer.
Here's the code that I'm using right now. Where id_in is an input value on a saved procedure. The issue is that it gives me all of the answers for each question, but all of them return the same question text. I feel like possibly a type of join would be better here, but we haven't started learning about them yet and I hardly know anything about them as is.
BEGIN
DECLARE question_text VARCHAR(40);
SELECT question INTO question_text FROM questions WHERE user_id = id_in;
SELECT question_text, Q.* FROM answers AS Q WHERE user_id = id_in;
END
Yes, this is homework. I'm just completely lost as to what I need to be doing.

Left joins allow for All things in the left table, and only the matching things in the right table. In my example I may have A and Q mixed up but I think this is the general gist of it. You can also take the user_id = in_id and move that to a wear, but filter on the join should be faster.
SELECT
Q.QUESTION
, A.ANSWER
, A.CORRECT
FROM ANSWERS A
LEFT JOIN QUESTION Q
ON A.QUESTION_ID = Q.QUESTION_ID
AND A.USER_ID = Q.USER_ID
AND A.USER_ID = ID_IN
AND Q.USER_ID = ID_IN

Related

How tor write a query to perform two different selection from same table

I am working on a table that is similar to stack overflow. It stores questions answers and comments.
I tried to create a query that retrieves data in the following way. A question and its comments and answer for this question and comments for the answer.
I don't know how to retrieve all this information from the same table. If they were in different tables, it would be easy. I tried inner join and using sub-query but they didn't give me the correct output.
I want to use this query in spring-data-jpa. First, I tried to make it with jpa but it doesn't work. I think this query is more complex to create it directly with spring-data-jpa.
these are the important fields of my table in MySQL:
id | content_id | content_type_id | subject | body | tags | user_id | dtype
content_id is using for answers and comments and shows the id of a question that answer or comment belongs to. dtype specify the type of record it has one of the Question, Answer or Comment values.
these are queries that I tried:
select distinct T1.`content_id`,
(Select `body`,`dtype` from content As T3 where T3.content_id = 19 and
T3.content_type_id = 2),
(Select `body`,`dtype` from content As T4 where T4.content_type_id = 3 and
T4.content_id = T3.id)
from content as T1
SELECT a.body, a.user_id, a.dtype, c.body, c.user_id, c.dtype FROM content a
JOIN content c ON a.id = c.content_id WHERE a.content_id = 19
this is the method that I tried to use in spring boot
public Question getQuestionAnswerAndComment(Question question){
List<List> result = new ArrayList<>();
Optional<Question> ques = getQuestion(question); // getting one question by id
result.add(questionRepository.findByContentTypeIdAndContentId(3, question.getId())); //getting comments of the question (3 means comment)
Answer ans = questionRepository.findByContentTypeIdAndContentIdAnswer(2, question.getId()); // getting answers of the question (2 measn answer)
}
the problem here is the third method call that selects all the answers for a question and return a list so I can't use its output to find comments for a specific answer.
now what should I do to achieve my desired data from database, I should use spring-data-jpa methods or create a custom query and then call it.
Desired result:
Question
----Comments of the question
The first answer to the question
----Comments of the first answer
The second answer to the question
----Comments of the second answer
I would start with a select like the following which should select all the data that belongs to a question along with it in consecutive rows.
select * from content as questions
left join content as comment_or_answer
on questions.id = comment_or_answer.content_id
left join content as answer_comments
on comment_or_answer.id = answer_comments.content_id
where questions.content_type_id = <question-type-id>
and <add constraints on the question here, e.g. on the id>
order by questions.id, comment_or_answer.content_type_id, comment_or_answer.id
This gives you a threefold join of the table with questions in the first set of columns, comment and answers in the second set of columns and comments to answers in the third set of columns.
I then would execute that query using a NamedParameterJdbcTemplate and use a ResultSetExtractor to construct a result object from it.

Show only results from table where a certain combination of variables is not present in other table?

I want to build a system in which users can submit quiz questions and users can also rate those questions.
There are two tables relevant to my problem:
"Question", which contains a questionID, a userID(from the user who
submitted the question), a correct answer, a wrong answer, another
wrong answer and a boolean that is set to true when the question
survived the validation process.
"Validation", which contains a questionID for which question is validated, a UserID for which user validated it and a validation (0,
1 or 2 depending on the rating the user gave the question).
To give users questions that need rating, I need a MySQL query with which I receive a question that is:
not through validation yet (validated bool = false)
not made by the user requesting it
not already validated by the user requesting it
the first question in the list of results with these factors
EDIT: I found a solution, look at the bottom edit.
I have tried the following query:
set #UserId=5;
SELECT q.id, q.question, q.correct_answer, q.wrong_answer1, q.wrong_answer2
FROM question q
LEFT JOIN validation v ON v.question_id=q.id
WHERE q.validated = 0
AND q.user_id!=#UserId
AND v.user_id!=#UserID
ORDER BY q.id
LIMIT 1
I exclude questions already through validation with WHERE q.validated = 0.
I make sure it's the first question in the list of results with ORDER BY q.id LIMIT 1
I exclude questions made by the user requesting it with q.user_id!=#UserId
This query returns nothing, though.
The question table contains some unvalidated questions. The validation table is empty.
I know the mistake lies somewhere within the LEFT JOIN validation v ON v.question_id=q.id and v.user_id!=#UserID parts, but I don't know how to translate my will to MySQL..
EDIT: I found a solution that worked for my problem:
set #UserId=5;
SELECT q.id, q.question, q.correct_answer, q.wrong_answer1, q.wrong_answer2 FROM question q
WHERE NOT EXISTS
(SELECT * FROM validation v WHERE v.user_id=#UserID AND q.id = v.question_id)
AND q.validated = 0 AND q.user_id!=#UserId
But, I read this method is very bad for performance.
Is there a more performant method?
You have to check for null in v.user_id because there is no entry.
This query works:
set #UserId=5;
SELECT q.id, q.question, q.correct_answer, q.wrong_answer1, q.wrong_answer2
FROM question q
LEFT JOIN validation v ON v.question_id=q.id
WHERE q.validated = 0
AND q.user_id!=#UserId
AND v.user_id is null or v.user_id!=#UserId
ORDER BY q.id
LIMIT 1

Filtering Results with JOIN

I have page called questions where the user gets asked questions and he/she has the option to answer them. The questions are pulled from a table called questions. When a question gets answered, a table in my database called answered_questions registers the id of the question answered and the id of the user who answered the question. The purpose of this is to hide the answered questions when the user accesses the page again.
On page load I'm trying to join the two tables and see if the question_id exists in both tables where the userID is that of the logged in user. If the id does exist in both tables then it shouldnt display the result per the use of <>. Problem is that its looping several times for each iteration when I try the following query:
SELECT questions.question_id, questions.user_id
FROM `questions`
JOIN `answered_questions`
ON questions.question_id <> answered_questions.question_id
WHERE answered_questions.user_id = ".$userID."
But it works fine when I use this
SELECT questions.question_id, questions.user_id
FROM `questions`
JOIN `answered_questions`
ON questions.question_id **=** answered_questions.question_id
WHERE answered_questions.user_id = ".$userID."
I sense that I'm doing something wrong with the logic of it all. Any help or clues would be highly appreciated.
To get unanswered questions You can use LEFT JOIN:
SELECT questions.question_id, questions.user_id
FROM questions
LEFT JOIN answered_questions
ON answered_questions.question_id = questions.question_id
AND answered_questions.user_id = ".$userID."
WHERE answered_questions.question_id IS NULL

MySQL joining table to itself and comparing results

MySQLFiddle here: http://sqlfiddle.com/#!2/15d447/1
I have a single table I am trying to work with:
Table 1: user_answers table (stores users answers to various questions)
The notable values that are stored are the users id (column uid), the question id for the question they are answering (column quid), the answer to the question (column answer) and the importance of their answer (column importance).
The end result I want:
I'd like to be able to grab all the questions that any two users have answered, excluding any answers from questions that have either not been answered by the other party, or answers to the same question which have a value of 1 for either user in importance. Again, this will only ever be used to compare two users at a time.
I've been pretty unsuccesful in my attempts, but here is what I've tried, just piecing things together:
#attempt one: trying to exclude answers that were not answered by both users
SELECT * FROM user_answers AS uid1
JOIN user_answers AS uid2 ON uid1.uid = uid2.uid
WHERE uid1.uid = 1
AND uid2.uid = 20008
AND uid1.quid IS NOT NULL
AND uid2.quid IS NOT NULL;
This returns no results but I'm not exactly sure why.
#attempt two: trying to exclude where answers are the same for both users
SELECT * FROM user_answers AS uid1
LEFT JOIN user_answers AS uid2 ON (uid1.uid = uid2.uid AND uid1.answer <> uid2.answer)
This gives me results, but seems to be doubling up on everything because of the join. I also tried in this attempt to eliminate any answers what were the same, which seems to be working in that sense.
Any guidance is appreciated.
Thanks.
You can answer your question using an aggregation query. The idea is to using the having clause to filter the rows for the conditions you are looking at.
Because you are not interested at all in questions with importance = 1 those are filtered using a where clause:
select ua.quid
from user_answers ua
where importance <> 1 and uid in (1, 20008)
group by ua.quid
having sum(uid = 1) > 0 and
sum(uid = 20008) > 0;
If you want to include the answers, you can do:
select ua.quid,
group_concat(concat(uid, ':', answer) order by uid) as answers
Just a simple version of what you need.
select *
from user_answers a,
user_answers b
where a.quid = b.quid
and a.uid <> b.uid
and 1 not in (a.importance, b.importance)
If you like to filter just the questions just change the * for distinct a.quid
See it here on fiddle: http://sqlfiddle.com/#!2/15d447/15

How do I fix this query?

I'm writing an application where people ask questions, and get answers in the form of a survey. Each question has 2 options, plus a default option(s). When a person answers the question, they can choose from either the 2 options set by the asker, or the default option(s) chosen by me. For instance, if the question is Vanilla vs. Chocolate, options will be Vanilla, Chocolate, and Neither. I want to be able to tabulate the percentage of options chosen for a question, i.e., 25% say chocolate, 30% say vanilla, 45% say neither.
I'll start by showing the table structure and the query I'm running.
These are the tables involved (Note: these are not the full table structures):
--questions--
id
user_id
topic
description
--options--
id
text
default (bool)
--questions_options--
question_id
option_id
--answers--
id
question_id
user_id
option_id
Here is the query:
SELECT
options.id AS option_id, options.text, options.default,
ROUND(
IFNULL(
(COUNT(answers.option_id) * 100)
/
(SELECT COUNT(answers.option_id) FROM answers WHERE question_id = QUESTION_ID)
, 0)
, 2) AS percentage
FROM options
LEFT JOIN questions_options ON questions_options.option_id = options.id
LEFT JOIN answers ON answers.option_id = options.id
WHERE questions_options.question_id = QUESTION_ID
OR options.default = '1'
GROUP BY options.id
ORDER BY percentage DESC, option_id ASC
Where QUESTION_ID is an integer constant.
The problem is the query is not limiting answers to only those given for a particular question, and because the options are many to many with questions, I'm getting results like 600% for vanilla (if multiple questions use vanilla as an option). In cases where the options are unique to ONE question, then the percentages make sense, except for the default options, which are present for all questions. I tried putting WHERE answers.question_id = QUESTION_ID in there, but it did not work.
Any solutions?
Thanks
You're doing the joins in the wrong direction - you're looking at options first, even though you have specifically stated you want things tabulated by question. This means that you're getting results for all options, regardless of whether or not they even relate to your question...
Oh, and I'm assuming that answer_id is mapped to question_id, or you're not going to be able to get any meaningful results (that is - answers are not otherwise mapped to questions...)
Try this query instead:
SELECT b.id, b.text, b.default, (SELECT IFNULL(
ROUND((COUNT(c.id) * 100) /
(SELECT COUNT(d.id)
FROM answers as d
WHERE d.answer_id = a.question_id)
, 2)
, 0)
FROM answers as c
WHERE c.answer_id = a.question_id
AND c.option_id = a.option_id) as percentage
FROM questions_options as a
JOIN options as b
ON b.id = a.option_id
WHERE a.question_id = QUESTION_ID
ORDER BY percentage DESC, a.option_id ASC
Please note that I don't have a copy of MySQL to run this against, and I would normally implement with CTEs (which I have been informed are not supported for MySQL).
EDIT:
In light of the fact that 'default' options may not be mapped through the questions_options table, try this:
SELECT a.id, a.text, a.default, IFNULL(
ROUND((b.answerCount * 100) /
(SELECT COUNT(c.id)
FROM answers as c
WHERE c.answer_id = QUESTION_ID)
, 2)
, 0)
FROM options as a
LEFT JOIN (SELECT c.option_id, count(c.id) as answerCount
FROM answers as c
WHERE c.question_id = QUESTION_ID
GROUP BY c.option_id) as b
ON b.option_id = a.id
Please note that you will still get "meaningless" '0' results for every 'default' answer that was not presented to survey respondents - and no way to distinguish these from any actual '0' results results for 'default' answers that were presented to respondents. You are likely to be far better off placing the so called 'default' options in the questions_options table - as it is, you have no way to determine all the options that were presented to respondents (just which ones you have answers to, which is quite different); this may be a huge business-accountability issue for your company. In addition, some 'default' options may not make sense in context - "Do you prefer your tea hot or cold", "Yes".
Issues that I can see:
You GROUP BY options.id which means you are getting random values for options.text and options.default. This may or may not change your results depending on the structure of your data. If there are multiple rows per id then it will be inaccurate or misleading data.
You have a WHERE clause for your divisor but not your dividend in the percentage calculation - this means you will never have a lower count for the dividend. Try putting a WHERE question_id = QUESTION_ID to the first COUNT statement.