How can I join 3 tables, limit the query based on a condition, and return empty columns if condition not met? - sql

I have the following query currently:
select * from people
LEFT JOIN addresses
ON people.id = addresses.id
LEFT JOIN pers
ON people.id = pers.pers_id
WHERE people.id =:id
AND addresses.is_primary = 'Y'
Of course if there is no address where is_primary = 'Y' for a given person, the query doesn't return any results.
Without is_primary='Y', the query returns multiple addresses.
Is there any way, instead, to return null columns for all of the address fields in the event where there is no record for the id where is_primary = 'Y'?

You can do something like this -
select *
from people
LEFT JOIN addresses
ON people.pidm = addresses.pidm
and addresses.is_primary = 'Y'
RIGHT JOIN pers
ON people.id = pers.pers_id
WHERE people.id = :id

use case when
select *,case when addresses.is_primary not in('Y') then 'primary address different' else addresses.is_primary end as is_primary from people
LEFT JOIN addresses
ON people.pidm = addresses.pidm
RIGHT JOIN pers
ON people.id = pers.pers_id
WHERE people.id =:id

I strongly recommend that you not mix left join and right join. The query is just so hard to follow.
Instead, start with the table where you want to keep all the rows. Then only use left join. Sometimes, you may need to put conditions in the on clause.
In your case:
select *
from people p left join
addresses a
on p.pidm = a.pidm and a.is_primary = 'Y' left join
pers
on p.id = pers.pers_id
where p.id = :id;

Related

Access Subquery On mulitple conditions

