Oracle SQL How to find missing value for different users - sql

I have 2 tables
One table with questions
ID Description
== ===========
1 Some Question
2 Some Question
3 Some Question
4 Some Question
And an other one with the awsers to each question of every users
ID_USER ID_QUESTION ANSWER
======= =========== =========
1 2 a
1 1 b
1 3 d
2 1 e
2 4 a
3 4 c
3 2 a
As you can see it is possible that a user does not answer a question and this is my problem
I am currently trying to find wich answer a user did not answer to.
I'd like to have something like this
ID_USER ID_MISSING_QUESTION
======= ===================
1 4
2 3
2 2
3 1
3 3
I can easly find the missing questions for a single user but i can't do that for every user since they are quite numerous.
Thanks Ayoye

Quick and Dirty:
SELECT TB_USER.ID, TB_QUESTION.ID AS "Q_ID" FROM TB_USER, TB_QUESTION
minus
SELECT ID_USER, ID_QUESTION FROM tb_answer
Sql Fiddle Demo here.

I think You are looking for something like this:
SELECT
u.id_user,
q.id_question
FROM
questions q
CROSS JOIN users u
LEFT JOIN answers a ON (a.id_question = q.id_question and a.id_user = u.id_user)
WHERE
a.answer IS NULL
First You shoyul create set of every question for every user, and then try to join in with Your answers. And then filer out all results that have found answers. :)

You should post the SQL statement(s) you tried, before expecting a full answer, else someone might think you want let others write all the code for you...
Nevertheless, instead of plain JOIN, use a FULL OUTER JOIN and a LEFT OUTER JOIN resp. RIGHT OUTER JOIN, depending on table ordering in your SQL statement (which you did not post yet), and filter with IS NULL.

Related

SQL subquery to join the most recent plan for a client

In MS Access 365, I have two tables (TClients & TPlans) that I am trying to combine into a single Query (QClientsExtended). Each client could have many or no associated entries on the TPlan list. In my final query, I would like it to list every client regardless of wether or not they have a plan, and give me the date and the details of the most recent plan, if there is one.
I've read all the relevant W3C reference pages, and looked at so many possible solutions, and i've struggled to turn them into something that works for this. It feels like it should be SO simple, I appreciate I'm probably missing a fundamental aspect of SQL coding.
TClients
ClientID ClientFullName ExternalAppts
1 Testy McTestFace 1
2 Clemence Closed 0
3 Nancy New Ref 3
4 Juan One Appt 0
TPlans
PlanID ClientID PlanDetails PlanDate
1 3 Plan 1 05-Dec-22
2 3 Plan 2 10-Dec-22
3 1 plan 10-Dec-22
4 4 nil 05-Dec-22
Qclients Extended
ClientID ClientFullName PlanDetails PlanDate ExternalAppts
1 Testy McTestFace Plan 2 10-Dec-22 1
2 Clemence Closed 0
3 Nancy New Ref plan 10-Dec-22 3
4 Juan One Appt nil 05-Dec-22 0
I've found a solution, but it feels incredibly clunky. I have made two Queries, one to find the most recent date for each client, and the second to Left Join this with the Client Table. I'm sure this should be doable in a single query, but maybe it can't be done in Access. Here are my two Queries:
QlastPlan
SELECT t1.*
FROM TPlans AS t1
INNER JOIN (
SELECT [ClientID],
MAX(PlanDate) AS LastPlan
FROM TPlans
GROUP BY [ClientID])
AS t2 ON (t1.[PlanDate] = t2.LastPlan) AND (t1.[ClientID] = t2.[ClientID]);
QClients
SELECT
TC.*,
QLP.PlanDetails,
QLP.PlanDate,
FROM TClients TC
LEFT JOIN QlastPlan QLP on TC.ClientID = QLP.ClientID;
So perhaps there's another option someone can suggest, but this is what I will run with for now.
Try with a Left Join:
SELECT
TClients.ClientID,
TClients.ClientFullName,
TPlans.PlanDetails,
TPlans.PlanDate,
TClients.ExternalAppts
FROM
TClients
LEFT JOIN
TPlans ON TClients.ClientID = TPlans.ClientID
GROUP BY
TClients.ClientID,
TClients.ClientFullName,
TPlans.PlanDetails,
TPlans.PlanDate,
TClients.ExternalAppts
HAVING
TPlans.PlanDate=
(Select Max(PlanDate) From [TPlans] As T Where T.ClientID = TClients.[ClientID])
OR
TPlans.PlanDate Is Null

