Check if ALL conditions specified are satisfied in SQL query - sql

Lets assume I have the following derived table:
Comment | Condition_Lower_Score | Condition_Higher_Score | Question_Score
=========================================================================
text1 | 1 | 3 | 2
text1 | 3 | 5 | 4
text2 | 5 | 6 | 1
text2 | 3 | 6 | 4
My table has a comment which is in a one to many relationship with conditions. Each condition can specify multiple questions(in this table the question score relates to different questions). I need to create a query that only selects the comment if all its conditions are satisfied.
The derived table is created from the following tables:
Comment:
Comment_ID | Comment_Text
===========================
1 | text1
2 | text2
Condition:
Condition_ID | Condition_Lower_Score | Condition_Higher_Score | Comment_ID | Question_ID
=========================================================================================
10 | 1 | 3 | 1 | 100
11 | 3 | 5 | 1 | 101
12 | 5 | 6 | 2 | 102
13 | 3 | 6 | 2 | 103
Question:
Question_ID | Question_Score
============================
100 | 2
101 | 4
102 | 1
103 | 4
So in this scenario, I would like only 'text1' to be selected from the derived table, and not 'text2' because all its conditions are not satisfied.
How do I create a query that only selects if all the conditions are met?

WITH TestsCTE AS
(
SELECT M.Comment_Text AS Comment,
C.Condition_Lower_Score,
C.Condition_Higher_Score,
Q.Question_Score,
CASE
WHEN Q.Question_Score BETWEEN C.Condition_Lower_Score AND C.Condition_Higher_Score
THEN 1
ELSE 0
END AS Pass
FROM [Condition] C
JOIN Comment M
ON C.Comment_ID = M.Comment_ID
JOIN Question Q
ON C.Question_ID = Q.Question_ID
)
SELECT COMMENT
FROM TestsCTE
GROUP BY COMMENT
HAVING MIN(Pass) = 1
SQL FIDDLE DEMO

select comment from (
<query for your derived table here>
) t1 group by comment
having count(
case
when question_score not between condition_lower_score and condition_higher_score
then 1 end
) = 0
or
select c.comment_text from comment c
join condition co on co.comment_id = c.comment_id
join question q on q.question_id = co.question_id
group by c.comment_text
having count(
case
when question_score not between condition_lower_score and condition_higher_score
then 1 end
) = 0

Related

Get the records from a table which id does not exist in another table for a specific User

I've Two tables:
table name column names
----------- -------------
question id | name | description
review_labels id | question_id | user_id | review_label
I have a user_id (example: 9101)
Now I want to extract the questions from question table of which the question_id doesn't exist in review_labels table for user 9101.
example:
Question table
id | name | description
1 | .... | ....
2 | .... | ....
3 | .... | ....
4 | .... | ....
5 | .... | ....
6 | .... | ....
table ReviewLabel
id | question_id | user_id | review_label
1 | 1 | 9432 | 1
2 | 3 | 9442 | 5
3 | 1 | 9101 | 4
4 | 4 | 9101 | 5
5 | 4 | 9432 | 4
6 | 6 | 9432 | 4
The result of the query should be
id | name | description
2 | .... | ....
3 | .... | ....
5 | .... | ....
6 | .... | ....
I tried this following query:
Question.left_outer_joins(:review_labels).where(review_labels: {user_id: 9101, question_id: nil})
It create the following sql:
SELECT `questions`.* FROM `questions` LEFT OUTER JOIN `review_labels` ON `review_labels`.`question_id` = `questions`.`id` WHERE `review_labels`.`user_id` = 9101 AND `review_labels`.`question_id` IS NULL
Unfortunately the result is an empty list.
I can't understand what should I do to solve this problem.
Don't know ruby-on-rails but in SQL NOT EXISTS suits your problem better:
SELECT `questions`.*
FROM `questions` Q
WHERE NOT EXISTS
(SELECT 1
FROM `review_labels` RL
WHERE RL.`question_id` = Q.`id`
AND RL.`user_id` = 9101
)
Can you try?
The query doesn't work since .where(review_labels: {user_id: 9101 ... creates WHERE review_labels.user_id = 9101 AND review_labels.question_id = nil. The rows would have to meet both of those condition - not just the joined rows. If you wanted to do it though a join you would have to add the conditions to the join clause:
SELECT *
FROM "questions"
LEFT OUTER JOINS "review_labels" ON
"review_labels"."user_id" = 9101
AND "review_labels"."question_id" = "questions"."id"
WHERE "review_labels"."id" IS NULL
AR doesn't have a good way to use binds in join clauses so there are other solutions to the problem that are preferable.
The most straight forward way is a WHERE id NOT IN (subquery):
Question.where.not(
id: ReviewLabel.select(:question_id).where(
user_id: 9101
)
)
Another way of doing this is a NOT EXIST subquery like in Tinamzu's answer:
Question.where(
ReviewLabel.where(
user_id: 9101
).where(
ReviewLabel.arel_table[:question_id].eq(Question.arel_table[:id])
).arel.exists.not
)

