Reputation: 11
I have a customer table and some customers can have two different addresses. One address is marked as Primary and the other is marked as Mailing Only. If the customer has two different addresses, I would like to pull just the one that is marked as Mailing Only. If they have multiple accounts but none indicates Mailings Only, then I want just the Primary.
Name | PrefMailings | Address
John B. | Primary | PO Box 123
John B. | Mailing Only | 123 Street
Tim T. | Primary | 999 MyAddress
Tim T. | Joint | 999 MyAddress
Susan | Primary | 987 Home St.
RESULT
Name | PrefMailings | Address
John B. | Mailing Only | 123 Street
Tim T. | Primary | 999 MyAddress
Susan | Primary | 987 Home St.
In the WHERE clause, I used "PrefMailings = Primary or Mailing Only" still not getting the preferred "Mailing Only" address. Any suggestion is greatly appreciated for this newby.
Upvotes: 1
Views: 629
Reputation: 48139
I would hope that you are actually joining based on something more than just the name, but you know your data better and probably know that already... That said, you could do a a multiple join by the name and ensure you do get all customers too.
select
c.name,
case when mo.name is not null then mo.addess
when p.name is not null then p.address
when j.name is not null then j.address
end FinalAddress
from
( select distinct name
from customers ) c
left join customers mo
on c.name = mo.name and mo.prefMailings = 'Mailing Only'
left join customers p
on c.name = p.name and p.prefMailings = 'Primary'
left join customers j
on c.name = j.name and j.prefMailings = 'Joint'
Ensure your customer table has an index on (name, prefMailings) to optimize the joins. So you start with each customer and join to the customer table again 3 times for each possible mailing address type. If "Mailing Only" exists, grab it... if not, try "Primary" get it... if that fails, then "Joint".
Upvotes: 0
Reputation: 35593
I realize that what you are about to see may appear strange, but whilst it may look ungainly it has the advantage of only requiring one pass through the table, and hence it may perform better than alternatives needing multiple passes.
Please also note I have expanded the formatting to make it easier to understand the method employed. This syntax is for MySQL, but equivalent concatenation and substring functions exist in all SQL flavours.
SELECT
name
, substr(
MIN(
concat(
CASE
WHEN prefmailings = 'Mailing Only' THEN 1
WHEN prefmailings = 'Primary' THEN 2
WHEN prefmailings = 'Joint' THEN 3
ELSE 9 END
, prefmailings
)
)
FROM 2
) AS prefmailings
, substr(
MIN(
concat(
CASE
WHEN prefmailings = 'Mailing Only' THEN 1
WHEN prefmailings = 'Primary' THEN 2
WHEN prefmailings = 'Joint' THEN 3
ELSE 9 END
, address
)
)
FROM 2
) AS prioritized_address
FROM customers
WHERE prefmailings IN ( 'Mailing Only' , 'Primary' )
GROUP BY
name
;
The WHERE clause is optional by the way, and note that to change priority you simply change the numbers assigned to each mailing type. That priority number could be held in a table or derived table in lieu of the hardcoding seen above.
Upvotes: 0
Reputation: 13248
Another way is joining into an inline view that uses conditional aggregation:
select c.*
from customers c
join (select sum(case prefmailings
when 'Mailing Only' then 1
end) as prim,
name
from customers
group by name) v
on (v.prim = 1
and c.prefmailings = 'Mailing Only')
or (v.prim is null
and c.prefmailings = 'Primary')
and c.name = v.name
Fiddle: http://sqlfiddle.com/#!2/cdc15/8/0
Upvotes: 0
Reputation: 1269953
You can do this in standard SQL using union all
and some logic:
select c.*
from customers c
where c.PrefMailings = 'Mailing Only'
union all
select c.*
from customers c
where c.PrefMailings = 'Primary' and
not exists (select 1
from customers c2
where c2.Name = c.Name and c2.PrefMailing = 'Mailing Only'
) ;
Upvotes: 1