This SQL query needs to be done in ACCESS.
I am trying to do a subquery on the total sales, but I want to link the sale to the province AND to product. The below query will work with one or the other: (po.product_name = allp.all_products) AND (p.province = allp.all_province); -- but it will no take both.
I will be including every month into this query, once I can figure out the subquery on with two criteria.
Select
p.province as [Province],
po.product_name as [Product],
all_price
FROM
(purchase_order po
INNER JOIN person p
on p.person_id = po.person_id)
left join
(
select
po1.product_name AS [all_products],
sum(pp1.price) AS [all_price],
p1.province AS [all_province]
from (purchase_order po1
INNER JOIN product pp1
on po1.product_name = pp1.product_name)
INNER JOIN person p1
on po1.person_id = p1.person_id
group by po1.product_name, pp1.price, p1.province
)
as allp
on (po.product_name = allp.all_products) AND (p.province = allp.all_province);
Make the first select sql into a table by giving it an alias and join table 1 to table 2. I don't have your table structure or data to test it but I think this will lead you down the right path:
select table1.*, table2.*
from
(Select
p.province as [Province],
po.product_name as [Product]
--removed this ,all_price
FROM
(purchase_order po
INNER JOIN person p
on p.person_id = po.person_id) table1
left join
(
select
po1.product_name AS [all_products],
sum(pp1.price) AS [all_price],
p1.province AS [all_province]
from (purchase_order po1
INNER JOIN product pp1
on po1.product_name = pp1.product_name)
INNER JOIN person p1
on po1.person_id = p1.person_id
group by po1.product_name, pp1.price, p1.province --check your group by, I dont think you want pp1.price here if you want to aggregate
) as table2 --changed from allp
on (table1.product = table2.all_products) AND (table1.province = table2.all_province);

PostgreSQL select multiple columns of a table that is connected via a many-to-many pivot

I have this query:
SELECT
a.account_uuid,
a.account_no,
a.account_group_uuid,
a.account_scope_uuid,
a.created_at,
a.deleted_at,
s.service_uuid,
s.status,
st.service_type,
(
SELECT
c.company
FROM companies c
WHERE a.company_owner_uuid = c.company_uuid
)
FROM
accounts a
LEFT JOIN
services s
ON a.account_uuid = s.account_uuid
LEFT JOIN
service_types st
ON s.service_type_uuid = st.service_type_uuid
WHERE
a.deleted_at IS NULL
ORDER BY
a.account_no
And I need to join and select multiple columns from a people table by way of a pivot table accounts_contacts that would have the account_uuid and a person_uuid. There are also is_primary and is_active columns on the accounts_contacts table and there will only be one primary at a time, so the end result would be a single first and last name. This is the idea of the query:
SELECT
p.first_name, p.last_name
FROM
people p
INNER JOIN
accounts_contacts ac
ON ac.account_uuid = a.account_uuid
AND ac.person_uuid = p.person_uuid
WHERE
ac.is_primary = true
AND ac.is_active = true
But not sure how to fit it into the above query. A subquery would only allow for one of the columns.
account_contacts is an "association" or "junction" table. It is not a pivot table.
The basic idea should be joins:
SELECT . . . ,
p.first_name, p.last_name
FROM accounts a LEFT JOIN
services s
ON a.account_uuid = s.account_uuid LEFT JOIN
service_types st
ON s.service_type_uuid = st.service_type_uuid LEFT JOIN
accounts_contacts ac
ON ac.account_uuid = a.account_uuid LEFT JOIN
people p
ON ac.person_uuid = p.person_uuid AND
ac.is_primary = true AND
ac.is_active = true

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 to itself to compare two different types of fields

sorry for the confusing title.
I have two tables, individu and orgcont. individu looks something like:
individu
-------------
ind_id
org_id
email
orgcont looks something like:
orgcont
-------------
ind_id
function_code
I have many users that can belong to the same org_id and they each of their own ind_id. The 'leaders' of the org_id have a function code of 'PRIM', everyone else's are irrelevant. I am trying to return a list of org_id and email of all of the users who have a 'PRIM' function code who have users that have NULL email addresses that are not 'PRIM'.
I have something like this, but the results are not correct. Any help would be appreciated:
select
i.email,
i.org_id
from
individu i,
orgcont o,
individu i2,
orgcont o2
where
i.ind_id = o.ind_id
and i.org_id = i2.org_id
and i2.ind_id = o2.ind_id
and o.function_code = 'PRIM'
and o2.function_code != 'PRIM'
and i2.email is NULL
Try it with exists:
select
i.email,
i.org_id
from
individu i
inner join orgcont o on
i.ind_id = o.ind_id
where
o.function_code = 'PRIM'
and exists (
select
1
from
individu i2
inner join orgcont o2 on
i2.ind_id = o2.ind_id
where
i2.org_id = i.org_id
and o2.function_code != 'PRIM'
and i2.email is null
)
This will only return you one row for each leader with folks in their org with null email addresses, as opposed to your original query, which will return duplicate rows.
Also note the join syntax--you want to do an inner join here, not a cross join, so do so explicitly.
Additionally, you could have just done a select distinct, but that's a little more expensive than the exists.
This should work, the INNER JOIN to the sub query will act as the filter
SELECT DISTINCT i.email, i.org_id
FROM individu i
INNER JOIN orgcont o ON i.ind_id = o.ind_id
INNER JOIN
( SELECT i.id
FROM individu i
INNER JOIN orgcont o ON i.ind_id = o.ind_id
WHERE o.function_code != 'PRIM' AND i.email IS NULL
) t ON t.id = i.id
WHERE o.function_code = 'PRIM'

Conditional Inner Join

I want to be able to inner join two tables based on the result of an expression.
What I've been trying so far:
INNER JOIN CASE WHEN RegT.Type = 1 THEN TimeRegistration ELSE DrivingRegistration AS RReg
ON
RReg.RegistreringsId = R.Id
RegT is a join I made just before this join:
INNER JOIN RegistrationTypes AS RegT ON R.RegistrationTypeId = RegT.Id
This SQL-script does not work.
So all in all, if the Type is 1, then it should join on the table TimeRegistration else it should join on DrivingRegistration.
Solution:
In my select statement I performed the following joins:
INNER JOIN RegistrationTypes AS RegT ON R.RegistrationTypeId = RegT.Id
LEFT OUTER JOIN TimeRegistration AS TReg ON TReg.RegistreringsId = R.Id AND RegT.Type = 1
LEFT OUTER JOIN DrivingRegistration AS DReg ON DReg.RegistreringsId = R.Id AND RegT.Type <>1
Then I edited my where-clause to output the correct, depending on the RegType, like this:
WHERE (CASE RegT.Type WHEN 1 THEN TReg.RegistreringsId ELSE DReg.RegistreringsId END = R.Id)
Try putting both tables in the query using LEFT JOIN's
LEFT JOIN TimeRegistration TR ON r.rid = TR.Id AND RegT.type =1
LEFT JOIN DrivingRegistration DR ON r.rid = DR.Id AND RegT.type <>1
Now, in you select clause, use
CASE RegType.Type WHEN 1 THEN TR.SomeField ELSE DR.someField END as SomeField
The other option is to use dynamic SQL
You probably need to perform two left joins, one onto TimeRegistration and one onto DrivingRegistration, and return the fields you want from the appropriate join table something like this:
LEFT JOIN TimeRegistration ON TimeRegistration.RegistreringsId = R.Id
LEFT JOIN DrivingRegistration ON DrivingRegistration.RegistreringsId = R.Id
and you select statement would be something like this:
SELECT CASE WHEN RegT.Type = 1 THEN TimeRegistration.Foo ELSE DrivingRegistration.Bar END
I like what you're trying to do, but I don't think SQL is that clever.
SELECT
R.foo, tr.bar
FROM
SomeTable AS R
INNER JOIN RegistrationTypes AS RegT ON R.RegistrationTypeId = RegT.Id
AND RegT1.Type = 1
INNER JOIN TimeRegistration AS tr ON /* whatever */
UNION
SELECT
R.foo, dr.bar
FROM
SomeTable AS R
INNER JOIN RegistrationTypes AS RegT ON R.RegistrationTypeId = RegT.Id
AND RegT1.Type = 2
INNER JOIN DrivingRegistration AS dr ON /* whatever */
So I had a scenario where there were three email columns in one table (don't ask why) and any of them could be null (or empty). In this example code I will just deal with a case where it is null.
I had to join it to another table by any of the emails to retrieve the users firstname.
Here is what worked
select
m.email1,
m.email2,
m.email3,
m2.firstName
from MyTable m
left join MyOtherTable m2 on m2.Email =
case when m.email1 is null then
case when m.email2 is null then
case when m.email3 null then
'nonexistent#mydomain.com' -- i stopped here
else m.email3 end
else wm.email2 end
else m.email1 end
Obviously you would include further conditions like
case when m.email1 is null or m.email1 = '' then ...
To cover for empty values.