Outer join with mandatory chain - sql

Imagine I have this data
people -> address changes -> address change reasons
I want details of all people
I also want to know if they have ever changed address because of fire. So I don't want to know what the reasons are but if they have changed address for 1 single reason.
Each person could possibly have multiple address change reasons
so I have
SELECT people.*
CASE WHEN add_change.reason_id is not NULL THEN
'Y'
ELSE
'N'
END as been_fire
from people
left outer join add_change ON person.id = add_change.person
left outer join add_change_reason ON add_change.reason_id = add_change_reason.id AND add_change_reason.text = 'FIRE'
but this returns multiple rows per person if they have many address changes.
I can't just use
left outer join add_change ON add_change.person = person.id AND add_change.reason_id = 5
as this isn't fixed data.

Use exists:
SELECT p.*,
(CASE WHEN EXISTS (SELECT 1
FROM add_change ac JOIN
add_change_reason acr
ON ac.reason_id = acr.id
NVL(address_change.reason,'N')
WHERE p.id = ac.person AND
acr.text = 'FIRE'
)
THEN 'Y' ELSE 'N'
END) as has_fire_address_change
from people p;
Note that this changes the flag to 'Y' and 'N', which is what the description of your problem suggests that you want.

You could do a left join to a derived table that only returns person IDs that do have the change reason 'FIRE':
SELECT p.*
CASE WHEN cr.person IS NOT NULL THEN 'Y' ELSE 'N' END as been_fire
from people
left join (
select ac.person
from add_change ac
where exists (select *
from add_change_reason acr
where acr.id = ac.reason_id
AND acr.text = 'FIRE')
) cr on cr.person = p.id

Related

JOIN AND CASE MORE AN TABLE

I have 2 tables; the first one ORG contains the following columns:
ORG_REF, ARB_REF, NAME, LEVEL, START_DATE
and the second one WORK contains these columns:
ARB_REF, WORK_STREET - WORK_NUM, WORK_ZIP
I want to do the following: write a select query that search in work and see if the WORK_STREET, WORK_ZIP are duplicate together, then you should look at WORK_NUM. If it is the same then output value ' ok ', but if WORK_NUM is not the same, output 'not ok'
I wrote this SQL query:
select
A.ARB_REF, A.WORK_STREET, A.WORK_NUM, A.WORK_ZIP
case when B.B = 1 then 'OK' else 'not ok' end
from
work A
join
(select
WORK_STREET, WORK_ZIP count(distinct , A.WORK_NUM) B
from
WORK
group by
WORK_STREET, WORK_ZIP) B on B.WORK_STREET = A.WORK_STREET
and B.WORK_ZIP = A.WORK_ZIP
Now I want to join the table ORG with this result I want to check if every address belong to org if it belong I should create a new column result and set it to yes in it (RESULT) AND show the "name" column otherwise set no in 'RESULT'.
Can anyone help me please?
While you can accomplish your result by adding a left outer join to the query you've already started, it might be easiest to just use count() over....
with org_data as (
-- do the inner join before the left join later
select * from org1 o1 inner join org2 o2 on o2.orgid = o1.orgid
)
select
*,
count(*) over (partition by WORK_STREET, WORKZIP) as cnt,
case when o.ARB_REF is not null then 'Yes' else 'No' end as result
from
WORK w left outer join org_data o on o.ARB_REF = w.ARB_REF

Adding CASE expression in the correct spot?

