Get Questions if Not Answered - mysql

Basically, there are three tables with the following structure
First table: tournament_questions, having all the questions
id tournament_id question active created_at updated_at
1 5 Text question 1 2018-12-08 20:28:49 NULL
Second table: tournament_options, having all the options
id question_id option correct active created_at updated_at
1 1 1 1 1 2018-12-08 20:29:02 NULL
2 1 26 0 1 2018-12-08 20:29:02 NULL
Third Table: tournament_user_answers, having all the user answers.
id option_id user_id score active created_at updated_at
This table has no data at this time.
I want to achieve all the questions that are not answered by the users and hence the query should return the first question. Here is the query that I tried, but it always returns null
SELECT * FROM tournament_user_answers
INNER JOIN tournament_options ON tournament_user_answers.option_id =
tournament_options.id AND tournament_options.active = 1
LEFT JOIN tournament_questions ON tournament_questions.id =
tournament_options.question_id AND tournament_questions.active = 1
WHERE tournament_questions.tournament_id = 5 AND
tournament_questions.active = 1
AND tournament_questions.id IS NULL AND tournament_user_answers.user_id = 1
LIMIT 1

You start with FROM tournament_user_answers (which is empty) and you do a LEFT JOIN which includes all rows on the left hand (which was empty) and appending on those the data on the right hand if available. empty LEFT JOIN data will be empty.
SELECT
tournament_questions.*
FROM tournament_questions
JOIN tournament_options
ON tournament_options.question_id = tournament_questions.id
AND tournament_options.active = 1
LEFT JOIN tournament_user_answers
ON tournament_user_answers.option_id = tournament_options.id
AND tournament_user_answers.user_id = 1
WHERE
tournament_questions.tournament_id = 5
AND tournament_questions.active = 1
GROUP BY tournament_questions.id
HAVING MAX(tournament_user_answers.id) IS NULL
ORDER BY tournament_questions.id ASC
In this case the left part (questions + options) has data, and the answers are appended if available. By including a MAX(tournament_user_answers.id) IS NULL in your HAVING you get all questions where there is NO answer.

Maybe this would work for your case (reduced version):
SELECT q.id, q.question, ua.id ua_id FROM tournament_questions q
INNER JOIN tournament_options o ON q.id = o.question_id
LEFT JOIN tournament_user_answers ua ON o.id = ua.option_id
GROUP BY q.id
HAVING ua_id IS NULL

Related

Contiditional WHERE with two joins on same table