Postgres SQL query to get the first row of distinct id

channels table
id | name
------------
1 | ABC
2 | XYZ
3 | MNO
4 | ASD
user_channels table
user_id | channel_id
----------------------
555 | 1
666 | 1
777 | 1
555 | 2
888 | 2
999 | 3
555 | 3
user_chats table
id | created_at | channel_id | content
---------------------------------------
2 | time 1 | 1 | Hello
3 | time 2 | 1 | Hi
4 | time 3 | 2 | Good day
5 | time 4 | 2 | Morning
I have these 3 tables in postgres SQL,
I want to write a sql query to get user_channels by user_id and it's latest message only (time 1 is oldest message) from user_chats table. How can I do that?
For example, for user_id = 555, the query should return
channel_id | content | created_at
---------------------------------------
1 | Hi | time 2
2 | Morning | time 4
3 | Null | Null
Use distinct on:
select distinct on (a.channel_id) a.*
from user_chats a
inner join user_channels l on l.channel_id = a.channel_id
where l.user_id = 555
order by a.channel_id, a.createt_at desc
If you want this for all users at once:
select distinct on (l.user_id, a.channel_id) l.user_id, a.*
from user_chats a
inner join user_channels l on l.channel_id = a.channel_id
order by l.user_id, a.channel_id, a.createt_at desc
You can use distinct on:
select distinct on (c.channel_id) c.channel_id, uc.content, uc.created_at
from user_channels c left join
user_chats uc
on uc.channel_id = c.channel_id
where c.user_id = ?
order by c.idchannel_id, uc.created_at desc;

Check if data for update is same as before in SQL Server

