【问题标题】:Speeding up a SQL search with multiple tables and joins使用多个表和连接加速 SQL 搜索
【发布时间】: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 子句中。

标签: mysql sql left-join query-performance


【解决方案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 版本对这些做了一些优化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-08
      • 1970-01-01
      • 2015-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多