在 FROM 子句中创建大型虚拟表

Posted

技术标签:

【中文标题】在 FROM 子句中创建大型虚拟表【英文标题】:Creating a large virtual table in FROM clause 【发布时间】:2014-06-03 18:28:15 【问题描述】:

背景

我有一个 Java 应用程序,它定期从一个数据库 (mysql) 获取结果集,并尝试在另一个数据库中查找匹配项(具体为 Postgres / PostGIS)。

问题

目前,应用程序查询 Postgres 数据库一次,结果集中的 MySQL 记录(可能超过数万条)。我正在尝试更改算法,以便应用程序生成一个查询,如果找到任何匹配项,该查询会产生多个结果。描述我的目标的另一种方式是,如果这两个表存在于同一个数据库系统中,则查询的行为应该类似于典型的 JOIN。

当前解决方案

为了解决这个问题,我在FROM 子句中创建了一个虚拟表。但是,我知道如何从值列表中执行此操作的唯一方法是编写单独的 SELECT 语句和 UNION。结果似乎有效,虽然我没有测试数千条记录的性能,但使用数百个这样的SELECT-UNION 语句似乎没有任何巨大影响。这是整个查询的相关部分,以说明我到目前为止所做的事情:

SELECT *, ST_Distance_Sphere(latlng, geom) as distance 
FROM rwis_sites 
    INNER JOIN 
(SELECT 1100 as unit_id, ST_GeomFromText('POINT(-81.19701 32.09279)', 4326) as geom UNION
 SELECT 1100 as unit_id, ST_GeomFromText('POINT(-81.19682 32.09224)', 4326) as geom 
  UNION
 SELECT 1100 as unit_id, ST_GeomFromText('POINT(-81.1968 32.09213)', 4326) as geom 
  UNION
... just a few more...hundred...thousand...
 SELECT 2266 as unit_id, ST_GeomFromText('POINT(-97.98719 29.57656)', 4326) as geom 
  UNION
 SELECT 2266 as unit_id, ST_GeomFromText('POINT(-97.98815 29.57602)', 4326) as geom
) virtualTable 
ON ST_Distance_Sphere(latlng, geom) < 10000 
ORDER BY ST_Distance_Sphere(latlng, geom) ASC limit 1

由于“虚拟表”是以编程方式生成的,因此我的工作量很小。

问题

但是,我担心这是否是一种“愚蠢”的方法(更不用说我尚未检测到的任何性能问题),最终我想知道:有没有更好的方法来创建类似的东西而无需数千SELECT-UNION 声明?

【问题讨论】:

你能不能从 MySQL 中导出所需的数据到一个描述的文件中,然后使用 PostgreSQL COPY 命令导入到一个 PostgreSQL 临时表中。从那里使用您创建的导入表查询您的主表。 @Bob 哦,好吧,这是一个有趣的想法。我会调查的;只要它可以从 Java 应用程序以编程方式完成,并且不会对性能产生负面影响。谢谢你的想法。 【参考方案1】:

这是动态创建values 表的更好方法

select *
from (
  values
    (1100::int, 'POINT(-81.19701 32.09279)'::geography(Point)),
    (1100::int, 'POINT(-81.19682 32.09224)'::geography(Point))
) as t(unit_id, geom)

但更好的办法可能是使用foreign data wrapper 将您的mysql 表带入PG。

编辑

如果 0.1° 只是 slightly more (mind cos(32.09)) 比 10000 m,您可能想尝试从主表中预筛选记录。

【讨论】:

嗯,很好,非常好。到目前为止,我没有发现这种方法有任何明显的性能差异,但它看起来更干净,而且我可能避免了 Marlin 提到的UNION 的性能问题。我将进一步研究数据包装器,我没有意识到这存在。谢谢! 您不能从“动态表”的空间索引中受益,因此如果两个表都有很多记录,它可能会很慢。你在rwis_sites(latlng) 上有一个空间索引,正如 Marlin Pierce 提到的,对吧?虽然它通常与 ST_DWithin 一起使用。如果您的客户端库支持它,如果您传递 Well Known Binary 而不是要转换为地理的文本,也可能会更好。 Prepared statement 如果您经常调用它可能会有所帮助。【参考方案2】:

首先,我建议使用“UNION ALL”而不是“UNION”。当您使用 UNION 时,它会尝试删除重复项,这是您不需要它做的工作。由于您有数千个值,因此检查重复项会变得很慢。

除了 SELECT 之外,我想不出另一种方法来在一个 SQL 语句中填充虚拟表。您可能会尝试在一个 SQL 语句中填充一个临时表,然后在一秒钟内执行连接。但是,这会带来交易问题,因此您可能希望坚持使用它的方式。

作为另一个有助于性能的提示,您的内部联接正在评估

ST_Distance_Sphere(latlng, geom) < 10000

对于 rwis_sites 中的每条记录和虚拟表中的(可能数千条)记录的每种组合,它无法使用索引来优化它。如果您在点的一个轴上建立索引,请在连接中使用 10000 的范围,并将 ST_Distance_Sphere 移动到 where 子句,它可能会运行得更快。矛盾的是,您正在添加更多工作,但如果它首先检查范围,它可以使用索引来取消许多组合的资格,并且仅在点沿一个轴靠近时检查实际距离。

【讨论】:

感谢UNION ALL 提示。 Re: 临时表,我也想过;还没有尝试过,但它可能会起作用,并且在我需要扩展进程数量之前,事务问题现在可能不是问题。回复:距离连接性能,我对一件事感到困惑:你说“在你的连接中使用 10000 的范围,并将 ST_Distance_Sphere” 移动到 where 子句......,我不太清楚您建议加入的内容。 "use a range of 10000" 部分让我失望,你的意思是在两个子句中执行相同的ST_Distance... 吗? 事务问题源于使用临时表,因为您的 Web 服务器将创建多个线程。如果您删除临时表中的所有行并插入新行,如果多个线程交错调用删除插入和选择,它可能会变得混乱。 对于范围注释,我当然不是说重复相同的条件。索引一个轴,例如经度。在连接中,使用“-97.98815 - R 和 -97.98815 + R 之间的长”代替“ST_Distance_Sphere(latlng, geom)

以上是关于在 FROM 子句中创建大型虚拟表的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Laravel 8 中的工厂在 tinker 中创建虚拟表数据并连接它们?

在Azure中创建多网卡虚拟机

select 选项的子句

Mysql DQL语言执行顺序

SQL查询语句执行顺序

sql语句的优先级