I'm trying to create an effective query but can't get it working.
Tables:
- one table containing types of objects
- one table containing objects
Conditions:
- there can be single objects of a type
- there can be child objects of a type
- parent and child objects don't need to be of the same type
- objects can be published
- types can be published
- the results should only get pulled from a specific pool of object IDs. So i need to add AND (o.id IN (1,2,3,4)
I want a simple result list that shows how many types are published and the number of objects assigned to these types.
types
id | title | published
---------------------
1 type1 1
2 type2 1
3 type3 1
4 type4 1
5 type5 1
6 type6 0
7 type7 1
objects
id |title | type | parent | published
---------------------------------------
1 a 1 0 1
2 b 1 0 1
3 c 3 2 1
4 d 2 0 1
5 e 2 2 1
6 f 4 0 0
7 g 5 6 1
8 h 6 0 1
9 i 3 8 1
10 j 3 8 0
11 k 7 8 1
Results should be:
type1 (#2) (two singles)
type2 (#2) (one single + one child of id 2)
type3 (#3) (one child of id 2 + one published child of id 8)
type4 (#0) (one single not published)
type5 (#0) (because it's parent id 6 is not published)
type6 (#0) (because type6 is not published)
I tried this one (type publishing not included):
SELECT o.type, t.title, COUNT(t.id) AS cnt
FROM types AS t
LEFT JOIN objects AS o ON o.type = t.id
LEFT JOIN objects AS o2 ON o.id = o2.parent
WHERE o.published = 1 AND o2.published = 1
GROUP BY o.type
The conditions in the WHERE clause negate the "outerness" of the left joins.
Move those conditions to the ON clauses. The WHERE clause can be dropped.
Also, reference columns from t, the driving table, and count non-NULL expressions from the outer joined tables.
That will allow the query to return zero counts.
I didn't fully delve into the specification, but it looks like we want to count matching rows from o and o2.
I think something like this will get a resultset consistent with one interpretation of the specification... child o2 rows get counted under parent o type, regardless of the type on the child o2 row.
This is not tested, and I'm not fully understanding the specification...
SELECT t.id AS `type`
, t.title AS `title`
, COUNT(DISTINCT o.id)
+ COUNT(DISTINCT o2.id) AS `cnt`
-- , COUNT(DISTINCT o.id) AS `cnt_o`
-- , COUNT(DISTINCT o2.id) AS `cnt_o2`
FROM types t
LEFT
JOIN objects o
ON o.type = t.id
AND o.published = 1
AND o.parent = 0
AND t.published = 1
LEFT
JOIN objects o2
ON o2.parent = o.id
AND o2.published = 1
GROUP
BY t.id
, t.title
Not clear in the spec...
Do child rows (from o2) get omitted from the count if the type on the o2 row matches a row in types that is published=0 ?
If we are "grouping" by type on the o2 rows , then we'd need to something different,
EDIT
we could get the count from the parent and the child separately, in two separate SELECT, and then combine the two resultsets with a UNION ALL set operator, and then total up the counts.
something along these lines:
SELECT c.type
, c.title
, SUM(c.cnt) AS cnt
FROM (
SELECT t.id AS `type`
, t.title AS `title`
, COUNT(o.id) AS `cnt`
FROM types t
LEFT
JOIN objects o
ON o.type = t.id
AND o.published = 1
AND o.parent = 0
AND t.published = 1
GROUP
BY t.id
, t.title
UNION ALL
SELECT tc.id AS `type`
, tc.title AS `title`
, COUNT(oc.id) AS `cnt`
FROM types tc
JOIN objects oc
ON oc.type = t.id
AND oc.published = 1
AND t.published = 1
JOIN objects op
ON op.id = oc.parent
AND op.published = 1
JOIN types pt
ON pt.id = op.type
AND pt.published = 1
GROUP
BY tc.id
, tc.title
) c
GROUP
BY c.type
, c.title
again, untested, and without a full understanding of the spec.
the count of the parent o is straightforward. we use an outer join, with t as the driving table, so we get all types, and can get zero counts.
the count of the child oc, we can do inner joins. since the previous SELECT is getting us all the types, missing rows in the second SELECT won't cause a problem.
note that we join the child o2 rows by type, and then we join to parent (to make sure parent is published), and join to parent type (to check that type is published) ...
How do we distinguish "parent" rows, do we check parent=0 ?
Is this a hierarchy, can a "child" also be the "parent" of another row ?
FOLLOWUP
Another way to think about it (maybe this was the approach of the OP query) ... we are counting rows from o, parents and children. What's important is that the type is published type, and that o is published.
Additionally, either
o is not a child (i.e. there isn't a row in objects op that has an id value equal to `o.parent)
or
if o does have a parent row (a row in objects op with an id value equal to o.parent, the [parent op is published and the parent type is published.
We could approach it like this:
SELECT t.id AS `type`
, t.title AS `title`
, COUNT(o.id) AS `cnt`
FROM types t
LEFT
JOIN objects o
ON o.type = t.id
AND o.published = 1
AND t.published = 1
LEFT
JOIN objects op
ON op.id = o.parent
LEFT
JOIN types pt
ON pt.id = op.type
WHERE -- this not a child (there is no parent)
op.id IS NULL
OR -- parent is published and parent type is published
( op.published = 1 AND pt.published = 1 )
GROUP
BY t.id
, t.title

Sql Query with 1 Join and 2 Where Clauses not returning all records

So guys, trying to write a query to get the count of statuses where project_id = ? and statuses in 'New' from a couple of tables so let me break it down.
I have these three tables
Case_Status
id case_status
1 New
2 Failed
3. Accepted
Referral
id case_status_id project_id application_id
1 1 1 20
2 2 1 21
Project
id name
1 project1
2 project2
So this is my query
SELECT COUNT(referrals.id) AS count_all, case_statuses.case_status AS counted
FROM "case_statuses" LEFT OUTER JOIN "referrals" ON "referrals"."case_status_id" = "case_statuses"."id"
WHERE "case_statuses"."deleted_at" IS NULL AND (case_statuses.case_status IN ('New') AND referrals.project_id = 1)
GROUP BY case_statuses.case_status;
This is my result
count_all counted
1 New
1 Failed
But I am expecting this result instead
count_all counted
1 New
1 Failed
0 Accepted
Does anyone know what's wrong with my query that isnt showing count for all the case_statuses?
Thanks
Conditions on the second table (in a left join) should be in the on clause:
SELECT COUNT(r.id) AS count_all, cs.case_status AS counted
FROM case_statuses cs LEFT OUTER JOIN
referrals r
ON r.case_status_id = cs.id AND r.project_id = 1
WHERE cs.deleted_at IS NULL AND cs.case_status NOT IN ('New')
GROUP BY cs.case_status;
Otherwise, the WHERE clause turns the outer join into an inner join.
change your query like this
SELECT COUNT(referrals.id) AS count_all, case_statuses.case_status AS counted
FROM "case_statuses" LEFT JOIN "referrals" ON "referrals"."case_status_id" = "case_statuses"."id" AND referrals.project_id = 1
WHERE "case_statuses"."deleted_at" IS NULL AND case_statuses.case_status NOT IN ('New')
GROUP BY case_statuses.case_status;
Given your data and the expected result you just need to loose the WHERE clause.
SELECT COUNT(referrals.id) AS count_all, case_statuses.case_status AS counted
FROM case_statuses
LEFT OUTER JOIN referrals ON referrals.case_status_id = case_statuses.id
GROUP BY case_statuses.case_status;
See this fiddle for details.

Conditionals and counts in SQL

I have a MySQL table
"ratings", with
an ID column
a column called like_dislike (holds either null, 0, or 1),
and a column called lesson_id (a foreign key from lessons).
the MySQL table, "lessons", with
an ID column
a teacher_id column
I need to select this information:
"SELECT r.like_dislike FROM ratings r INNER JOIN lessons l on l.lesson_id = r.lesson_id";
However, this actually part of a much larger SQL statement, and what I would like to do is:
Foreach lesson_id, if like_dislike == 0, SELECT count(like_dislike) as like
AND
Foreach lesson_id, if like_dislike == 1, SELECT count(like_dislike) as dislike
I do not know how to turn this pseudo code into SQL. I also need to do this in SQL, rather than in something like PHP, because it is part of a larger SQL statement whose conversion into properly formatted arrays is deeply troubling.
You should be able to accomplish this with grouping. For example:
SELECT r.lesson_id, COUNT(*) AS like
FROM ratings r
INNER JOIN lessons l ON l.lesson_id = r.lesson_id
WHERE r.like_dislike = 0
GROUP BY r.lesson_id;
The same for dislike, just change the WHERE clause to
WHERE r.like_dislike = 1
EDIT:
This can be combined into one query as requested, by adding another level of grouping:
SELECT r.lesson_id, r.like_dislike, COUNT(*) AS count
FROM ratings r
INNER JOIN lessons l ON l.lesson_id = r.lesson_id
GROUP BY r.lesson_id, r.like_dislike;
This will give you output, for example:
+-----------+--------------+-------+
| lesson_id | like_dislike | count |
+-----------+--------------+-------+
| 1 | 0 | 12 |
| 1 | 1 | 7 |
| 2 | 0 | 1 |
| 2 | 1 | 4 |
+-----------+--------------+-------+
so for lesson_id of 1, there are 12 likes, and 7 dislikes, etc...
EDIT 2:
To get one row for each lesson_id, you can modify the statement a little:
SELECT r.lesson_id,
CASE WHEN r.like_dislike = 0 THEN COUNT(*) END AS like,
CASE WHEN r.like_dislike = 1 THEN COUNT(*) END AS dislike
FROM ratings r
INNER JOIN lessons l ON l.lesson_id = r.lesson_id
GROUP BY r.lesson_id, r.like_dislike;
For that matter, you don't even need to join on the lessons table at all, unless you are somehow getting ratings that do not link to a lesson. If you want to include lessons that have no ratings, then you will have to change to an OUTER join:
SELECT l.lesson_id,
CASE WHEN r.like_dislike = 0 THEN COUNT(*) END AS like,
CASE WHEN r.like_dislike = 1 THEN COUNT(*) END AS dislike
FROM lessons l
LEFT JOIN ratings r ON r.lesson_id = l.lesson_id
GROUP BY l.lesson_id, r.like_dislike;
Maybe you can use a case statement, something like this:
SELECT r.lesson_id,
case when r.like_dislike == 0 then (count(*)) end as like,
case when r.like_dislike == 1 then (count(*)) end as dislike
FROM ratings r INNER JOIN lessons l ON l.lesson_id = r.lesson_id
GROUP BY r.lesson_id, r.like_dislike
I haven't tested it, but you can see the idea. Further, you must set a case to count 1's and another to count 0's because like_dislike can be null.
try this
SELECT if(r.like_dislike =0 ,count(like_dislike) as like , if( r.like_dislike =1 , count(like_dislike) as dislike, 'its null'))
FROM ratings r
INNER JOIN lessons l on l.lesson_id = r.lesson_id
if you are adding condition on yr datatable then code it like below:
Declare #Counter int
Set #Counter=(SELECT Count(Student_ID) as 'StudentCount' FROM tbCourseSemOne
where Student_ID=1 Having Count(Student_ID) < 6 and Count(Student_ID) > 0)
if(#Counter <6)
print'Sorry! You cannot add more than five subject data for a single stduent'
else
print'Insert Code'
hope it helps

LEFT JOIN with a condition

I need to join two tables. I only want to show matches where users.private = 0
feeds
id user_id
100 1
101 NULL
102 2
users
id private
1 1
2 0
I have seen many related questions, and the answer is always to move the WHERE condition into ON instead. But if i do this:
SELECT `feeds`.`id`, `feeds`.`user_id` AS `feed_user_id`, `users`.`id` AS `user_id`, `users`.`private`
FROM `feeds`
LEFT JOIN `users` ON `feeds`.`user_id` = `users`.`id`
AND `users`.`private` = 0
This returns
id feed_user_id user_id private
100 1 NULL NULL
101 NULL NULL NULL
102 2 2 0
What I WANT it to do is exclude the first row id 100. (So I guess that is not really a LEFT JOIN -> I want it to LEFT JOIN if the condition is met, otherwise exclude) How can I do this?
You need two separate condition clauses, one on the JOIN and one on the SELECT, like so:
SELECT `feeds`.`id`, `feeds`.`user_id` AS `feed_user_id`, `users`.`id` AS `user_id`, `users`.`private`
FROM `feeds`
LEFT JOIN `users` ON `feeds`.`user_id` = `users`.`id`
WHERE `users`.`private` = 0 OR `feeds`.`user_id' IS NULL
The problem with your current approach is that the join is treating private users as if they don't exist, rather than saying "this person exists and I should exclude this row from the results".

sql multiple join and count

I have tables like this. but it seems MySQL doesn't count my second join currently. i want to know what i missed for process count of reports for my comment list.
and i want to have average of rates also count of reports
SELECT *, avg(rate.score), count(report.id) FROM `comment`
left join rate on (comment.id = rate.comment_id)
left join report on (comment.id = report.comment_id)
group by comment.id
id text id comment_id score id comment_id type avg(rate.score) count(report.comment_id)
1 good article 1 1 2 1 1 1 4.0000 20
2 bad article NULL NULL NULL NULL NULL NULL NULL 0
good article have 2 reports.
count(report.id) give me wrong value. what's my mistake?
SELECT
*,
avg(rate.score),
(SELECT
count(report.comment_id)
FROM
report
WHERE
comment.id = report.comment_id) AS num_reports
FROM
comment
left join
rate ON (comment.id = rate.comment_id)
group by comment.id
Here's the example:
http://sqlfiddle.com/#!2/cf313/15
You dont need *. Try this
SELECT comment.id, avg(rate.score), count(report.id) FROM `comment`
left join rate on (comment.id = rate.comment_id)
left join report on (comment.id = report.comment_id)
group by comment.id