使用多个表和连接加速 SQL 搜索

Posted

技术标签:

【中文标题】使用多个表和连接加速 SQL 搜索【英文标题】:Speeding up a SQL search with multiple tables and joins 【发布时间】:2018-01-12 20:23:10 【问题描述】:

虽然我精通科技领域的不同领域,但我是 SQL 新手。我做了很多搜索,发现了很多类似的问题,不幸的是,在将其与我的查询相关联时,我无法理解我所看到的内容。所以请善待...

我们办公室的新软件允许对我们的数据库创建新的查询。我创建了一个搜索,提示用户输入电话号码,该查询将在我们系统中的所有帐户中搜索该电话号码,并显示具有该电话号码的帐户。我们的系统中大约有 179,000 个帐户,查询是在 7 个不同表的 19 个不同字段中检查提示中提供的电话号码。

搜索有效,但大约需要 33 秒,在当今时代,这感觉太长了,尤其是对于打电话的人来说,我们正在搜索他的帐户。但也许这和它会得到的一样好?我确实意识到我正在搜索很多信息。我也愿意将结果限制在过去 7 年的帐户中,但是当我添加此限定符时,它确实只显示了过去 7 年的结果,但根本没有加快搜索速度,所以我接受了退出。

有什么方法可以加快这个查询的速度吗?我们只搜索一个字段的简单电话搜索几乎是瞬间完成的,虽然我不希望这样,但我希望尽可能缩短查询时间。

代码如下:

SELECT
    '[!Enter Phone Number|String|0]' AS 'Phone',
    COLLACCT@.RECNUM AS 'AccountNumber',
    COLLACCT@.COLLECTORNUMBER AS 'Collector',
    COLLDEBT@.LASTNAME || ' ' || COLLDEBT@.FIRSTNAME AS 'MakerName',
    COLLDEBT1@.LASTNAME || ' ' || COLLDEBT1@.FIRSTNAME AS 'Co-MakerName',
    COLLDEBT@.CITY AS 'City',
    COLLDEBT@.STATEANDZIP AS 'StateAndZip',
    COLLACCT@.MASTERACCOUNT AS 'DebtorNumber'
FROM COLLDEBT@
    LEFT JOIN COLLACCT@ ON COLLACCT@.MASTERACCOUNT = COLLDEBT@.RECNUM
    LEFT JOIN U_CELLPHN@ ON U_CELLPHN@.MASTERLINK = COLLDEBT@.RECNUM
    LEFT JOIN COLLDEBT1@ ON COLLDEBT1@.MASTER = COLLDEBT@.RECNUM
    LEFT JOIN U_SPOUSEINFO@ ON U_SPOUSEINFO@.MASTERLINK = COLLDEBT@.RECNUM
    LEFT JOIN U_ASTSCRN@ ON U_ASTSCRN@.MASTERLINK = COLLDEBT@.RECNUM
    LEFT JOIN U_ASTSCRNB@ ON U_ASTSCRNB@.MASTERLINK = COLLDEBT@.RECNUM
    LEFT JOIN AUXDEBTOR@ ON AUXDEBTOR@.DEBTORMASTER = COLLDEBT@.RECNUM
WHERE 
    COLLDEBT@.PHONE = Phone
    OR U_SPOUSEINFO@.SPOUSEPHN = Phone
    OR U_CELLPHN@.CELLMKR = Phone
    OR COLLDEBT1@.PHONE = Phone
    OR U_ASTSCRN@.PHONE0 = Phone
    OR U_ASTSCRN@.PHONE1 = Phone
    OR U_ASTSCRN@.PHONE2 = Phone
    OR U_ASTSCRN@.PHONE3 = Phone
    OR U_ASTSCRN@.PHONE4 = Phone
    OR U_ASTSCRN@.PHONE5 = Phone
    OR U_ASTSCRN@.PHONE6 = Phone
    OR U_ASTSCRN@.PHONE7 = Phone
    OR U_ASTSCRN@.PHONE8 = Phone
    OR U_ASTSCRN@.PHONE9 = Phone
    OR U_ASTSCRNB@.PHONE10 = Phone
    OR U_ASTSCRNB@.PHONE11 = Phone
    OR U_ASTSCRNB@.PHONE12 = Phone
    OR U_ASTSCRNB@.PHONE13 = Phone
    OR U_ASTSCRNB@.PHONE14 = Phone
    OR AUXDEBTOR@.EMPLOYERPHONE = Phone
 GROUP BY 
    MakerName
 ORDER BY 
    MakerName