I want to change this query:
select
t.AccountA
,t.AccountB
,t.totalNumber
,a.Category
from TableA t
left join Accounts a
on t.ActNum = a.ActNum
left join
(select distinct
s.col1
from (
select ....
from Table
group by...
) st
left join (select S....
group by..
) g on...
left join (select... on ...
) t on ...
where...
)
so that c.AccountB displays "X" if it was a "Y". So I want to do something like
CASE WHEN c.AccountB = 'Y' THEN 'X' ELSE 'c.AccountB END
Except I'm having a problem where some data (a.Category) is coming from the table a, and table a doesn't have a record in it equal to "Y", so the join doesn't get the category data from a. That field is therefore blank. I'm trying to avoid adding it to that table and would rather change the query. How can I do this? What I think would work is:
select
t.AccountA
,t.AccountB
,t.totalNumber
,a.Category
from TableA t
left join ****** (Select CASE WHEN t.AccountB = 'Y' THEN 'X' ELSE 't.AccountB END Accounts a)
on t.ActNum = a.ActNum
left join
(select distinct
col1
from (
select ....
from Table
group by...
) sta
left join (select S....
group by..
) g on...
left join (select... on ...
) t on ...
where...
)
Where I put the CASE expression in the 7th line here by the asterisks ***
Will this return exactly the same records? This is a really long running query and difficult to test so I'm trying to run it as few times as possible, would like some input to help me so this doesn't turn into a 6 hour project.
EDIT: I had a typo, the first columns selected were supposed to reference the first table - I changed it (table "t")
First, this might be as simple as getting rid of the single quote before c.AccountB CASE WHEN c.AccountB = 'Y' THEN 'X' ELSE c.AccountB END Otherwise I'm not quite sure I understand what you want but I'll try:
If you just want to select then:
select
c.AccountA
,CASE WHEN c.AccountB = 'Y' THEN 'X' ELSE c.AccountB END AccountB
,totalNumber
,a.Category
from TableA t
left join Accounts a
on t.ActNum = a.ActNum
left join
...
If instead you want to use this as part of a join you'll have to use it in your join. Since you don't show how "c" is joined, nor how "c" and "a" are related I will try to give an example:
select
c.AccountA
,CASE WHEN c.AccountB = 'Y' THEN 'X' ELSE c.AccountB END AccountB
,totalNumber
,a.Category
from CheckRegister c
left join Accounts a
on a.ActNum = c.AccountA
left join Accounts b
on b.ActNum = CASE WHEN c.AccountB = 'Y' THEN 'X' ELSE c.AccountB END

How can I get a distinct count of a named inner join?

I've built a query for a summary table of information, and it's almost there, with one small bug. The confirmed_class_count variable comes back too high if there's multiple users on a class, leading me to believe that the number isn't distinct
Here's my current code:
SELECT "staffs".*,
count(distinct subclasses) as class_count,
sum(case when users.confirmed_at is not null then 1 else 0 end) confirmed_class_count
FROM
staffs
INNER JOIN classes as subclasses on staffs.staff_id = ANY(subclasses.staff)
INNER JOIN "classes_users" ON "classes_users"."class_id" = "subclasses"."id"
INNER JOIN "users" ON "users"."id" = "classes_users"."user_id"
INNER JOIN class_types ON class_types.code = subclasses.class_type_code
WHERE
(subclasses.closed_date is NULL OR subclasses.closed_date > '2019-09-06')
GROUP BY
staffs.id ORDER BY "staffs"."full_name" ASC
I want to replace the sum with something like (select distinct count(*) from subcases where users.confirmed_at is not null) as confirmed_case_count but I get relation "subclasses" does not exist.
How do I get what I'm intending here?
You can use count distinct with conditional aggregation. Replace
sum(class when users.confirmed_at is not null then 1 else 0 end) confirmed_class_count
^ looks like a typo, this should be case not class
with
count(distinct case when users.confirmed_at is not null then classes_users.class_id end) confirmed_class_count

SQL: Want to alter the conditions on a join depending on values in table

I have a table called Member_Id which has a column in it called Member_ID_Type. The select statement below returns the value of another column, id_value from the same table. The join on the tables in the select statement is on the universal id column. There may be several entries in that table with this same universal id.
I want to adjust the select statement so that it will return the id_values for entries that have member_id_type equal to '7'. However if this is null then I want to return records that have member_id_type equal to '1'
So previously I had a condition on the join (commented out below) but that just returned records that had member_id_type equal to '7' and otherwise returned null.
I think I may have to use a case statement here but I'm not 100% sure how to use it in this scenario
SELECT TOP 1 cm.Contact_Relation_Gid,
mc.Universal_ID,
mi.ID_Value,
cm.First_Name,
cm.Last_Name,
cm.Middle_Name,
cm.Name_Suffix,
cm.Email_Address,
cm.Disability_Type_PKID,
cm.Race_Type_PKID,
cm.Citizenship_Type_PKID,
cm.Marital_Status_Type_PKID,
cm.Actual_SSN,
cm.Birth_Date,
cm.Gender,
mc.Person_Code,
mc.Relationship_Code,
mc.Member_Coverage_PKID,
sc.Subscriber_Coverage_PKID,
FROM Contact_Member cm (NOLOCK)
INNER JOIN Member_Coverage mc (NOLOCK)
ON cm.contact_relation_gid = mc.contact_relation_gid
AND mc.Record_Status = 'A'
INNER JOIN Subscriber_Coverage sc (NOLOCK)
ON mc.Subscriber_Coverage_PKID = sc.Subscriber_Coverage_PKID
AND mc.Record_Status = 'A'
LEFT outer JOIN Member_ID mi ON mi.Universal_ID = cm.Contact_Gid
--AND mi.Member_ID_Type_PKID='7'
WHERE cm.Contact_Relation_Gid = #Contact_Relation_Gid
AND cm.Record_Status = 'A'
Join them both, and use one if the other is not present:
select bt.name
, coalesce(eav1.value, eav2.value) as Value1OrValue2
from BaseTable bt
left join EavTable eav1
on eav1.id = bt.id
and eav1.type = 1
left join EavTable eav2
on eav2.id = bt.id
and eav2.type = 2
This query assumes that there is never more than one record with the same ID and Type.

Enriching Table

This is my query:
SELECT
a.account_type AS ACCOUNT_TYPE
,b.at_account_type_desc
,COUNT(a.BAN) AS num_BAN
FROM csm_adx.billing_account_act AS a
LEFT OUTER JOIN csm_adx.account_type_act AS b ON a.account_type = b.at_acc_type
GROUP BY 1,2
Now I want to connect it to another table TABLE_C which contains the information is the account: tentative, cancelled, closed, suspended, open.
I would like my result table to contain aditional three columns: ACTIVE_BAN, SUSPENDED_BAN and CANCELLED_BAN
and that each value contains the number of current active, suspended and cancelled bans. I´m using Teradata.
Can you please help me do this?
This is the result when the table is connected with another table which contains BAN status:
SELECT
a.account_type AS ACCOUNT_TYPE
,b.at_account_type_desc
,c.description
,COUNT(a.BAN) AS num_BAN
FROM csm_adx.billing_account_act AS a
LEFT OUTER JOIN csm_adx.account_type_act AS b
ON a.account_type = b.at_acc_type
LEFT OUTER JOIN csm_adx.acct_status AS c
ON a.ban_status = c.original_status_code
GROUP BY 1,2,3
SELECT
a.account_type AS ACCOUNT_TYPE
,b.at_account_type_desc
,COUNT(a.BAN) AS num_BAN ,
sum(case when a.column=value then 1 else 0 end) as 'user_colname1',
sum(case when b.column=value then 1 else 0 end) as 'user_colname2'
FROM csm_adx.billing_account_act AS a
LEFT OUTER JOIN csm_adx.account_type_act AS b
ON a.account_type = b.at_acc_type
GROUP BY 1,2