当前位置: 首页 > 面试题库 >

SQLite-WHERE子句和UDF

柳晔
2023-03-14
问题内容

我有下面的SQLite表,其中包含198,305个地理编码的葡萄牙邮政编码:

CREATE TABLE "pt_postal" (
  "code" text NOT NULL,
  "geo_latitude" real(9,6) NULL,
  "geo_longitude" real(9,6) NULL
);

CREATE UNIQUE INDEX "pt_postal_code" ON "pt_postal" ("code");
CREATE INDEX "coordinates" ON "pt_postal" ("geo_latitude", "geo_longitude");

我在PHP中还具有以下用户定义的函数,该函数返回两个坐标之间的距离:

$db->sqliteCreateFunction('geo', function ()
{
    if (count($data = func_get_args()) < 4)
    {
        $data = explode(',', implode(',', $data));
    }

    if (count($data = array_map('deg2rad', array_filter($data, 'is_numeric'))) == 4)
    {
        return round(6378.14 * acos(sin($data[0]) * sin($data[2]) + cos($data[0]) * cos($data[2]) * cos($data[1] - $data[3])), 3);
    }

    return null;
});

只有 874个 记录的距离38.73311, -9.138707小于或等于1 km。

问题

UDF在SQL查询中可以完美地工作,但是由于某些原因,我不能在WHERE子句中使用它的返回值-例如,如果我执行查询:

SELECT
    "code",
    geo(38.73311, -9.138707, "geo_latitude", "geo_longitude") AS "distance"
    FROM "pt_postal" WHERE 1 = 1
        AND "geo_latitude" BETWEEN 38.7241268076 AND 38.7420931924
        AND "geo_longitude" BETWEEN -9.15022289523 AND -9.12719110477
        AND "distance" <= 1
    ORDER BY "distance" ASC
LIMIT 2048;

它返回1035条记录, _ 排序distance时间约为0.05秒, _但是
最后一条记录的距离为1.353km(大于我定义为最后一条的最大1 km WHERE)。

如果我删除以下子句:

AND "geo_latitude" BETWEEN 38.7241268076 AND 38.7420931924
AND "geo_longitude" BETWEEN -9.15022289523 AND -9.12719110477

现在,查询将花费近6秒钟的时间,并返回LIMIT排序的2048条记录(我的)distance。本来应该花很长时间,但它只应返回具有的
874条记录"distance" <= 1

EXPLAIN QUERY PLAN原始查询返回:

SEARCH TABLE pt_postal USING INDEX coordinates (geo_latitude>? AND geo_latitude<?)
#(~7500 rows)
USE TEMP B-TREE FOR ORDER BY

并且没有坐标边界:

SCAN TABLE pt_postal
#(~500000 rows)
USE TEMP B-TREE FOR ORDER BY

我想做什么

我想我知道为什么会这样,SQLite正在这样做:

  1. 使用索引coordinates过滤掉WHERE子句中边界之外的记录
  2. 通过"distance" <= 1 WHERE子句过滤那些记录, _ 但是distance仍然NULL => 0_!
  3. 填充“代码”和“距离”(通过首次调用UDF)
  4. 按“距离”排序(目前已填充)
  5. 限制记录

我想要SQLite做什么:

  1. 使用索引coordinates过滤掉WHERE子句中边界之外的记录
  2. 对于这些记录,填充codedistance通过调用UDF
  3. 通过"distance" <= 1 WHERE子句过滤记录
  4. 按“距离”排序(无需再次调用UDF)
  5. 限制记录

谁能解释我如何使SQLite表现出(如果可能的话)我想要的方式?

后记

出于好奇,我尝试对两次调用UDF的速度进行基准测试:

SELECT
    "code",
    geo(38.73311, -9.138707, "geo_latitude", "geo_longitude") AS "distance"
    FROM "pt_postal" WHERE 1 = 1
        AND "geo_latitude" BETWEEN 38.7241268076 AND 38.7420931924
        AND "geo_longitude" BETWEEN -9.15022289523 AND -9.12719110477
        AND geo(38.73311, -9.138707, "geo_latitude", "geo_longitude") <= 1
    ORDER BY "distance" ASC
LIMIT 2048;