提前谢谢你!

【问题讨论】:

有很多电话。您的索引如何查找这些表?您是否有机会改进架构,使每条记录仅将一个电话存储在一个新的、闪亮的(和索引的)“电话”表中? 如果您只是 SQL 新手,那么您还不是“精通技术”哈哈!我建议您首先搜索带有电话号码的表格,合并客户密钥,然后查询每个债务人的详细信息。目前,使用所有这些左连接,您可能会成倍增加要过滤的行数。 @JNevill 什么,你没有 15 部手机?嘘.. 你有一个非常糟糕的数据结构。您应该考虑将所有电话号码存储在一个表中,其中电话号码只有一列。然后该表上的索引将为您提供惊人的性能。 WHERE 子句中的LEFT JOIN 中的表不应该有条件,这些条件应该在它们的ON 子句中。 【参考方案1】:

@otisbartleh,只是为了强调我在评论中描述的方法的基本结构。我还没有输入整个查询,所以我使用省略号来反映您要填写的位置。

基本上,您要做的是首先从与电话条件匹配的所有表中获取帐号列表,然后,一旦您获得了匹配(和去重)帐号的列表(我认为应该最少),然后您基本上只是在表格中查询这些帐户的详细信息(应该很快)。

如果您每天多次运行此查询以查找不同的电话号码,您可能需要创建一个表来索引数据库中的所有电话号码并将它们与帐号相匹配(这可能需要一点时间),然后很容易查找与任何特定个人电话号码匹配的帐户,因为您已经准备好带有交叉引用的表。

WITH u_astscrn_matches AS
(
    SELECT  
        masterlink AS master_debtor_id
    FROM 
        U_ASTSCRN@
    WHERE   
        @phone IN (phone0, phone1, phone2, ...)
)
,u_astscrn_b_matches AS
(
    SELECT  
        masterlink AS master_debtor_id
    FROM 
        U_ASTSCRNB@
    WHERE   
        @phone IN (phone10, phone11, phone12, ...)
)   
...

,all_matches AS
(
    SELECT master_debtor_id FROM u_astscrn_matches
    UNION
    SELECT master_debtor_id FROM u_astscrn_b_matches
    UNION
    ...
)

SELECT
    ...

FROM
    all_matches AS am

LEFT JOIN
    COLLDEBT@ AS cd
    ON (cd.recnum = am.master_debtor_id)

LEFT JOIN
    ... --join onto other tables to get all the debtor details

【讨论】:

我会尝试这些建议并报告。我很感激。【参考方案2】:

“不要跨列展开数组。”

重构架构。有一个主要用于电话号码的表格。它可能有一个从人到电话的 1:many 链接。 (或者你可能需要很多:很多。)

这样,它就是一个简单的JOIN 和一个电话号码的单一测试。

它去掉了电话号码的所有 15 列。它更优雅地处理手机少于 15 部的人。而且它可以让一个人拥有超过 15 个。

【讨论】:

【参考方案3】:

最直接的解决方案是使用 UNION,如下所示:

(注意:查询实际上并没有看起来那么复杂;为了清楚起见,我将每个 UNION 部分替换的原始部分都保留了。)

SELECT
    '[!Enter Phone Number|String|0]' AS 'Phone',
    COLLACCT@.RECNUM AS 'AccountNumber',
    COLLACCT@.COLLECTORNUMBER AS 'Collector',
    COLLDEBT@.LASTNAME || ' ' || COLLDEBT@.FIRSTNAME AS 'MakerName',
    COLLDEBT1@.LASTNAME || ' ' || COLLDEBT1@.FIRSTNAME AS 'Co-MakerName',
    COLLDEBT@.CITY AS 'City',
    COLLDEBT@.STATEANDZIP AS 'StateAndZip',
    COLLACCT@.MASTERACCOUNT AS 'DebtorNumber'
FROM COLLDEBT@
    LEFT JOIN COLLACCT@ ON COLLACCT@.MASTERACCOUNT = COLLDEBT@.RECNUM