Writing a SQL conditional statement across columns for survey data

I am working with survey data that is feeding directly into a Snowflake DB.
In the survey tool, the primary question is formatted with a scaled numeric 0 through 10 satisfaction question, and then a subsequent question asks for a written explanation/exposition, but that subquestion can be skipped/left unanswered.
Also, based on how the respondent responds and rates to the scaled question, the survey tool will change the subquestion ie What do you like most about our Waffles? or What could be better?
Each of the three unique questions -the primary scaled question and the two different subquestions- have their own unique QUESTIONID and survey participants have a unique ID as RESPONSEID.
I am attempting to collect survey responses from only participants that have answered both the primary question and their resulting subquestion regardless of which subquestion they were prompted, and ignoring participants that did not write/respond to their subquestions.
The current query and output looks like this:
SELECT DISTINCT
r.RESPONSEID,
r.QUESTIONID,
CASE
WHEN r.SURVEY = q.SURVEY AND r.questionid = q.QUESTIONID THEN q.QUESTIONTEXT
ELSE NULL
END QTEXT,
r.RESPONSE,
FROM RESPONSES r
JOIN QUESTIONS q ON q.questionid = r.questionid
JOIN QUESTION_RESPONSE s ON s.response_id = r.responseid
WHERE r.SURVEY IN ('WaffleSurvey3000')
AND (q.QUESTIONID = '1' OR q.QUESTIONID = '1A'
OR q.QUESTIONID = '1B')
AND QTEXT IS NOT NULL
ORDER BY RESPONSEID;
And output:
RESPONSEID QUESTIONID QTEXT RESPONSE
A 1 Between 0 and 10... 7
B 1 Between 0 and 10... 9
B 1A What did you like... Best Waffles EVER!
C 1 Between 0 and 10... 5
D 1 Between 0 and 10... 6
E 1 Between 0 and 10... 2
E 1B What could be better... Awful Waffles! Do better! SHAME
But would like an output that ignores participants that did not answer their prompted subquestion asking for text which would look like:
RESPONSEID QUESTIONID QTEXT RESPONSE
B 1 Between 0 and 10... 9
B 1A What did you like... Best Waffles EVER!
E 1 Between 0 and 10... 2
E 1B What could be better... Awful Waffles! Do better! SHAME
I was hoping I could pull this off with another WHERE clause at the end, but since this seems to be a conditional statement, I'm assuming it'll involve another CASE statement, but not sure.
Any thoughts?
Perhaps you can add to the where clause of your query another filter:
AND r.RESPONSEID = (
select p.RESPONSEID from RESPONSES p
group by p.RESPONSEID having COUNT(1) > 1
)
Another version:
AND r.RESPONSEID||SUBSTR(r.QUESTIONID,1,1) = (
select p.RESPONSEID||SUBSTR(p.QUESTIONID,1,1) from RESPONSES p
group by p.RESPONSEID||SUBSTR(p.QUESTIONID,1,1) having COUNT(1) > 1
)

