Tables are not joining properly in sql server 2008 - sql

I have to show result from the dbo.mail_Messages table, but the problem is that Messages should be shown which Ids are not in dbo.mail_Reply. For that I am using the query below but it is showing nothing.
I have also attached a screen shot:
How can I fix this?
My code:
SELECT
dbo.mail_Messages.MessageID,
dbo.mail_Users_Messages_Mapped.PlaceHolderID,
IsRead,
SenderId,
dbo.mail_Messages.Subject,
dbo.mail_Messages.Body,
dbo.mail_Messages.Date,UserEmail
FROM
dbo.mail_Users_Messages_Mapped
JOIN
dbo.mail_Messages ON dbo.mail_Messages.MessageID = dbo.mail_Users_Messages_Mapped.MessageID
JOIN
dbo.mail_Users ON dbo.mail_Users.UserID = dbo.mail_Users_Messages_Mapped.UserId
JOIN
dbo.mail_Reply ON dbo.mail_Reply.MessageID = dbo.mail_Messages.MessageID
WHERE
UserEmail = 'user1'
AND dbo.mail_Users_Messages_Mapped.PlaceHolderId = 1
AND dbo.mail_Messages.MessageID != dbo.mail_Reply.MessageID

Your query will produce an empty result as it has two contradicting conditions. In the join clause you demand that:
dbo.mail_Reply.MessageID = dbo.mail_Messages.MessageID
And in the where clause you demand that
dbo.mail_Messages.MessageID != dbo.mail_Reply.MessageID
Since a value cannot be both equal and not equal to another value, the combination of these two conditions is an empty result.
One way to solve this, if I understand the requirements correctly, is to stop joining mail_Reply, and use the in operator instead:
SELECT
dbo.mail_Messages.MessageID,
dbo.mail_Users_Messages_Mapped.PlaceHolderID,
IsRead,
SenderId,
dbo.mail_Messages.Subject,
dbo.mail_Messages.Body,
dbo.mail_Messages.Date,UserEmail
FROM
dbo.mail_Users_Messages_Mapped
join
dbo.mail_Messages on dbo.mail_Messages.MessageID = dbo.mail_Users_Messages_Mapped.MessageID
join
dbo.mail_Users on dbo.mail_Users.UserID = dbo.mail_Users_Messages_Mapped.UserId
where
UserEmail = 'user1'
and
dbo.mail_Users_Messages_Mapped.PlaceHolderId = 1
and
dbo.mail_Messages.MessageID NOT IN (SELECT MessageID FROM dbo.mail_Reply)

Related

Check in T-SQL whether certain value sets exist in a related table

