Feel like this should be a rather simple problem yet, I'm struggling to find the solution.
We have three tables to create a Question Answer system. One is the question, other is answer and then the third is finally where we store the user's selection.
Question table
QuestionID Question
1 What is your favorite color?
2 Where were you born?
Answer table
AnswerID QuestionID Answer
1 1 Blue
2 1 Green
3 1 Yellow
4 2 USA
5 2 Africa
Answer stored table
AnswerStoreID QuestionID AnswerID UserID
1 1 1 1
2 1 2 1
3 2 4 2
4 2 5 2
5 1 1 3
I want to find the UserID that answered QuestionID 1 as AnswerID 1 AND QuestionID 2 as AnswerID 4.
Thought it would be simple like this
SELECT UserID
FROM Question Q
INNER JOIN Answer A ON A.QuestionID = A.QuestionID
INNER JOIN AnswerStore AS ON AS.AnswerID = A.AnswerID
WHERE (AS.AnswerID = 1 AND AS.QuestionID = 1)
AND (AS.AnswerID = 2 AND AS.QuestionID = 4)
That renders nothing though. When replacing the AND between the two where statements with an OR gets results that don't have both those answers though which is not desired either. I want only those users who answered both of these questions.
I then did a query with some various joins to do a query per question but feel that is too complicated and heavy for this problem and I'm overthinking it. Is there an easier solution to this problem?
---- Edit ----
Actually, you don't even need the JOINs in your original query:
SELECT t.UserID
FROM AnswerStore AS t
WHERE (t.AnswerID = 1 AND t.QuestionID = 1)
OR (t.AnswerID = 2 AND t.QuestionID = 4)
GROUP BY t.UserID
HAVING COUNT(*) = 2
---- Original Full Answer ----
This is actually a fairly common question, that appears a couple times a week. Unfortunately, it is really hard to formulate a repeatable/searchable question to reference for it.
SELECT UserID
FROM Question Q
INNER JOIN Answer A ON A.QuestionID = A.QuestionID
INNER JOIN AnswerStore AS ON AS.AnswerID = A.AnswerID
WHERE (AS.AnswerID = 1 AND AS.QuestionID = 1)
OR (AS.AnswerID = 2 AND AS.QuestionID = 4)
GROUP BY UserID
HAVING COUNT(*) = 2
The general form is:
SELECT A.a_id
FROM A
INNER JOIN B ON A.a_id = B.a_id
WHERE B.something IN ([list])
GROUP BY a_id
HAVING COUNT(*) = [length of list]
-- or in cases where B matches may be non-unique
-- HAVING COUNT(DISTINCT B.something) = [length of list]
You are really looking at two sets of data, UserIDs that answered QuestionID 1 as AnswerID 1, and UserIDs that answered QuestionID 2 as AnswerID 4. So you can join the sets together to find UserIDs that are in both sets of data:
SELECT UserID
FROM AnswerStore as1 INNER JOIN AnswerStore as2 ON as1.UserID = as2.UserID
AND as1.QuestionID = 1 AND as1.AnswerID = 1
AND as2.QuestionID = 2 AND as2.AnswerID = 4
Related
Title of the question may not have been very clear - I am not really sure how to name this question, but I hope that my explanation will make my problem clearer.
I have 3 tables:
[1] score
id
rating_type
1
UPVOTE
2
UPVOTE
3
DOWNVOTE
4
UPVOTE
5
DOWNVOTE
6
DOWNVOTE
[2] post_score
post_id
score_id
1
1
1
2
1
3
2
4
2
5
2
6
and [3] post
id
title
1
title1
2
title2
My goal is to order [3] post table by score.
Assume UPVOTE represents value of 1 and DOWNVOTE value of -1; In this example, post where id = 1 has 3 scores related to it, and the values of them are UPVOTE, UPVOTE, DOWNVOTE, making the "numeric score" of this post: 2;
likewise, post where id = 2, also has 3 scores, and those values are: UPVOTE, DOWNVOTE, DOWNVOTE, making the "numeric score": -1;
How would I order post table by this score? In this example, if I ordered by score asc, I would expect the following result:
id
title
2
title2
1
title1
My attempts didn't go far, I am stuck here with this query currently, which doesn't really do anything useful yet:
WITH fullScoreInformation AS (
SELECT * FROM score s
JOIN post_score ps ON s.id = ps.score_id),
upvotes AS (SELECT * FROM fullScoreInformation WHERE rating_type = 'UPVOTE'),
downvotes AS (SELECT * FROM fullScoreInformation WHERE rating_type = 'DOWNVOTE')
SELECT p.id, rating_type, title FROM post p JOIN fullScoreInformation fsi on p.id = fsi.post_id
I am using PostgreSQL. Queries will be used in my Spring Boot application (I normally use native queries).
Perhaps this data structure is bad and I should have constructed my entities differently ?
My goal is to order post table by score. Assume UPVOTE represents value of 1 and DOWNVOTE value of -1
One option uses a subquery to count the upvotes and downvotes of each post:
select p.*, s.*
from post p
cross join lateral (
select
count(*) filter(where s.rating_type = 'UPVOTE' ) as cnt_up,
count(*) filter(where s.rating_type = 'DOWNVOTE') as cnt_down
from post_score ps
inner join score s on s.id = ps.score_id
where ps.post_id = p.id
) s
order by s.cnt_up - s.cnt_down desc
Perhaps this data structure is bad and I should have constructed my entities differently ?
As it stands, I don't see the need for two distinct tables post_score and score. For the data you have showed, this is a 1-1 relationship, so just one table should be sufficient, storing the post id and the rating type.
You better use a LEFT join, otherwise you wouldn't get posts that have no votes yet. Then aggregate to get the fitered sum of the scores. Then add these sums, apply coalesce() to get 0 for posts without votes and order by the result.
SELECT p.id,
p.title
FROM post p
LEFT JOIN post_score ps
ON ps.post_id = p.id
LEFT JOIN score s
ON s.id = ps.score_id
GROUP BY p.id,
p.title
ORDER BY coalesce(sum(1) FILTER (WHERE rating_type = 'UPVOTE')
+
sum(-1) FILTER (WHERE rating_type = 'DOWNVOTE'),
0);
I second GMB's comment about the superfluous table.
I have three tables with the following (relevant) columns:
questions: level, answer_id
choices: question_id, answer_id, user_id, date_answered
users: id, level
Users are presented with the questions and when they answer them by selecting one of the available answers, this creates a choices record and assigns the answer_id the appropriate value. Users can answer a question multiple times.
I need a query that will tell me, taking into account only the latest choice for each answered question, how many were answered correctly. I know enough SQL to be dangerous, but this one is above my head.
As an example:
questions
id level answer_id
------------------
1 1 101
2 1 102
3 1 103
4 2 110
choices
question_id answer_id user_id date_answered
--------------------------------------------
1 101 201 2019-01-24
1 104 201 2019-01-25
2 105 201 2019-01-25
2 102 201 2019-01-26
3 103 201 2019-01-26
So user 201 answered the first question correctly and then incorrectly. Then they answered the second question incorrectly and then correctly. Finally, they answered question 3 once and it was correct.
Note that this is all within a question level. If the user is at level 1, I'd only be considering choices to level 1 questions. If at level 2, only choice to level 2 question.
Although they've answered all the level 1 questions correctly, they've only answered level 1 most recently correctly twice. 2 would be the result I'm looking for, therefore.
Is there a SQL query that can do this or must I use a loop within the programming?
I don't currently have SQL Server setup so there might be some typos in my answer. This should get you questions answered correctly only if it is the most recent answer by that user.
SELECT *
FROM
questions a INNER JOIN
choices b on (a.question_id = b.question_id) INNER JOIN
( SELECT question_id, user_id, max(date_answered) as date_answered
FROM choices
GROUP BY question_id, user_id
) c
on (b.question_id = c.question_id and b.user_id = c.user_id and b.date_answered = c.date_answered)
Answer to updated question:
SELECT *
FROM
questions a INNER JOIN
choices b on (a.id = b.question_id) INNER JOIN
( SELECT question_id, user_id, max(date_answered) as date_answered
FROM choices
GROUP BY question_id, user_id
) c INNER JOIN
on (b.question_id = c.question_id and b.user_id = c.user_id and b.date_answered = c.date_answered) INNER JOIN
users d on (d.id = b.user_id and d.level = a.level)
I have the following two tables in Oracle database (read only access so I can only use select).
Question table:
N Question ID
1 1
2 2
3 3
Response table:
Question ID Response day
1 01-04-15
3 02-04-15
4 03-04-15
I want the output result to be:
Question ID Response day
1 01-04-15
2 null
3 02-04-15
4 03-04-15
I m strying with the following query and I'm ony getting the question ID when there is a response. Anyone know where I'm going wrong?
select questionID, responseday
from questions
join response
where question.question.ID = response.question.ID;
With this query I'm getting these results:
Question ID Response day
1 01-04-15
3 02-04-15
4 03-04-15
One method is a full outer join:
select coalesce(q.questionid, r.questionid) as questionid,
r.responseday
from questions q full outer join
responses r
on q.questionid = r.questionid;
Based on your schema, a simple LEFT JOIN will be sufficient for your model.
SELECT q.Question_ID,
r.Response_Day
FROM Question AS "q"
LEFT JOIN Response AS "r"
ON r.Question_ID = q.Question_ID;
JOIN or INNER JOIN will only return the result sets from both tables where ALL attributes match. Because your Question record with ID of 2 does not exist in the Response table, SQL will exclude this data.
To account for NULLs, you have to LEFT JOIN, see the sqlfiddle here:
SQL Fiddle
Also, have a look at this graphical model explaining all the JOINs:
Visual Representation of SQL Joins
Use Left Join
select
q.questionid as questionid,
r.responseday
from
questions q
left join
responses r
on
q.questionid = r.questionid;
FULL outer JOIN is sufficient or the output required.
SELECT NVL(a.id,b.id),
b.nm
FROM
(SELECT 1 id,1 nm FROM dual
UNION ALL
SELECT 2 id,2 nm FROM dual
UNION ALL
SELECT 3 id,3 nm FROM dual
)a
FULL OUTER JOIN
(SELECT 1 id,'01-04-15' nm FROM dual
UNION
SELECT 3 id,'02-04-15' nm FROM dual
UNION ALL
SELECT 4 id,'03-04-15' nm FROM dual
)b
ON a.id = b.id;
---------------------------------OUTPUT -----------------------------------
ID NM
1 01-04-15
3 02-04-15
4 03-04-15
2 NULL
----------------------------------OUTPUT-----------------------------------
select questionID
, responseday
from questions
join response
on question.question.ID = response.question.ID;
I have a simple SQL query I just get to work out right. I've put together a test database like this;
TABLE MAIN
id name groupone grouptwo
1 Fred 1 3
2 bob 2 1
TABLE DETAIL
id group groupname
1 1 onegrp
2 2 twogrp
4 3 threegrp
My Select query is;
SELECT name, groupone, grouptwo, groupname
FROM main
INNER JOIN detail
ON main.groupone = detail.group
WHERE main.id = 1
The result I get is;
id name groupone grouptwo groupname
1 fred 1 3 onegrp
How do I change this to instead of giving the result as 1 and 3.
I get ... fred onegrp, threegrp
I've tried a dozen things but can't get it to work, I sort of want a give me the groupname again option but not sure what the syntax is for that! :(
Thanks in advance for your time and help
I think this is what you are after. You need to join the detail table twice, on the two different keys.
SELECT
m.Name
,a.groupname as 'GroupOne'
,b.groupname as 'GroupTwo'
FROM
main m
INNER JOIN
detail a
on m.groupone = a.group
INNER JOIN
detail b
on m.grouptwo = b.group
WHERE
m.id = 1
I have an application where users can take tests (which are composed of questions and answers).
I'm trying to construct a query that returns a count of answers grouped by question, for a specific teacher and test.
The problem is I would like the query to return 0 count for questions with no answers.
answers
id
question_id
test_id
student_id
questions
id
teacher_students
student_id
teacher_id
tests
id
Query
SELECT COUNT(answers.id) AS rcount,
questions.id
FROM "questions"
LEFT JOIN answers ON answers.question_id = questions.id
LEFT JOIN teacher_students ON teacher_students.student_id = answers.student_id
WHERE (questions.test_id = 1)
AND (teacher_students.teacher_id = 1)
GROUP BY questions.id
ORDER BY questions.id
Output
rcount | question_id
--------+----
4 | 1
2 | 3
Desired Output
rcount | question_id
--------+----
4 | 1
0 | 2
2 | 3
0 | 4
If I remove teacher_students.teacher_id = 1, the number of questions returned is correct, but the count is not.
Move the teacher_students.teacher_id = 1 check from the WHERE clause to the joining ON clause.
When a condition that refers to a table in the right side of a LEFT JOIN is put in the WHERE clause, the LEFT JOIN is cancelled and it acts as an INNER JOIN.
SELECT count(teacher_students.student_id) AS rcount <--- changed
, questions.id
FROM "questions"
LEFT JOIN answers
ON answers.question_id = questions.id
LEFT JOIN teacher_students
ON teacher_students.student_id = answers.student_id
AND teacher_students.teacher_id = 1
WHERE questions.test_id = 1
GROUP BY questions.id
ORDER BY questions.id