SQL Left Join with verification on each table - sql

First, my SQL skils are really bad.
Here is my tables :
Message :
id | title | arrayusers
1 |Title1 | a:2:{i:1;i:1;i:5;i:5;}
2 |Title2 | a:2:{i:1;i:1;i:5;i:5;}
3 |Title3 | a:2:{i:1;i:1;i:5;i:5;}
4 |Title4 | a:2:{i:1;i:1;i:5;i:5;}
5 |Title5 | a:2:{i:1;i:1;i:5;i:5;}
6 |Title6 | a:2:{i:1;i:1;i:5;i:5;}
Read :
id | status | userid | message_id
1 | 0 | 5 | 1
2 | 0 | 1 | 2
3 | 0 | 5 | 2
4 | 0 | 1 | 3
5 | 0 | 5 | 4
6 | 1 | 1 | 5
7 | 1 | 5 | 5
7 | 1 | 5 | 6
I use the userid 1 for my test :
My goal, is to get all lines from Message, where my user 1 is in arrayusers with a LIKE(i:1;i:1;)
And user 1 not in table Read (userid), with the status equal to 0. If another user (5) but not me (user 1) is in Read table, I want to see my Message line
With the data above, I want to return the id 1 / 4 from Message table
I started a request with Left join, but my request hide line id 1 and 4 because there is another user in this table with this id.
SELECT * FROM message as m
LEFT JOIN read as r ON m.id = r.message_id
WHERE m.arrayusers LIKE '%i:1;i:1;%'
AND r.id IS NULL
Hope you understand my request.
Someone can help me to debug my request ?
Thanks

I'd use NOT EXISTSor NOT IN to look up the status in the read table:
select *
from message
where arrayusers like '%i:1;i:1;%'
and id not in
(
select message_id
from read
where userid = 1
and status = 0
);
As to your own query: You are merely missing the criteria on the read table:
SELECT m.*
FROM message as m
LEFT JOIN read as r ON m.id = r.message_id and r.userid = 1 and r.status = 0
WHERE m.arrayusers LIKE '%i:1;i:1;%'
AND r.id IS NULL;

What is the use of AND r.id IS NULL? Please remove this condition and check:
SELECT * FROM message as m
LEFT JOIN read as r ON m.id = r.message_id
WHERE m.arrayusers LIKE '%i:1;i:1;%'
AND r.id IS NULL

Related

MS SQL Query to get all entries comming multiple times in table where some column value doesnt have entries

I just tried to formulate title as best as possible. So my case is as follow.
i have a table
venue_id | style_id | is_main
1 | 1 | 1
1 | 2 | 0
1 | 3 | 0
2 | 5 | 0
2 | 8 | 0
2 | 9 | 0
3 | 3 | 1
4 | 4 | 1
4 | 6 | 0
5 | 7 | 0
5 | 8 | 0
5 | 9 | 0
So i need to get only those venue ID, witch coming more then once and where is no is_main true entry.
So result should be contain venue_id's: 2 and 5
I would grateful for any suggestion how such query may looks like.
Thanks in Advance.
UPD: in my case with is_amin BIT value answer would be:
select venue_id
from table
group by venue_id
having cast(max(cast(is_main as INT)) AS BIT) = 0 and
count(*) >= 2;
You seem to want:
select venue_id
from t
group by venue_id
having max(is_main) = 0 and
count(*) >= 2;
You can use this one:
SELECT DISTINCT v.venue_id FROM venue v
LEFT OUTER JOIN (SELECT DISTINCT venue_id FROM venue WHERE is_main=1) m
ON v.venue_id = m.venue_id
WHERE m.venue_id IS NULL
If you have many thousands of rows, it would be better to create a secondary table or a materialized view to be used in place of the nested SELECT.

Get right table data on LEFT JOIN