WHERE 
    COLLDEBT@.PHONE = Phone
    OR COLLDEBT@.RECNUM IN (        
        -- LEFT JOIN U_CELLPHN@ ON U_CELLPHN@.MASTERLINK = COLLDEBT@.RECNUM
        SELECT MASTERLINK FROM U_CELLPHN@  WHERE CELLMKR = Phone
        -- OR U_CELLPHN@.CELLMKR = Phone        
        UNION       
        -- LEFT JOIN COLLDEBT1@ ON COLLDEBT1@.MASTER = COLLDEBT@.RECNUM
        SELECT MASTER FROM COLLDEBT1@ WHERE PHONE = Phone
        -- OR COLLDEBT1@.PHONE = Phone
        UNION
        -- LEFT JOIN U_SPOUSEINFO@ ON U_SPOUSEINFO@.MASTERLINK = COLLDEBT@.RECNUM
        SELECT MASTERLINK FROM U_SPOUSEINFO@ WHERE SPOUSEPHN = Phone
        -- OR U_SPOUSEINFO@.SPOUSEPHN = Phone
        UNION
        -- LEFT JOIN U_ASTSCRN@ ON U_ASTSCRN@.MASTERLINK = COLLDEBT@.RECNUM
        SELECT MASTERLINK
        FROM U_ASTSCRN@ 
        WHERE PHONE0 = Phone
            OR PHONE1 = Phone
            OR PHONE2 = Phone
            OR PHONE3 = Phone
            OR PHONE4 = Phone
            OR PHONE5 = Phone
            OR PHONE6 = Phone
            OR PHONE7 = Phone
            OR PHONE8 = Phone
            OR PHONE9 = Phone
        -- OR U_ASTSCRN@.PHONE0 = Phone
        -- OR U_ASTSCRN@.PHONE1 = Phone
        -- OR U_ASTSCRN@.PHONE2 = Phone
        -- OR U_ASTSCRN@.PHONE3 = Phone
        -- OR U_ASTSCRN@.PHONE4 = Phone
        -- OR U_ASTSCRN@.PHONE5 = Phone
        -- OR U_ASTSCRN@.PHONE6 = Phone
        -- OR U_ASTSCRN@.PHONE7 = Phone
        -- OR U_ASTSCRN@.PHONE8 = Phone
        -- OR U_ASTSCRN@.PHONE9 = Phone
        UNION
        -- LEFT JOIN U_ASTSCRNB@ ON U_ASTSCRNB@.MASTERLINK = COLLDEBT@.RECNUM
        SELECT MASTERLINK
        FROM U_ASTSCRNB@ 
        WHERE PHONE10 = Phone
            OR PHONE11 = Phone
            OR PHONE12 = Phone
            OR PHONE13 = Phone
            OR PHONE14 = Phone
        -- OR U_ASTSCRNB@.PHONE10 = Phone
        -- OR U_ASTSCRNB@.PHONE11 = Phone
        -- OR U_ASTSCRNB@.PHONE12 = Phone
        -- OR U_ASTSCRNB@.PHONE13 = Phone
        -- OR U_ASTSCRNB@.PHONE14 = Phone
        UNION       
        -- LEFT JOIN AUXDEBTOR@ ON AUXDEBTOR@.DEBTORMASTER = COLLDEBT@.RECNUM
        SELECT DEBTORMASTER FROM AUXDEBTOR@ WHERE EMPLOYERPHONE = Phone
        -- OR AUXDEBTOR@.EMPLOYERPHONE = Phone
    )    
 GROUP BY 
    MakerName
 ORDER BY 
    MakerName

此外,将 OR 子查询更改为 IN 列表可能会加快速度;更新的 mysql 版本对这些做了一些优化。

【讨论】:

以上是关于使用多个表和连接加速 SQL 搜索的主要内容,如果未能解决你的问题,请参考以下文章

通过表和其他表执行搜索与 SQL Server 中的外键连接的内容以及如何对数据进行排序 [关闭]

如何使用不同的表和不同的列名连接多个查询

SQL Clob 在 Clob 中使用连接搜索多个字符串,可能是 REGEXP_SUBSTR

SQL 连接表和查找最大值

使用 2 个表和 3 个日期字段连接以降序获取多个日期的问题

如何在 SQL 中的不同行上将多个时间段连接在一起?