How do I go about checking the existence of records in all tables JOIN`ed in a statement? - sql

I want ensure none of the tables included through INNER JOIN return 0 rows, resulting in an empty result-set due to the join being INNER. I want to be able to return information to the user about which table had no records, causing the query to return empty.
PS: Please ignore Foreign Keys for this question.
Example-query:
SELECT * FROM Person P
INNER JOIN [User] U ON U.PersonId = P.Id
INNER JOIN Email E ON E.UserId = U.Id
INNER JOIN Company C on C.Id = U.CompanyId
WHERE P.Id = 3
Let's say the user had no associated Email-records. I then want to be able to tell the user about this. Note: I only need to print the first failing step, I.E if the user has no Email and no Company, I only need to tell the user about him not having any Email.
How I would have solved it earlier:
I would perform the complete query in steps, building it up join by join, doing a whole lot of redundant querying. I really don't like this solution at all, which is why I'm asking for help.
-- Ensure Person exists
IF NOT EXISTS (
SELECT * FROM Person P
WHERE P.Id = 3
)
BEGIN
PRINT 'No associated Person was found';
RETURN 1;
END
-- Ensure User exists
IF NOT EXISTS (
SELECT * FROM Person P
INNER JOIN [User] U ON U.PersonId = P.Id
WHERE P.Id = 3
)
BEGIN
PRINT 'No associated User was found';
RETURN 1;
END
And so on. How can I write this more concise, in a way that solves the same problem but avoids repeating queries?
Thanks in advance.
Update
By looking at the answers, I realized my example was bad. Using LEFT JOIN is ok in this example, since the "join-chain" is not straight; Person joins User, then User joins in multiple directions, like this:
Person - User - Email
|
Company
I'll try to provide a different example:
SELECT * FROM a
INNER JOIN b ON b.a_id = a.id
INNER JOIN c ON c.b_id = b.id
INNER JOIN d ON d.c_id = c.id
INNER JOIN e ON e.d_id = d.id
INNER JOIN f ON f.e_id = e.id
INNER JOIN g ON g.f_id = f.id
The join-chain would then look like this:
a - b - c - d - e - f - g
If I were to use LEFT JOIN's to ensure existence of records, the IIF/CASE statements would get pretty terrible. Example:
SELECT
CASE WHEN b.id is null THEN 'b is null' END as b_is_null
CASE WHEN b.id is not null and c.id is null THEN 'b is null' END as c_is_null
..
CASE WHEN b.id is not null and c.id is not null and d.id is not null and e.id is not null and f.id is not null and g.id is null THEN 'g is null' AS g_is_null
FROM a
LEFT JOIN b ON b.a_id = a.id
LEFT JOIN c ON c.b_id = b.id
LEFT JOIN d ON d.c_id = c.id
LEFT JOIN e ON e.d_id = d.id
LEFT JOIN f ON f.e_id = e.id
LEFT JOIN g ON g.f_id = f.id
It can get really ugly. And these examples are with 1-character alias-names and 2-character property-names.
Keep in mind, I also want to check if the first table (Person/a, the one not joined) also returns rows.

You need to use left join. From your query, if a person doesn't have any user they obviously doesn't have any email or company. So all left join will be fine.
SELECT P.*,
IIF(U.PersonID IS NULL, 0, 1) AS isUserExists,
IIF(E.UserId IS NULL, 0, 1) AS isEmailExists,
IIF(C.Id IS NULL, 0, 1) AS isCompanyInformationExists
FROM Person P
LEFT JOIN [User] U ON U.PersonId = P.Id
LEFT JOIN Email E ON E.UserId = U.Id
LEFT JOIN Company C on C.Id = U.CompanyId
WHERE P.Id = 3
If U.PersonID IS NULL then no user existed, if E.UserId IS NULL then no email existed, if C.Id IS NULL then no company information existed for that person. By this you can use your messages as you need.

Use case statements like below and print your message.
select
case when u.PersonId is null and p.id is not null then 'user not exists'
WHEN u.PersonId IS NOT NULL AND p.id IS NOT NULL AND e.mailid is null THEN 'mail id not exist'
else 'No associated Person was found' end message
FROM Person P
left JOIN [User] U ON U.PersonId = P.Id
left JOIN Email E ON E.UserId = U.Id
left JOIN Company C on C.Id = U.CompanyId
WHERE P.Id = 3

DECLARE #Email VARCHAR(50)
DECLARE #Company VARCHAR(50)
SELECT #Email = E.Email, #Company = C.CompanyName FROM Person P
INNER JOIN [User] U ON U.PersonId = P.Id
LEFT JOIN Email E ON E.UserId = U.Id
LEFT JOIN Company C on C.Id = U.CompanyId
WHERE P.Id = 3
IF #Email IS NULL
Print 'NO Email'
IF #Company IS NULL
Print 'No Company'

Related

How to only run left join when the variable is not null?

I'm trying to simplify my stored procedure and I have one that is only using left join based on the user id that is passed in. If the user id is null, don't do left join, but if it is null, left join it with another table. How should I re-write it ? Thank you
CREATE OR ALTER PROCEDURE [dbo].[GetRoom]
#RoomId UNIQUEIDENTIFIER NULL,
#UserId UNIQUEIDENTIFIER
AS
BEGIN
IF (#UserId IS NULL)
BEGIN
SELECT r.Id, r.DisplayName
FROM Room r
INNER JOIN Game g ON r.GameId = g.Id
INNER JOIN ProfileDuplicated pd ON r.HostedById = pd.Id
WHERE r.Id = #RoomId
END
ELSE
SELECT
r.Id, r.DisplayName,
ru.Description, -- this is coming from the left join table
ru.Tags -- from left join table
FROM Room r
INNER JOIN Game g ON r.GameId = g.Id
INNER JOIN ProfileDuplicated pd ON r.HostedById = pd.Id
LEFT JOIN RoomUser ru ON ru.RoomId = r.Id
WHERE r.Id = #RoomId AND ru.UserId = #UserId
END
Currently your stored procedure return different number columns depending on the #UserId.
You may remove the IF condition, and combined it as one single query by moving ru.UserId = #UserId to ON condition. This will make it a true LEFT JOIN to table RoomUser.
This will means it always return 4 columns as result
SELECT r.Id,
r.DisplayName,
ru.Description,
ru.Tags
FROM Room r
INNER JOIN Game g ON r.GameId = g.Id
INNER JOIN ProfileDuplicated pd ON r.HostedById = pd.Id
LEFT JOIN RoomUser ru ON ru.RoomId = r.Id
AND ru.UserId = #UserId
WHERE r.Id = #RoomId
Try something like below,
SELECT
r.Id, r.DisplayName,
ru.Description, -- this is coming from the left join table
ru.Tags -- from left join table
FROM Room r
INNER JOIN Game g ON r.GameId = g.Id
INNER JOIN ProfileDuplicated pd ON r.HostedById = pd.Id
LEFT JOIN RoomUser ru ON ru.RoomId = r.Id
AND r.Id IS NOT NULL
AND r.Id = #RoomId
AND ru.UserId IS NOT NULL
AND ru.UserId = #UserId
You can use ISNULL function to check the NULL value of the parameter. (Assuming ru.userId will not be NULL)
WHERE r.Id = #RoomId AND
ru.UserId = ISNULL(#UserId, ru.UserId)

SQL query doesn't work in my JOIN exercises

I am using SQL query but query resulted 12 lines, needs to be 3 lines; I can't figure it out.
Not working query:
SELECT g.*
,c.TxtFobFiyati
,C.TxtTalepETeslimTarihi
,c.TslParaBirimi
,c.TxtFiyat
,C.TslDepoKodu
,c.TslOlcuBirimi AS 'Olcu'
,j.TxtAmbalajDara
,j.TxtPaletDara
,g.TxtAdet AS 'ambalajAdet'
,g.TxtPaletSayisi AS 'paletSayisi'
,j.TxtAmbalajAdi
,J.TxtAmbalajNetKG
,j.TxtBoxGrossWeight
,C.TxtAciklama AS 'KalemAciklama'
,G.TxtAciklama AS 'KalemAciklama2'
,g.TxtPaletUstu AS 'PaletUstu'
,g.TxtPaletSayisi AS 'PaletSayisi'
,c.TxtUrunAdiEng AS 'UrunAdi_Ingilizce'
,C.TxtFobFiyati AS 'FobFiyati'
,j.TslPaletTipi AS 'PaletTipi'
,j.TxtPaletDara AS 'PaletDara'
FROM E_KT_SAS_Form A
LEFT JOIN E_KT_SAS_Form_DtyUrunler2 B
ON A.ID = B.FORMID
LEFT JOIN E_KT_SAS_FrmSipUrunEkle C
ON B.DOCUMENTID = C.ID
LEFT JOIN E_KT_SAS_Form_DtyTeyitEdilen2 D
ON A.ID = D.FORMID
LEFT JOIN E_KT_SAS_FrmUretimTarihiEkle E
ON D.DOCUMENTID = E.ID
AND E.TslUrun = C.TslUrunKodu
LEFT JOIN E_KT_SAS_Form_DtySevkiyatlar F
ON A.ID = F.FORMID
LEFT JOIN E_KT_SAS_FrmSevkiyatEkle G
ON F.DOCUMENTID = G.ID
LEFT JOIN E_KT_SAS_FrmSipUrunEkle_DtyAmbalajlama H
ON C.ID = H.FORMID
LEFT JOIN E_KT_SAS_FrmUrunAmbalajlama J
ON H.DOCUMENTID = J.ID
WHERE E.TslOnay_TEXT = 'Evet'
AND C.TslUrunKodu = G.TslUrun
AND A.ID = 11682
Not working result
Correct result table
Step1) Start count with the driver table i.e. the one you have alias as 'A'. Check the count if you get 3. Continue.
Repeat step 1 with each new join and see where your count blows up to 12. That is your troubling join. Comment it out if possible and continue with joins. If you cannot do successive joins then find best solution to bring count back down to 3.
If your sure of your where clause great; else start without it too. If count is more than 3. Pop it back in.
select count(1)
FROM E_KT_SAS_Form A
LEFT JOIN E_KT_SAS_Form_DtyUrunler2 B ON A.ID = B.FORMID
LEFT JOIN E_KT_SAS_FrmSipUrunEkle C ON B.DOCUMENTID = C.ID
LEFT JOIN E_KT_SAS_Form_DtyTeyitEdilen2 D ON A.ID = D.FORMID
LEFT JOIN E_KT_SAS_FrmUretimTarihiEkle E ON D.DOCUMENTID = E.ID
AND E.TslUrun = C.TslUrunKodu
LEFT JOIN E_KT_SAS_Form_DtySevkiyatlar F ON A.ID = F.FORMID
LEFT JOIN E_KT_SAS_FrmSevkiyatEkle G ON F.DOCUMENTID = G.ID
LEFT JOIN E_KT_SAS_FrmSipUrunEkle_DtyAmbalajlama H ON C.ID = H.FORMID
LEFT JOIN E_KT_SAS_FrmUrunAmbalajlama J ON H.DOCUMENTID = J.ID
WHERE E.TslOnay_TEXT = 'Evet' AND C.TslUrunKodu = G.TslUrun AND A.ID = 11682

Add condition in where clause

I have 3 tables advert , application and people.
Select * from advert v, application a
inner join people p on p.id = a.id
where v.id=a.id
This query returns me all application irrespective of the gender. But sometimes in the advert table, gender is specified as M. So now i want the query above return me only application made by M. To get this value i need to add one more condition, p.gender = v.gender. How do i do this? Sometimes the value of v.gender = n/a. Then I wont need this condition. It should return me all application irrespective gender.
To get your desired results, you need to modify the join condition between advert and people to join the records in either case (v.gender = 'n/a' or p.gender = v.gender):
select *
from advert v
join application a
on a.id = v.id
join people p
on p.id = a.id
and (v.gender = 'n/a' or p.gender = v.gender)
I'm a little confused as to what you are asking but here is something to start with. Some example data would also be helpful
select *
from advert v
inner join application a
on a.id = v.id
inner join people p
on a.id = p.id
where p.gender = 'M';
or
select *
from advert v
inner join application a
on a.id = v.id
inner join people p
on a.id = p.id
where p.gender = v.gender;
You could potentially use an IN operator thusly:
WHERE v.GENDER IN ('N/A',p.GENDER)
This would return all records where v.GENDER = 'N/A' or where v.gender = p.GENDER

Joining a table (or not) based on a condition in SQL

I have the three tables following in my SQL Server 2008 database:
Cars
Drivers
UnavailableDrivers
What I want is to show unavailableDrivers to the end users if they set #isAvailable = 0. On the other hand, if isAvailable=1, then the end user should see only available drivers. Finally, if isAvailable is NULL then the user should see all the drivers.
declare #isAvailable bit;
I need to write a query like that:
Select *
From Cars c
Inner join Drivers d on (c.driverId = d.Id)
CASE
WHEN #isAvailable = 0 THEN inner join UnavailableDrivers uc on (c.driverId= uc.Id)
WHEN #isAvailable = 1 THEN inner join UnavailableDrivers uc on (c.driverId != uc.Id)
ELSE #IsAvailable END
-- ELSE -> basically DO NOT JOIN UnavailableDrivers
It is giving incorrect syntax error and I couldn't find the right syntax for a few hours unfortunately. Actually I don't feel the query is right either. So any help to fix my query on this logic would be appreciated!
The following query do you want:
SELECT *
FROM Cars c
INNER JOIN Drivers d ON (c.driverId = d.Id)
WHERE (#isAvailable IS NULL) -- will not join to UnavailableDrivers is isAvailable is null
OR (#isAvailable = 0 AND c.driverId IN (SELECT uc.id FROM UnavailableDrivers uc)) -- will join to UnavailableDrivers only if isAvailable equals to 0
OR (#isAvailable = 1 AND c.driverId NOT IN (SELECT uc.id FROM UnavailableDrivers uc)) -- will join to UnavailableDrivers because isAvailable equals to 1
Terrible approach, but you asked for it
USE [DB]
GO
CREATE PROCEDURE [GetDrivers]
#isAvailable bit
AS
BEGIN
IF (#isAvailable = 0)
BEGIN
Select * From Cars c
Inner join Drivers d on (c.driverId = d.Id)
inner join UnavailableDrivers uc on (c.driverId= uc.Id)
END
ELSE
begin
Select * From Cars c
Inner join Drivers d on (c.driverId = d.Id)
inner join UnavailableDrivers uc on (c.driverId!= uc.Id)
end
END
SELECT
*
, CASE
WHEN d.DriverId IS NOT NULL THEN 'Availible'
WHEN u.DriverId IS NOT NULL THEN 'Unavailible'
ELSE 'Driver is in both'
AS 'Driver Status'
FROM Cars c
LEFT JOIN Drivers d
ON c.DriverId = d.DriverId
LEFT JOIN UnavailableDrivers u
ON u.DriverId = c.DriverId

How to return multiple values when using SELECT EXISTS in postgresql

I have the following SQL query:
SELECT EXISTS (SELECT r.id FROM Rules r INNER JOIN rule_t c on c.id=r.rule_t.id
INNER JOIN user u on u.id = r.user_id
WHERE u.fmnum='2813'
AND c.name='default') ::int
Is there a way I can modify this so that I get two values back, the INT from the EXISTS method, and r.id?
I know that I can change the query so that I remove the EXISTS method... if the sub select returns anything at all, then I know the record exists... but I'm just wondering if its possible to do the above.
Thanks.
EDIT 1
I'm testing the following code in a new query window in pgadmin3...
SELECT *
FROM (
SELECT TRUE, r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
);
But I'm getting the following error:
ERROR: subquery in FROM must have an alias LINE 2: (
^ HINT: For example, FROM (SELECT ...) [AS] foo.
EDIT 2
SELECT *
FROM (
SELECT TRUE, r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
) AS x;
SELECT 1 AS does_exist, r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
LIMIT 1; -- may or may not be needed.
This does what you seem to be asking for: you get two columns. But you get no row if nothing is found.
If you want a row, even if nothing is found, you need a subquery:
SELECT sub.t_id IS NOT NULL AS does_exist, sub.id
FROM (SELECT 1) x -- dummy to guarantee 1 row
LEFT JOIN ( -- LEFT JOIN is crucial
SELECT r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
LIMIT 1 -- may or may not be needed.
) ON TRUE; -- join condition is always true
Or, simpler / faster:
SELECT 1 AS does_exist, r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
UNION ALL
SELECT 0, NULL
LIMIT 1;