SQL Outer Join to show rows with no data - sql

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

Related

SQL Left Join with verification on each table

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

Query returned with an extra column in sql -ms access

So I am wondering. I fell into an interesting suggestion from another developer. So i basically have two tables I join in a query and I want the resulting table from the query to have an extra column that comes from the table on from the joint.
Example:
#table A: contains rating of players, changes randomly at any date depending
#on drop of form from the players
PID| Rating | DateChange |
1 | 2 | 10-May-2014 |
1 | 4 | 20-May-2015 |
1 | 20 | 1-June-2015 |
2 | 4 | 1-April-2014|
3 | 4 | 5-April-2014|
2 | 3 | 3-May-2015 |
#Table B: contains match sheets. Every player has a different match sheet
#and plays different dates.
MsID | PID | MatchDate | Win |
1 | 2 | 10-May-2014 | No |
2 | 1 | 15-May-2015 | Yes |
3 | 3 | 10-Apr-2014 | No |
4 | 1 | 21-Apr-2015 | Yes |
5 | 1 | 3-June-2015 | Yes |
6 | 2 | 5-May-2015 | No |
#I am trying to achieve this by running the ms-access query: i want to get
#every players rating at the time the match was played not his current
#rating.
MsID | PID | MatchDate | Rating |
1 | 2 | 10-May-2014 | 4 |
2 | 1 | 15-May-2015 | 2 |
3 | 3 | 10-Apr-2014 | 4 |
4 | 1 | 21-Apr-2015 | 4 |
5 | 1 | 3-June-2015 | 20 |
6 | 2 | 5-May-2015 | 3 |
This is what I have tried below:
Select MsID, PID, MatchDate, A-table.rating as Rating from B-table
left Join A-table
on B-table.PID = A-table.PID
where B-table.MatchDate > A-table.Datechange;
any help is appreciated. The solution can be in Vba as long as it returns something like a view/table I can manipulate using other queries or report.
Think of this in terms of sets of data... you need a set that lists the MAX dateChange for each player's and match date.
Soo...
SELECT MAX(A.DateChange) MDC, A.PID, B.Matchdate
FROM B-table B
INNER Join A-table A
on B.PID = A.PID
and A.DateChange <= B.MatchDate
GROUP BY A.PID, B.Matchdate
Now we take this and join it back to what you've done to limit the results in table A and B to ONLY those with that date player and matchDate (my inline table C)
SELECT B.MsID, B.PID, B.MatchDate, A.rating as Rating
FROM [B-table] B
INNER JOIN [A-table] A
on B.PID = A.PID
INNER JOIN (
SELECT MAX(Y.DateChange) MDC, Y.PID, Z.Matchdate
FROM [B-table] Z
INNER Join [A-table] Y
on Z.PID = Y.PID
and Y.DateChange <= Z.MatchDate
GROUP BY Y.PID, Z.Matchdate) C
on C.mdc = A.DateChange
and A.PID = C.PId
and B.MatchDate = C.Matchdate
I didn't create a sample for this using your data so it's untested but I believe the logic is sound...
Now Tested! SQL Fiddle using SQL server though...
My results don't match yours exactly. I think you're expected results are wrong though for MSID 4 given rules defined.

Oracle ordering by several same meaning columns

I have to make sortable table like this:
Sortable table:
building_id | building_age | title |
-------------------------------------------------
1 | 100 | New york buil |
2 | 50 | House 1 |
3 | 50 | House 10 |
From these tables:
Building Table:
building_id | building_age | building_type_1_FK | building_type_2_FK
---------------------------------------------------------
1 | 100 | null | 1
2 | 50 | 1 | null
3 | 50 | 2 | null
building_type_1:
type_id | title | diff1 |
-------------------------------------------------
1 | New york buil| blablabla |
building_type_2:
building_id | title |
----------------------------
1 | House 1 |
2 | House 10 |
3 | House 500 |
While joining these tables I get several title columns where one of them is not null. Is there any way to sort by title and select top 10 results without fetching all the data and then sorting in the app?
p.s.. I know that in general this architecture is not good, but I can't change it.
Yes. You want to do a left outer join to the two tables, and then bring the results together:
select b.building_id, b.building_age, coalesce(bt1.title, bt2.title) as title
from building b left outer join
building_type_1 bt1
on b.building_type_1_FK = bt1.type_id left outer join
building_type_2 bt2
on b.building_type_2_FK = bt2.building_id;
To get the top 10 results in Oracle:
select *
from (select b.building_id, b.building_age, coalesce(bt1.title, bt2.title) as title
from building b left outer join
building_type_1 bt1
on b.building_type_1_FK = bt1.type_id left outer join
building_type_2 bt2
on b.building_type_2_FK = bt2.building_id
order by title
) b
where rownum <= 10;