令我惊讶的是,它仍然在约0.06秒内运行-并且仍然(错误地!)返回了1035条记录。

似乎第二个geo()电话甚至都没有被评估…但是应该,对吧?


问题答案:

基本上,我sprintf()用来查看要计算哪种边界坐标,并且由于无法在PHP以外的任何地方运行查询(由于UDF),因此我正在使用准备好的语句生成另一个查询。问题是,我没有生成最后一个绑定参数distance <= ?子句中的公里数),而被我的sprintf()版本愚弄了。

猜猜我在困倦时不应该尝试编码。对于您所浪费的时间,我们深表歉意,谢谢大家!

为了完整起见,以下记录(正确!)在约0.04秒内返回了873条记录:

SELECT "code",
    geo(38.73311, -9.138707, "geo_latitude", "geo_longitude") AS "distance"
    FROM "pt_postal" WHERE 1 = 1
        AND "geo_latitude" BETWEEN 38.7241268076 AND 38.7420931924
        AND "geo_longitude" BETWEEN -9.15022289523 AND -9.12719110477
        AND "distance" <= 1
    ORDER BY "distance" ASC
LIMIT 2048;


 类似资料:
  • 主要内容:语法,实例SQLite的 WHERE 子句用于指定从一个表或多个表中获取数据的条件。 如果满足给定的条件,即为真(true)时,则从表中返回特定的值。您可以使用 WHERE 子句来过滤记录,只获取需要的记录。 WHERE 子句不仅可用在 SELECT 语句中,它也可用在 UPDATE、DELETE 语句中,等等,这些我们将在随后的章节中学习到。 语法 SQLite 的带有 WHERE 子句的 SELECT

  • 本章提供了有关如何使用JDBC应用程序从表中选择记录的示例。 这将在从表中选择记录时使用WHERE子句添加其他条件。 在执行以下示例之前,请确保您具备以下示例 - 要执行以下示例,您可以使用实际用户名和密码替换用户名和密码。 您的MySQL或您正在使用的任何数据库已启动并正在运行。 所需的步骤 (Required Steps) 使用JDBC应用程序创建新数据库需要以下步骤 - Import the

  • 问题内容: 简而言之,我有一个表格,其中除其他外,还包含一个用于时间戳记的列。我想获得具有最新(即最大价值)时间戳的行。目前,我正在这样做: 但是我宁愿做这样的事情: 但是,SQLite拒绝此查询: 该文件证实了这一行为(页面底部): 聚合函数只能在SELECT语句中使用。 我的问题是:是否可以编写查询以获取具有最大时间戳的行,而无需对select进行排序并将返回的行数限制为1?这似乎应该有可能,

  • 问题内容: 我需要检索与列表中存储的ID匹配的记录。在运行时生成的查询很简单: 相当于 很好,但是如果列表包含成百上千个ID,该怎么办?该语句将非常庞大,并且在某些时候SQL解析器可能崩溃了,或者如果不是,则性能可能会很差。 我该如何以对所检索的记录数量不太敏感的方式来执行此操作? (我不能只遍历列表并逐个检索记录的原因是,我需要数据库对我执行ORDER BY。记录必须来自按特定字段排序的数据库,

  • 在MariaDB中,子句与,,和语句一起使用来选择或更改想要更改的满足指定特定行记录。 它是一个在表名后面出现的语句。 语法 注意:子句是一个可选的子句。它可以和,,,运算符一起使用。 1. WHERE子句与单一条件 示例: 我们有一个表,里边有一些数据。假设要查询表中的小于的所有记录。 执行上面查询语句,得到以下结果 - 2. WHERE子句与AND条件 为了方便演示,这里再插入一条数据 - 当

  • 主要内容:Oracle WHERE子句简介,Oracle WHERE示例在本教程中将学习如何使用Oracle 子句来指定过滤的条件返回符合查询条件的行记录。 Oracle WHERE子句简介 子句指定语句返回符合搜索条件的行记录。下面说明了子句的语法: 子句出现在子句之后但在ORDER BY子句之前。在WHERE关键字之后是 - 它定义了返回行记录必须满足的条件。 除了语句之外,还可以使用DELETE或UPDATE语句中的子句来指定要更新或删除的行记录。 Oracle