我有下面的表格,并试图查找一个几十万个城市的列表的县代码。
create table counties (
zip_code_from char(5) not null,
zip_code_thru char(5) not null,
county_code char(3) not null
);
create table cities (
city text not null,
zip_code char(5) not null
);
我的第一个方法是在join中使用“between”:
select
ci.city, ci.zip_code, co.county_code
from
cities ci
join counties co on
co.zip_code between ci.zip_code_from and ci.zip_code_thru
我知道在甲骨文的世界里,这是不受欢迎的,而且确实表现得很糟糕。它需要超过8分钟来处理大约16,000个城市。邮政编码表有大约80,000条记录。我猜这个语法是一个美化的交叉连接?
from和thru代码都有索引,我可以控制结构,所以如果有帮助的话,我可以更改表。
我唯一的另一个想法是继续将表扩展到所有可能的值-类似于以下内容:
select
generate_series (
cast (zip_code_from as int),
cast (zip_code_thru as int)
) as zip_code,
*
from counties
这将使数据扩展到超过200,000条记录,这不是什么大问题,但我不确定这是否是我唯一的查询不可怕的资源。
我猜即使是在没有索引的情况下动态地做这件事也比在我的join中使用between
更好,但我希望有一个替代方案,无论是在我的SQL方面还是在表本身的结构方面。
我已经看到其他DBMS平台发布的这个问题,但是我已经能够用PostgreSQL实现在其他数据库中不可能(或实际)的小奇迹,所以我希望我错过了一些东西。
2条答案
按热度按时间dz6r00yl1#
几个月后,这个问题又出现了,我决定测试一下我的一些理论。
原始查询:
实际上实现了笛卡尔坐标。查询返回34,000行,耗时597秒。
如果我将邮政编码范围“预分解”为离散记录:
查询返回完全相同的行,但在2.8秒内完成。
因此,底线似乎是在连接(或任何不等式)中使用
between
是一个非常糟糕的主意。23c0lvtd2#
虽然很晚了,但我最近遇到了使用
BETWEEN
条件的join的问题。为了把这个问题具体化为最简单的形式,我有两张表第一个表大约有720万条记录,第二个表大约有300万条记录。连接需要以这样的方式发生,使得
customer_ip_int
在ip_int (inclusive)
和lead(ip_int) OVER(ORDER BY ip_int)
之间(不含)。我尝试了许多方法来重新表述连接条件、各种索引等。大部分工作是在修改后的
ip_location
的物化视图上完成的,它允许我将next_ip_int
作为列引用(而不是在查询本身中摆弄lead(ip_int) ...
)。所有这些都没有产生任何远程可行的东西。上面的解决方案(生成一个序列)对我来说也不可行,因为整数IP地址的序列(与邮政编码相反)跨越了 * 数十亿 * 个整数。
最后,成功的事情很简单(如果有点丑陋):
请注意,这需要
ip_location.ip_int
上的索引才有效。