Problems with an SQL Query

I need help with an sql table.
The table contains the following columns:
EventID, PersonID, EventType, EventTime(datetime), and a few other less significant columns.
Lets say that the two main event types are opening a door and closing a door.
I need the time duration(in seconds) between the same person opening a door and closing it or him opening a door and opening another one.(this of course allows sequences of simply opening doors)
Just to be clear if person 1 opened a door and person 2 closed a door no rows should be returned from the query.
I would like for it to be efficient but that isn't a must.
I'm using the 2008 SQL microsoft server(SQLEXPRESS)
Here is an example of a table:
EventID | PersonID | EventType | EventDate | PreviousDoor | CurrentDoor
1 | 1 | 1 | 12/10/2010 12:00:01.024 | 0 | 23
2 | 1 | 2 | 12/10/2010 12:05:40.758 | 23 | 0
3 | 2 | 1 | 12/10/2010 12:12:05.347 | 0 | 12
4 | 1 | 1 | 12/10/2010 12:50:12.142 | 0 | 23
5 | 2 | 2 | 12/10/2010 13:00:06.468 | 12 | 23
6 | 3 | 1 | 13/10/2010 13:00:06.468 | 0 | 23
EventType: 1(Opened door), 2(Closed door)
Result should be:
EventID | PersonID | EventType | EventDate | SecondsDifference
1 | 1 | 1 | 12/10/2010 12:00:01.024 | 339
3 | 2 | 1 | 12/10/2010 12:12:05.347 | 2881
I could really use your guys help.
Thanks in advance.
I think this should do it:
SELECT p1.EventID,
p1.PersonID,
p1.EventType,
p1.EventDate,
DATEDIFF(SECOND, p1.EventDate, p2.EventDate) AS SecondsDifference
FROM [Event] p1
LEFT JOIN [Event] p2 --Left join to self returning only closed door events
ON p2.PersonID = p1.PersonID
AND p2.EventType = 2 -- Closed Door
AND p1.EventID < p2.EventID --We don't want to bring back events that happened before the next event
WHERE p2.EventID IS NOT NULL --We don't want to show any people that have not closed a door
Try something like this:
select t1.EventID, t1.PersonID, t1.EventType, t1.EventDate, datediff(second, t1.EventDate, t2.EventDate) as 'SecondsDifference'
from [Event] t1
inner join [Event] t2 on t2.PersonID = t1.PersonID and t2.EventType = 2 and t2.PreviousDoor = t2.CurrentDoor and t2.EventID < t1.EventID
where t1.EventType = 1
Will using ROW_NUMBER and PARTITION help?
I'm not sure if the following is a legal SQL statement so please consider it as semi pseudo code.
SELECT *,
ROW_NUMBER() OVER(PARTITION BY PersonID ORDER BY EventTime) AS RowNumber,
datediff(seconds, t2.EventTime, t1.EventTime) AS SecondsDiff
FROM Events t1
INNER JOIN
SELECT *,
ROW_NUMBER() OVER(PARTITION BY PersonID ORDER BY EventTime) AS RowNumber
From Events t2
ON t1.RowNumber + 1 = t2.RowNumber
AND t1.PersonID = t2.PersonID AND t1.EventType = 1
AND (t2.EventType = 1 OR t2.EventType = 2)

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.