Nested SELECT vs JOIN performance [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 months ago.
Improve this question
I have the following two tables in PostgreSQL database (simplified for the sake of example):
article
id
summary
1
Article 1
2
Article 2
3
Article 3
...
...
event
id
article_id
eventtype_id
comment
108
1
4
Comment 1
109
2
8
Comment 2
110
3
4
Comment 3
...
...
I would like to select only 1 event with eventtype_id=4 for each article. The result should look like this:
article_id
article_summary
event_comment
1
Article 1
Comment 1
2
Article 2
3
Article 3
Comment 3
...
Which of these 2 queries (Query 1 or Query 2) runs faster? Do they return the same result?
Query1:
SELECT
a.id AS article_id,
a.summary AS article_summary,
evnt.comment AS event_comment
FROM
article a
LEFT JOIN
event evnt ON evnt.article_id = a.id AND evnt.eventtype_id = 4;
Query2:
SELECT
a.id AS article_id,
a.summary AS article_summary,
(
SELECT
evnt.comment
FROM
event evnt
WHERE
evnt.article_id = a.id AND
evnt.eventtype_id = 4
LIMIT 1
) AS event_comment
FROM
article a;
Apart from the fact that queries are not the same, as you said in the comments, generally speaking, correlated queries are not considered suitable from a performance point of view. That's because these queries are applied row by row. They are usually helpful in some particular situations: read this. However, even in those situations, it is a good practice to use them in an exists clause if possible, So that whenever it finds a row for the query it returns true.

PostgreSQL check parent of parent in query

Okay, I'll try to be as precise as possible. I have a database with comments who have a parent_comment_id. I want to select a number of comments from this table and a matching number of answers to these comments. Here comes my problem: If I arbitrarily select comments, I might grab some who don't have any answers. However, if I select comment answers first, I might grab answers who are an answer to another comment already. So, what I want to do is grab answers who are immediate answers to a comment (parent_comment_id = NULL) and then get those comments base on the parent_comment_id. How would I go about this in a query?
Assume this layout of a database
comment_id parent_comment_id
1 NULL
2 NULL
3 1
4 3
5 1
6 3
7 1
8 4
9 NULL
10 NULL
...
Now, I would select, let's say 2 answers and the corresponding comments. If I just select the first 2 answers, I would get comment 3 and 4, but if I backtrace those, I would only get comment 1, because comment 4 is an answer to an answer. Instead, I would want to find only comments whose parent comment has no parent comment, which would be comment 3 and 5 in this example.
I haven't really tried anything beyound realizing it doesn't work without somehow recursively getting parents which I have no clue how to do in SQL queries.
I solved it with a pretty easy solution. Might be horrible practice, but works for my use-case ^^
For the given example it would be something like this:
select distinct on (parent_comment_id) c.id, c.parent_comment_id, c.text
from comments c
where parent_comment_id is not null
and (select parent_comment_id from comments p where id = c.parent_comment_id) is null
I would want to find only comments whose parent comment has no parent comment.
I think that you just want a self-join and some filtering:
select c.*
from comments c
inner join comments pc on pc.comment_id = c.parent_comment_id
where pc.parent_comment_id is null
You could also use exists:
select c.*
from comments c
where exists (
select 1
from comments pc
where pc.comment_id = c.parent_comment_id and pc.parent_comment_id is null
)

Access SQL Syntax for Cross-Table Count

I have what may be a simple SQL syntax question but I've been scratching my head over it for weeks now and it's abstract enough that I'm having trouble hunting down the correct search in Google.
Let us assume that I have three tables: Users and Bookings:
Users:
UserID Name
1 Adam
2 Bob
3 Charlie
Bookings:
BookingID UserID MeetingID
1 1 1
2 2 1
3 2 2
4 3 2
The query that I'm looking to create will tell me how many other users each user has shared a meeting ID with. In my example:
Output:
UserID Co-Meeters
1 1
2 2
3 1
As in the example, users 1 and 3 only had one co-meeter (in this case "2") and user 2 had two co-meeters (1 and 3).
I'm sorry if my description isn't terribly clear, but I'm trying to strip out the needless complication from other intersecting tables and fields that don't really affect the syntax I'm looking for. My initial thought was to use "Group By", but since Bookings is a many-to-many (in terms of the fields I'm looking at) I can't make the logic work.
Any and all help is appreciated.
I think this should do the trick:
select u.userid, iif(isnull(x.others), 0, x.others) as [Co-Meeters]
from users u
left join bookings b on u.userid = b.userid
left join (
select meetingid, count(*) as others from bookings where userid <> u.userid
) x on b.meetingid = x.meetingid
order by 1