I have a problem with my sql join request.
I need to get lines of left table who are not referenced in right table for ME (User 1) or referenced in right table with status equal to 0 and user equal to 1.
I also need the field status of right table.
Here is my two tables :
Table left
ID | title
1 | Title 1
2 | Title 2
3 | Title 3
4 | Title 4
5 | Title 5
6 | Title 6
Table right
ID | status | user | left_id
1 | 0 | 1 | 1
2 | 0 | 50 | 1
3 | 1 | 1 | 2
4 | 0 | 50 | 2
5 | 0 | 1 | 3
6 | 1 | 50 | 3
7 | 0 | 50 | 4
8 | 1 | 50 | 5
My goal is to get this result :
left.ID | left.title | right.status | right.user
1 | Title 1 | 0 | 1
3 | Title 3 | 0 | 1
4 | Title 4 | NULL | NULL
5 | Title 5 | NULL | NULL
6 | Title 6 | NULL | NULL
Here is my request for the moment :
SELECT l.id, l.title, r.user, r.status
FROM left as l
LEFT JOIN right as r ON l.id = r.left_id
WHERE r.left_id IS NULL or (r.user = 1 AND r.status = 0)
With this request I get lines ID (left table) 1 / 3 / 6. But I also need the ID 4 / 5.
Those lines isn't displayed because another user (50) as a reference, but it's not me (1).
If someone can help me to add line 4 / 5 to my result I would be happy.
Thanks
Small improvement of the query should be sufficient:
SELECT l.id, l.title, r.user, r.status
FROM left as l
LEFT JOIN right as r ON l.id = r.left_id and r.user = 1
WHERE r.left_id IS NULL or r.status = 0
(Select t1.id, t1.title,t2.status,t2.user
from tableLeft t1
right outer join tableRight t2 on t2.left_id=t1.id
where t1.id not in
(select tt2.left_id from tableRight tt2)
)
union
(select t1.id,t1.title,t2.status,t2.user
from tableLeft t1
left join tableRight t2 on t1.id=t2.left_id
where t2.status=0 and t2.user=1
)

SQL Outer Join to show rows with no data

Need some help to get out some data and im stuck in the join swamp. I run on a MS SQL Server.
I have a list of clients in one table (clientgroup)
clientgroup
Groupid | clientid
1 | 11
1 | 12
1 | 13
1 | 14
1 | 15
And I have another table where I have if the clientid has some clientactivity
clientcontactlog
Logid | clientid
1 | 11
2 | 14
3 | 15
4 | 11
5 | 11
6 | 11
Then I have another table with info about the clientactivity
contactlog
Logid | logtype | logdate | logtext
1 | 1 | ’2016-05-16’ | ’Toys’
2 | 1 | ’2016-05-16’ | ’Toys’
3 | 1 | ’2016-05-16’ | ’Toys’
4 | 2 | ’2016-05-17’ | ’Lunch’
5 | 2 | ’2016-05-18’ | ’Dinner on Mars’
6 | 1 ! ’2016-05-19’ | ’Dinner on Mars’
I now want to make a mothly statistic (sum on logtypes in contactlog) about this and include all clients I have in my client list with id 1. So the output also shows the clients that have no record clientcontactlog. This is what I need help with.. I get all the data where we have input but I also need to show 0 on the clients that has no record.
Output should be
Clientname | sum(logtype1)
Client11 | 2
Client12 | 0
Client13 | 0
Client14 | 1
Client15 | 1
Thanks for input and help
Pretty sure that you mean COUNT(LogType), not SUM(LogType), since Summing a date makes no sense whatsoever. In any case, this can do what you are asking for.
SELECT ClientName, IsNull(ct, 0) as LogCount
FROM clientgroup cg
INNER JOIN Clients c
ON cg.clientid = c.clientid
LEFT JOIN (
SELECT LogID,
Count(*) as ct
FROM ClientContactLog
GROUP BY ClientID
) as cl
Notes:
I assumed a Clients table containing the client name.
I am not looking at the contactlog table because it is not needed to provide what you are requesting.
Assuming there is a client table where the clientname column is, you can do that this way:
select t2.clientname, coalesce(count(distinct t4.logtype), 0)
from clientgroup t1
join client t2
on t1.clientid = t2.id
left join
clientcontactlog t3
on t1.clientid = t3.clientid
left join
contactlog t4
on t3.Logid = t4.Logid
where t1.Groupid = 1

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