I have a table Table1:
ID | RefID | Answer | Points |
----+-------+---------+--------+
1 | 1 | 1 | 5 |
2 | 1 | 2 | 0 |
3 | 1 | 3 | 3 |
4 | 2 | 1 | 4 |
I have a result set in temp table Temp1 with same structure and have update Table1 only if for refID answer and points have changed, otherwise there should be deletion for this record.
I tried:
update table1
set table1.answer = temp1.answer,
table1.points = temp1.points
from table1
join temp1 on table1.refid = temp1.refid
where table1.answer != temp1.answer or table1.points != temp1.points
Here is a fiddle http://sqlfiddle.com/#!18/60424/1/1
However this is not working and don't know how to add the delete condition.
Desired result should be if tables not the same ex. (second row answer 2 points3):
ID | RefID | Answer | Points |
----+-------+---------+--------+
1 | 1 | 1 | 5 |
2 | 1 | 2 | 3 |
3 | 1 | 3 | 3 |
4 | 2 | 1 | 4 |
if they are same all records with refID are deleted.
Explanation when temp1 has this data
ID | RefID | Answer | Points |
----+-------+---------+--------+
12 | 1 | 1 | 5 |
13 | 1 | 2 | 0 |
14 | 1 | 3 | 3 |
EDIT: adding another id column questionid solved the update by adding this also in join.
Table structure is now:
ID | RefID | Qid |Answer | Points |
----+-------+------+-------+--------+
1 | 1 | 10 | 1 | 5 |
2 | 1 | 11 | 2 | 0 |
3 | 1 | 12 | 3 | 3 |
4 | 2 | 11 | 1 | 4 |
SQL for update is: (fiddle http://sqlfiddle.com/#!18/00f87/1/1) :
update table1
set table1.answer = temp1.answer,
table1.points = temp1.points
from table1
join temp1 on table1.refid = temp1.refid and table1.qid = temp1.qid
where table1.answer != temp1.answer or table1.points != temp1.points;
SELECT ID, refid, answer, points
FROM table1
How can I make the deletion case, if data is same ?
You need to add one more condition in the join to exactly match the column.Try this one.
update table1
set table1.answer=temp1.answer,
table1.points=temp1.points
from
table1 join temp1 on table1.refid=temp1.refid and **table1.ID=temp1.ID**
where table1.answer!=temp1.answer or table1.points!=temp1.points
I would first do the delete, and only then the update.
The reason for this is that once you've deleted all the records where the three columns are the same, your update statement becomes simpler - you only need the join, and no where clause:
DELETE t1
FROM table1 AS t1
JOIN temp1 ON t1.refid = temp1.refid
AND t1.qid = temp1.qid
AND t1.answer=temp1.answer
AND t1.points=temp1.points
UPDATE t1
SET answer = temp1.answer,
points = temp1.points
FROM table1 AS t1
JOIN temp1 ON t1.refid=temp1.refid
AND t1.qid = temp1.qid
I think from what i understood that you need to use id instead of refid or both if id is unique

Microsoft Access query to duplicate ROW_NUMBER

Obviously there are a bunch of questions about ROW_NUMBER in MS Access and the usually response is that it does not exist but instead to use a COUNT(*) to create something similar. Unfortunately, doing so does not give me the results that I need.
My data looks like:
RID | QID
---------
1 | 1
1 | 2
1 | 3
1 | 3
2 | 1
2 | 2
2 | 2
What I am trying to get at is a unique count over RID and QID so that my query output looks like
RID | QID | SeqID
------------------
1 | 1 | 1
1 | 2 | 1
1 | 3 | 1
1 | 3 | 2
2 | 1 | 1
2 | 2 | 1
2 | 2 | 2
Using the COUNT(*) I get:
RID | QID | SeqID
------------------
1 | 1 | 1
1 | 2 | 2
1 | 3 | 3
1 | 3 | 3
2 | 1 | 1
2 | 2 | 2
2 | 2 | 2
My current query is:
SELECT
d.RID
,d.QID
,(SELECT
COUNT(*)
FROM
Data as d2
WHERE
d2.RID = d.RID
AND d2.QID < d.QID) + 1 AS SeqID
FROM
Data as d
ORDER BY
d.RID
,d.QID
Any help would be greatly appreciated.
As Matt's comment implied, the only way to make this work is if you have some column in your table that can uniquely identify each row.
Based on what you have posted, you don't seem to have that. If that's the case, consider adding a new auto increment numeric column that can serve that purpose. Let's pretend that you call that new column id.
With that in place, the following query will work:
select t.rid, t.qid,
(select count(*)
from data t2
where t2.rid = t.rid
and t2.qid = t.qid
and t2.id <= t.id) as SeqID
from data t
order by t.rid, t.qid
SQLFiddle Demo

Find matching set of records

I have the following tables on SQL Server 2008R2
MessageTable
ContrlNo| LineNo | Msg
1 | 1 | Tiger1 Text
1 | 2 | Tiger1 Text
1 | 3 | Tiger1 Text
1 | 4 | Tiger1 Text
2 | 1 | Tiger1 Text1
2 | 2 | Tiger1 Text2
2 | 3 | Tiger1 Text3
2 | 4 | Tiger1 Text4
3 | 1 | Horse 1
3 | 2 | Horse 2
3 | 3 | Horse 3
3 | 4 | Horse 4
RuleTable
RuleNo| MsgLineNo | RuleStartingPos | RuleMsg
1 | 1 | 1 | Tiger1 Text
2 | 1 | 1 | Tiger1 Text
2 | 3 | 1 | Tiger1 Text3
For each set of ControlNo records in the MESSAGETABLE I would like to apply the rule from the RULETABLE and list the RULENo if any mataches.
If you see the RuleTable, the Rule 2 overlaps rule 1. And the requirement is to get the most matched RuleNo for each Control number. the expected result is,
ContrlNo | RuleNo
1 | 1
2 | 2
3 | NULL
Thanks,
Jay
I believe the following will retrieve the listed results: it will show a control number with the associated rule that has the most matching lines (if, as in your first case, two rules have an equal number of matches, it will check the MatchPercent).
SELECT MT.ContrlNo, r.RuleNo, r.MatchPercent
FROM
MessageTable MT
LEFT JOIN
(
SELECT
ContrlNo,
RuleNo,
MatchedRules / AvailableRules AS MatchPercent,
ROW_NUMBER() OVER (PARTITION BY ContrlNo ORDER BY MatchedRules DESC, MatchedRules / AvailableRules DESC) AS rn
FROM
(
SELECT
ContrlNo,
R.RuleNo,
COUNT(*) as MatchedRules,
(SELECT COUNT(*) FROM RuleTable WHERE RuleTable.RuleNo = R.RuleNo) + 0.0 AS AvailableRules
FROM
MessageTable M
INNER JOIN
RuleTable R ON
M.[LineNo] = R.MsgLineNo AND
SUBSTRING(M.Msg,R.RuleStartingPos,LEN(R.RuleMsg)) LIKE '%' + R.RuleMsg + '%'
GROUP BY M.ContrlNo, R.RuleNo
) q
) r ON
MT.ContrlNo = r.ContrlNo AND
r.rn = 1
GROUP BY MT.ContrlNo, r.RuleNo, r.MatchPercent
SQL Fiddle
First subquery is getting total Matched rules and second sub query is getting total rules, when these two conditions are matched then we show the RuleNo other wise NULL as we are using LEFT JOIN
Select A.ContrlNo, ISNULL(T.RuleNo,0) as RuleNo FROM
( select ContrlNo, COUNT(R.RuleNo) as MatchedRules
FROM Messages M
LEFT JOIN Rules R
on M.[LineNo] = R.MsgLineNo
and SUBSTRING(M.Msg,R.RuleStartingPos,LEN(R.RuleMsg)) = R.RuleMsg
AND M.ContrlNo = R.RuleNo
GROUP BY M.ContrlNo) A
LEFT JOIN (
select COUNT(MsgLineNo) as TotalRules, RuleNo
from Rules R1
group by RuleNo) T
ON A.MatchedRules = T.TotalRules