I have following tables:
User - userId, userName, ...
Settings - settingId, userId, settingKey, settingValue
for example for userId = 123, I might have:
settingId: 1
userId: 123
settingKey: "allowClient"
settingValue: "0"
settingId: 2
userId: 123
settingKey: "allowAccess"
settingValue: "1"
Then for example how can I query for all users that have settingValue of "0" corresponding to settingKey of "allowClient" and settingValue of "1" corresponding to settingKey of "allowAccess"? Sometimes the settingKey and settingValue that I'm looking for might not even be there for a particular user, in which case, I would just want to ignore those users.
My "attempt":
select * from User u inner join Settings s on u.userid = s.userid
where s.settingKey = 'allowClient and s.settingValue = '0'
and s.settingKey = 'allowAccess' and s.settingValue = '1'
this doesn't work for obvious reason because it's putting AND on all the conditions. I'm not aware of any sql construct that can get around this and allow me to just say what I actually want.
Your first attempt doesn't work because the WHERE clause check each row one at a time. And no single row fulfils all of those conditions at once.
So, you could use an EXISTS() check on each of the two keys, for a very literal expression of your problem...
SELECT
user.*
FROM
user
WHERE
EXISTS (
SELECT *
FROM settings
WHERE userId = user.userId
AND settingKey = 'allowClient'
AND settingValue = '0'
)
AND
EXISTS (
SELECT *
FROM settings
WHERE userId = user.userId
AND settingKey = 'allowAccess'
AND settingValue = '1'
)
Depending on data characteristics, you may benefit from a single sub-query instead of two EXISTS() checks.
This is closer to what you were trying to do.
Filter to get two rows per user (using OR instead of AND)
Aggregate back down to a single row and check if both conditions were met
(But I'd go with two EXISTS() first, and let the optimiser do its work.)
WITH
matching_user
(
SELECT
userId
FROM
settings
WHERE
(settingKey = 'allowClient' AND settingValue = '0')
OR
(settingKey = 'allowAccess' AND settingValue = '1')
GROUP BY
userId
HAVING
COUNT(DISTINCT settingKey) = 2 -- DISTINCT only needed if one user has the same key set twice
)
SELECT
user.*
FROM
user
INNER JOIN
matching_user
ON user.userId = matching_user.userId
Finally, you could just join twice, which is functionally similar to the double-exists check, but shorter code, though not always as performant.
SELECT
user.*
FROM
user
INNER JOIN
settings AS s0
ON s0.userId = user.userId
AND s0.settingKey = 'allowClient'
AND s0.settingValue = '0'
INNER JOIN
settings AS s1
ON s1.userId = user.userId
AND s1.settingKey = 'allowAccess'
AND s1.settingValue = '1'
Using the two different aliases prevents ambiguity (which would cause an error).
It does assume that the joins will only ever find 0 or 1 rows, if they can find many, you get duplication. EXISTS() doesn't have that problem.

Issue with my Left outer join query

Here are my tables,
I'm trying to fetch the list of free bets with the details if the user has placed his bet or not. This is the query i have written to fetch the details for the same,
select DISTINCT tbl_CreateFreeBet.FreeBetID,
tbl_CreateFreeBet.FreeBetDescription,
tbl_CreateFreeBet.FreeBetAmount,
tbl_CreateFreeBet.TournamentID,
tbl_CreateFreeBet.MatchID,
tbl_UserFreeBets.UserForYes,
tbl_UserFreeBets.UserForNo,
tbl_UserFreeBets.UserForNoBets
from tbl_CreateFreeBet left outer join tbl_UserFreeBets
on tbl_CreateFreeBet.MatchID = 1 and
tbl_CreateFreeBet.MatchID = tbl_UserFreeBets.MatchID and
tbl_CreateFreeBet.FreeBetID = tbl_UserFreeBets.FreeBetID and
tbl_CreateFreeBet.TournamentID = tbl_UserFreeBets.TournamentID and
(tbl_UserFreeBets.UserForYes = 'User2' or tbl_UserFreeBets.UserForNo =
'User2')
This is working fine, when there is a data in tbl_CreateFreeBet table for the MatchID. But if there is no data, then this query is not returning the expected result.
For example: With tbl_CreateFreeBet.MatchID = 1, I need to get all the free bets of matchID = 1, with the details of the passed in user, if has bet on 'yes' or 'no'. This comes up fine, as there is data for MatchId = 1 in tbl_CreateFreeBet.
But, it fails, when the tbl_CreateFreeBet.MatchID = 2 input is passed. Here there is no free bet created for the MatchID = 2. But still it returns me the result for MatchID=1.
Please share the query if one is aware of what changes need to be done for my query. Thank you.
Conditions on the first table in a LEFT JOIN should be in the WHERE clause. I think you intend this logic:
select cfb.FreeBetID, cfb.FreeBetDescription, cfb.FreeBetAmount,
cfb.TournamentID, cfb.MatchID,
ufb.UserForYes, ufb.UserForNo, ufb.UserForNoBets
from tbl_CreateFreeBet cfb left outer join
tbl_UserFreeBets ufb
on cfb.MatchID = ufb.MatchID and
cfb.FreeBetID = ufb.FreeBetID and
cfb.TournamentID = ufb.TournamentID and
(ufb.UserForYes = 'User2' or ufb.UserForNo = 'User2')
where cfb.MatchId = 1

The "where" condition worked not as expected ("or" issue)

I have a problem to join thoses 4 tables
Model of my database
I want to count the number of reservations with different sorts (user [mrbs_users.id], room [mrbs_room.room_id], area [mrbs_area.area_id]).
Howewer when I execute this query (for the user (id=1) )
SELECT count(*)
FROM mrbs_users JOIN mrbs_entry ON mrbs_users.name=mrbs_entry.create_by
JOIN mrbs_room ON mrbs_entry.room_id = mrbs_room.id
JOIN mrbs_area ON mrbs_room.area_id = mrbs_area.id
WHERE mrbs_entry.start_time BETWEEN "145811700" and "1463985000"
or
mrbs_entry.end_time BETWEEN "1458120600" and "1463992200" and mrbs_users.id = 1
The result is the total number of reservations of every user, not just the user who has the id = 1.
So if anyone could help me.. Thanks in advance.
Use parentheses in the where clause whenever you have more than one condition. Your where is parsed as:
WHERE (mrbs_entry.start_time BETWEEN "145811700" and "1463985000" ) or
(mrbs_entry.end_time BETWEEN "1458120600" and "1463992200" and
mrbs_users.id = 1
)
Presumably, you intend:
WHERE (mrbs_entry.start_time BETWEEN 145811700 and 1463985000 or
mrbs_entry.end_time BETWEEN 1458120600 and 1463992200
) and
mrbs_users.id = 1
Also, I removed the quotes around the string constants. It is bad practice to mix data types, and in some databases, the conversion between types can make the query less efficient.
The problem you've faced caused by the incorrect condition WHERE.
So, should be:
WHERE (mrbs_entry.start_time BETWEEN 145811700 AND 1463985000 )
OR
(mrbs_entry.end_time BETWEEN 1458120600 AND 1463992200 AND mrbs_users.id = 1)
Moreover, when you use only INNER JOIN (JOIN) then it be better to avoid WHERE clause, because the ON clause is executed before the WHERE clause, so criteria there would perform faster.
Your query in this case should be like this:
SELECT COUNT(*)
FROM mrbs_users
JOIN mrbs_entry ON mrbs_users.name=mrbs_entry.create_by
JOIN mrbs_room ON mrbs_entry.room_id = mrbs_room.id
AND
(mrbs_entry.start_time BETWEEN 145811700 AND 1463985000
OR ( mrbs_entry.end_time BETWEEN 1458120600 AND 1463992200 AND mrbs_users.id = 1)
)
JOIN mrbs_area ON mrbs_room.area_id = mrbs_area.id

DISTINCT SQL query with inner joins that omits a column from considerations,

I have a DB2 query as follows:
SELECT DISTINCT RETAILMASTERFILE.DOIDCD AS "RETAILMASTERFILE_DOIDCD",
RETAILMASTERFILE.COCOMO AS "RETAILMASTERFILE_COCOMO",
#XENOS.CUSTREF AS "XENOS_CUSTREF",
#XENOS.ADDUDT AS "XENOS_ADDUDT",
#XENOS.ADUPDD AS "XENOS_ADUPDD",
#XENOS.ADUPDT AS "XENOS_ADUPDT",
#XENOS.ADSTAT AS "XENOS_ADSTAT"
FROM RETAILMASTERFILE INNER JOIN
#XENOS ON RETAILMASTERFILE.DOCOMP = #XENOS.ADCOMP
AND RETAILMASTERFILE.COCOMO = #XENOS.ADDELN
WHERE (RETAILMASTERFILE.DOIDCD = 'CUST008')
AND (RETAILMASTERFILE.COCOMO = '345126032')
AND (RETAILMASTERFILE.DOCOMP = 'LONDON')
The problem is #XENOS.ADUPDT may not be unique which gives me an unwanted duplicate record.
Is there any way I can exclude this from consideration ? Everything I've tried so far within my limited knowledge and crude understanding of group by has so far broken my query.
Use GROUP BY instead:
SELECT RETAILMASTERFILE.DOIDCD AS "RETAILMASTERFILE_DOIDCD",
RETAILMASTERFILE.COCOMO AS "RETAILMASTERFILE_COCOMO",
#XENOS.CUSTREF AS "XENOS_CUSTREF",
#XENOS.ADDUDT AS "XENOS_ADDUDT",
#XENOS.ADUPDD AS "XENOS_ADUPDD",
MAX(#XENOS.ADUPDT) AS "XENOS_ADUPDT",
#XENOS.ADSTAT AS "XENOS_ADSTAT"
FROM RETAILMASTERFILE INNER JOIN
#XENOS
ON RETAILMASTERFILE.DOCOMP = #XENOS.ADCOMP AND
RETAILMASTERFILE.COCOMO = #XENOS.ADDELN
WHERE (RETAILMASTERFILE.DOIDCD = 'CUST008') AND (RETAILMASTERFILE.COCOMO = '345126032') AND
(RETAILMASTERFILE.DOCOMP = 'LONDON')
GROUP BY RETAILMASTERFILE.DOIDCD,
RETAILMASTERFILE.COCOMO,
#XENOS.CUSTREF,
#XENOS.ADDUDT,
#XENOS.ADUPDD,
#XENOS.ADSTAT;

SQL - Query Select Joins through multiple tables

Here is the situation:
I search for Persons with ID (empr.empr_cb)
that has bill(s) to pay (transactions.montant)
this refers to transactions.compte_id
which is identical to comptes.id_compte
this refers to comptes.proprio.id
which is identical to empr.id_empr
that would give us the Person ID (empr.empr_cb)
I tried this, but I don't know what joins to set (cross Join?):
SELECT `empr`.`empr_cb`,`transactions`.`montant`
FROM `empr`,`comptes`,`transactions`
WHERE `transactions`.`montant` > `0`
AND `transactions`.`encaissement` = `0`
AND `transactions`.compte_id` = `comptes`.`id_compte`
AND `comptes`.`proprio_id` = `id_empr`
Any ideas how to put the joins?
This query is already using implicit INNER JOINs. It can be rewritten this way:
SELECT empr.empr_cb
, transactions.montant
FROM empr
JOIN comptes ON comptes.proprio_id = empr.id_empr
JOIN transactions ON transactions.compte_id = comptes.id_compte
WHERE transactions.encaissement = 0
AND transactions.montant > 0