Here am having Person and Address Tables .Some Persons may have address or not.If they have address then want to join address table otherwise no need to join.Please help to solve this case.
select p.name,nvl(a.address,'address not available') from person p,address a
where p.id = 2 and case
when p.addid is not null
then p.addid = a.id
else 0=0 end
The general solution - use Boolean logic. You cannot choose between complete expressions using CASE, so you should rewrite it to use a combination of AND and OR. Using the logic from your question, you would rewrite it as:
WHERE p.id = 2
AND
(
(p.addid IS NOT NULL AND p.addid = a.id)
OR (p.addid IS NULL AND 0=0)
)
Which ultimately simplifies down to:
WHERE p.id = 2
AND (p.addid IS NULL OR p.addid = a.id)
The specific solution for your query - use better JOIN syntax, and simply leverage a LEFT JOIN:
SELECT p.name, nvl(a.address,'address not available')
FROM person p
LEFT OUTER JOIN address a ON p.addid = a.id
WHERE p.id = 2
Try to use coalesce function as below
select p.name,nvl(a.address,'address not available') from person p,address a
where p.id = 2
and coalesce(p.addid,a.id)=a.id
select p.name, nvl(a.address, 'address not available')
from person p left outer join address a
on (p.addid = a.id )
where p.id = 2;
You can't use case, but you can achieve the same effect with combination of ors sand ands
select p.name,
nvl(a.address,'address not available')
from person p,
address a
where p.id = 2 and
( p.addid is not null AND p.addid = a.id
OR
p.addid is null
)
try this:
select p.name,nvl(a.address,'address not available') from person p,address a
where p.id = 2 and a.id = case when p.addid is not null then p.addid else a.id end;
Related
I am working with sql server through SSMS right now. How can i choose all people with multiple(>2)vacancies?
I am trying something like that, but i dont understand how to make part with "more than 2 vacancies"?
SELECT dbo.applicants.FirstName, dbo.vacancy.Name
FROM dbo.applicants INNER JOIN
dbo.VacancyApplicant ON dbo.applicants.id = dbo.VacancyApplicant.ApplicantId INNER JOIN
dbo.vacancy ON dbo.VacancyApplicant.VacancyId = dbo.vacancy.id WHERE dbo.vacancy.Name='third vacancy'
SELECT dbo.applicants.FirstName, dbo.vacancy.Name
FROM dbo.applicants A INNER JOIN
dbo.VacancyApplicant V ON A.id = V.ApplicantId
WHERE EXIST(
SELECT 1
FROM dbo.applicants INNER JOIN
dbo.VacancyApplicant ON dbo.applicants.id =
dbo.VacancyApplicant.ApplicantId INNER JOIN
dbo.vacancy ON dbo.VacancyApplicant.VacancyId = dbo.vacancy.id
WHERE A.id=dbo.applicants.id
GROUP BY dbo.applicants.id,dbo.vacancy.id
HAVING COUNT(1)>2
)
Group By and Having are you basic answer. Below is a simple solution, might not be ideal, but can give you the idea.
I am finding target "applicants" ids in subquery, that uses GROUP BY and HAVING then outer query joins to that to output FirstName and LastName of applicant
SELECT dbo.applicants.FirstName, dbo.applicants.LastName FROM
dbo.applicants a INNER JOIN
(
SELECT dbo.applicants.id
FROM dbo.applicants INNER JOIN
dbo.VacancyApplicant ON dbo.applicants.id = dbo.VacancyApplicant.ApplicantId INNER JOIN
dbo.vacancy ON dbo.VacancyApplicant.VacancyId = dbo.vacancy.id AND dbo.vacancy.Name='third vacancy'
GROUP BY dbo.applications.id
HAVING COUNT(dbo.vacancy.id) > 2
) targetIds ON a.id = targetIds.id
"more than 2 vacancies"?
Your question only mentions vacancies but your query is filtering for a particular name. I assume you really want more than two of that name.
If I understand correctly, you want aggregation:
SELECT a.FirstName, a.Name
FROM dbo.applicants a INNER JOIN
dbo.VacancyApplicant va
ON a.id = va.ApplicantId INNER JOIN
dbo.vacancy v
ON va.VacancyId = v.id
WHERE v.Name = 'third vacancy'
GROUP BY a.FirstName, v.Name
HAVING COUNT(*) > 2;
Note the use of table aliases. They make the query easier to write and to read.
WITH TempCTE AS (
SELECT DISTINCT ap.FirstName
,vc.Name
,COUNT (va.VacancyId) OVER (PARTITION BY ap.id) AS NoOfVacancies
FROM dbo.applicants ap
JOIN dbo.VacancyApplicant va
ON ap.id = va.ApplicantId
JOIN dbo.vacancy vc
ON va.VacancyId = vc.id
)
SELECT FirstName,[Name], NoOfVacancies FROM TempCTE
WHERE NoOfVacancies > 2
I am Trying to make Left Outer Join Use bellow code;
The Result Using (LEFT OUTER , RIGHT OUTER, FULL OUTER And INNER JOIN) is The same Result.
I am Try to return All Publisher Name Include Those who don't connected with any Books yet!
SELECT P.PublisherName,
COUNT(B.BookID) AS BookPublished
FROM LR_Publisher AS P LEFT OUTER JOIN LR_Book As B
ON P.PublisherID = B.PublisherID
WHERE (P.PublisherID = #pPublisherID OR #pPublisherID IS NULL)
GROUP BY PublisherName
Thanks In Advance
Publishers that have no books published are filtered out by GROUP BY.
It can be fixed by using GROUP BY ALL:
SELECT P.PublisherName,
COUNT(B.BookID) AS BookPublished
FROM LR_Publisher AS P
LEFT OUTER JOIN LR_Book As B ON P.PublisherID = B.PublisherID
-- WHERE (P.PublisherID = #pPublisherID OR #pPublisherID IS NULL)
GROUP BY PublisherName
you could write it this way
select P.PublisherName,
isnull(bi.BookPublished, 0) as BookPublished
from LR_Publisher as P
left join (
select B.PublisherID, Count(B.BookID) BookPublished
from LR_Book as B
where (#pPublisherID is null or B.PublisherID = #pPublisherID)
group by B.PublisherID
) bi on P.PublisherID = Bi.PublisherID
where (#pPublisherID is null or P.PublisherID = #pPublisherID)
I also was not aware of the filtering done by group by. Thank you for the question.
simple left join will help you want all publisher name including who does not published any book yet
SELECT P.PublisherName
FROM LR_Publisher AS P LEFT OUTER JOIN LR_Book As B
ON P.PublisherID = B.PublisherID
I have a query I need to create that pulls data from three different tables. Essentially the end result is to pull data for users that have not had any activity on a users' account since 07/01/2018, but they have to have an account plan as "x." Is there a way to manipulate this query I've created to get it to display what I need it to? Maybe somehow correlate it to a count of 0 on the TRANDATE column? Very lost here and could use some help!
select p.ID as ID, p.LAST as LastName, p.FIRST as FirstName
From gl
inner join p
on p.ID = gl.PID
left join psp
on psp.PLANNUM = gl.ACCNUM
where gl.ACCNUM = 'x'
and psp.ACTIVE = 1
and gl.TRANDATE <= to_date('07/01/2018', 'MM/DD/YYYY')
;
commit;
Thank you all very much and please let me know if there's anything else I can provide here.
You don't really specify your table structure, so I'm kinda guessing here. Does your "gl" table have more than one record per PID+ACCNUM? What are the primary keys? There are a lot of extra details you could provide.
select p.ID as ID, p.LAST as LastName, p.FIRST as FirstName, g.maxdate
From (select gl.PID, gl.ACCNUM, max(gl.TRANDATE) as maxdate
from gl
where gl.ACCNUM = 'x'
group by gl.PID, gl.ACCNUM) g
inner join p
on p.ID = g.PID
inner join psp
on psp.PLANNUM = g.ACCNUM
and psp.ACTIVE = 1
where g.maxdate <= to_date('07/01/2018', 'MM/DD/YYYY')
;
Also, (a) you don't need a commit on a select, and (b) a left/outer join is an inner join when you have its column in the WHERE clause (psp.ACTIVE = 1).
I would simply use aggregation and max() in the having clause:
select p.ID as ID, p.LAST as LastName, p.FIRST as FirstName
from gl inner join
p
on p.ID = gl.PID inner join
psp
on psp.PLANNUM = gl.ACCNUM
where gl.ACCNUM = 'x' and psp.ACTIVE = 1
group by p.ID, p.LAST, p.FIRST
having max(gl.TRANDATE) <= date '2018-07-01';
Note that the where condition on psp.ACTIVE turns the outer join into an inner join, so I changed the join type for readability.
Since all your display fields are from p, you can do grouping in inner queryfor the p.id which occur more than once. All the rest of ids(not in) are of your interest.
select p.ID as ID, p.LAST as LastName, p.FIRST as FirstName
From p where p.ID not in (
select p.ID From gl
inner join p on p.ID = gl.PID
left join psp on psp.PLANNUM = gl.ACCNUM
where gl.ACCNUM = 'x'
and psp.ACTIVE = 1
and gl.TRANDATE <= to_date('07/01/2018', 'MM/DD/YYYY')
group by p.ID
having count(1) >= 1)
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'
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