PostgreSQL选择加入不在列表中
该项目使用Postgres 9.3 我的表格(已简化)如下:PostgreSQL选择加入不在列表中,sql,postgresql,join,Sql,Postgresql,Join,该项目使用Postgres 9.3 我的表格(已简化)如下: t_person (30 million records) - id - first_name - last_name - gender t_city (70,000 records) - id - name - country_id t_country (20 records) - id - name t_last_city_visited (over 200 million records) - person_id - ci
t_person (30 million records)
- id
- first_name
- last_name
- gender
t_city (70,000 records)
- id
- name
- country_id
t_country (20 records)
- id
- name
t_last_city_visited (over 200 million records)
- person_id
- city_id
- country_id
- There is a unique constraint on person_id, country_id to
ensure that each person only has one last city per country
我需要做的是以下方面的变化:
获取访问过“英国”国家的女性身份证
但从未访问过国家“美国”
我试过以下方法,但速度太慢了
select t_person.id from t_person
join t_last_city_visited
on (
t_last_city_visited.person_id = t_person.id
and country_id = (select id from t_country where name = 'UK')
)
where gender = 'female'
except
(
select t_person.id from t_person
join t_last_city_visited
on (
t_last_city_visited.person_id = t_person.id
and country_id = (select id from t_country where name = 'USA')
)
)
我非常感谢您的帮助。提示:您想做的是找到那些有英国之行但没有美国之行的女性 比如:
select ...
from t_person
where ...
and exists (select null
from t_last_city_visited join
t_country on (...)
where t_country.name = 'UK')
and not exists (select null
from t_last_city_visited join
t_country on (...)
where t_country.name = 'US')
另一种方法是找到访问过英国而不是美国的人,然后你可以加入这些人,按性别进行筛选:
select person_id
from t_last_city_visited join
t_country on t_last_city_visited.country_id = t_country.id
where t_country.name in ('US','UK')
group by person_id
having max(t_country.name) = 'UK'
请运行分析并执行此查询,好吗
-- females who visited UK
with uk_person as (
select distinct person_id
from t_last_city_visited t
inner join t_person p on t.person_id = p.id and 'F' = p.gender
where country_id = (select id from t_country where name = 'UK')
),
-- females who visited US
us_person as (
select distinct person_id
from t_last_city_visited t
inner join t_person p on t.person_id = p.id and 'F' = p.gender
where country_id = (select id from t_country where name = 'US')
)
-- females who visited UK but not US
select uk.person_id
from uk_person uk
left join us_person us on uk.person_id = us.person_id
where us.person_id is null
这是形成此查询的多种方法之一。您可能需要运行它们来找出哪一个运行得最好,并且可能需要进行索引调整以使它们运行得更快。这是我的方法,您可以稍后用别名替换内部查询,如@zedfoxus所说
select
id
from
(SELECT
p.id id
FROM
t_person p JOIN t_last_city_visited lcv
ON(lcv.person_id = p.id)
JOIN country c
ON(lcv.country_id = c.id and cname = 'UK')
WHERE
p.gender = 'female') v JOIN
(SELECT
p2.id id
FROM
t_person p2 JOIN t_last_city_visited lcv2
ON(lcv2.person_id = p2.id)
JOIN country c
ON(lcv.country_id = c.id and cname != 'USA')
WHERE
p.gender = 'female') nv
ON(v.id = nv.id)
你还没有提到到底有多慢。所有这些表都有哪些额外的索引?正如Dmitry所说。您必须提供查询的分析结果。并告诉我们需要多少时间。对所有回应-谢谢。所有ID字段上都有索引(因为它们是外键)。可以(在代码中)提前完成(选择id,其中name=x)以不影响查询,因此它只是一个id查找查询。t如果索引设置正确,这可能是最好的改进。@David Aldridge:上面的第二个解决方案非常完美。快得多。非常感谢。虽然您的版本不依赖于我们和英国之间的字符串值吗?@Ask613——是的,确实如此,这是一种非常特殊的情况,可以以一种不一定扩展到其他类似情况的方式优化非常特定的查询。如果您想测试“已访问FR和英国,但未访问美国或CA”,那么您可能会回到基于EXISTS的版本。通过在公共表表达式中获取相关的国家ID并在相关的子查询中使用这些ID,可以进一步优化该方法,以避免连接。@davidridge不一定<代码>具有sum(当t_country.name在('FR','UK')中时为1结尾)=2和sum(当t_country.name在('US','CA')中时为1结尾)=0应执行以下操作trick@Aツ 是的,类似的原则。怎么样访问英国、美国和法国,或(加拿大和德国或墨西哥)?不,只是开玩笑。通常,您可以使用聚合来构建所有这些东西,但每一个都需要一些思考。我猜exists方法也是如此,但对于许多情况,它可能更容易构造。