Left Join on Associative Table

I have three tables
Prospect -- holds prospect information
id
name
projectID
Sample data for Prospect
id | name | projectID
1 | p1 | 1
2 | p2 | 1
3 | p3 | 1
4 | p4 | 2
5 | p5 | 2
6 | p6 | 2
Conjoint -- holds conjoint information
id
title
projectID
Sample data
id | title | projectID
1 | color | 1
2 | size | 1
3 | qual | 1
4 | color | 2
5 | price | 2
6 | weight | 2
There is an associative table that holds the conjoint values for the prospects:
ConjointProspect
id
prospectID
conjointID
value
Sample Data
id | prospectID | conjointID | value
1 | 1 | 1 | 20
2 | 1 | 2 | 30
3 | 1 | 3 | 50
4 | 2 | 1 | 10
5 | 2 | 3 | 40
There are one or more prospects and one or more conjoints in their respective tables. A prospect may or may not have a value for each conjoint.
I'd like to have an SQL statement that will extract all conjoint values for each prospect of a given project, displaying NULL where there is no value for a value that is not present in the ConjointProspect table for a given conjoint and prospect.
Something along the lines of this for projectID = 1
prospectID | conjoint ID | value
1 | 1 | 20
1 | 2 | 30
1 | 3 | 50
2 | 1 | 10
2 | 2 | NULL
2 | 3 | 40
3 | 1 | NULL
3 | 2 | NULL
3 | 3 | NULL
I've tried using an inner join on the prospect and conjoint tables and then a left join on the ConjointProspect, but somewhere I'm getting a cartesian products for prospect/conjoint pairs that don't make any sense (to me)
SELECT p.id, p.name, c.id, c.title, cp.value
FROM prospect p
INNER JOIN conjoint c ON p.projectID = c.projectid
LEFT JOIN conjointProspect cp ON cp.prospectID = p.id
WHERE p.projectID = 2
ORDER BY p.id, c.id
prospectID | conjoint ID | value
1 | 1 | 20
1 | 2 | 30
1 | 3 | 50
1 | 1 | 20
1 | 2 | 30
1 | 3 | 50
1 | 1 | 20
1 | 2 | 30
1 | 3 | 50
2 | 1 | 10
2 | 2 | 40
2 | 1 | 10
2 | 2 | 40
2 | 1 | 10
2 | 2 | 40
3 | 1 | NULL
3 | 2 | NULL
3 | 3 | NULL
Guidance is very much appreciated!!
Then this will work for you... Prejoin a Cartesian against all prospects and elements within that project via a select as your first FROM table. Then, left join to the conjoinprospect. You can obviously change / eliminate certain columns from result, but at least all is there, in the join you want with exact results you are expecting...
SELECT
PJ.*,
CJP.Value
FROM
( SELECT
P.ID ProspectID,
P.Name,
P.ProjectID,
CJ.Title,
CJ.ID ConJointID
FROM
Prospect P,
ConJoint CJ
where
P.ProjectID = 1
AND P.ProjectID = CJ.ProjectID
ORDER BY
1, 4
) PJ
LEFT JOIN conjointProspect cjp
ON PJ.ProspectID = cjp.prospectID
AND PJ.ConjointID = cjp.conjointid
ORDER BY
PJ.ProspectID,
PJ.ConJointID
Your cartesian product is a result of joining by project Id - in your sample data there are 3 prospects with a project id of 1 and 3 conjoints with a project id of 1. Joining based on project id should then result in 9 rows of data, which is what you're getting. It looks like you really need to join via the conjointprospects table as that it what holds the mapping between prospects and conjoint.
What if you try something like:
SELECT p.id, p.name, c.id, c.title, cp.value
FROM prospect p
LEFT JOIN conjointProspect cp ON cp.prospectID = p.id
RIGHT JOIN conjoint c ON cp.conjointID = c.id
WHERE p.projectID = 2
ORDER BY p.id, c.id
Not sure if that will work, but it seems like conjointprospects needs to be at the center of your join in order to correctly map prospects to conjoints.