进行更有效的加入

时间:2010-09-03 14:55:41

标签: sql join

这是我的查询,它非常简单:

SELECT
    INVOICE_ITEMS.II_IVNUM, INVOICE_ITEMS.IIQSHP
FROM
    INVOICE_ITEMS
LEFT JOIN
    INVOICES
ON 
    INVOICES.INNUM = INVOICE_ITEMS.II_INNUM
WHERE
    INVOICES.IN_DATE
BETWEEN
    '2010-08-29' AND '2010-08-30'
;

我对SQL的知识非常有限,但我正在尝试理解一些概念,如子查询等。我不是在寻找这个代码的重新设计,而是解释为什么它如此缓慢(在我的测试数据库上600多秒)以及如何让它更快。

根据我的理解,左连接是创建一个虚拟表并用连接中的每个结果行填充它,这意味着它正在处理每一行。如何阻止查询完全读取表并首先找到WHERE/BETWEEN子句,然后再创建一个虚拟表(如果可能的话)?

我的逻辑怎么样?是否有任何一致推荐的资源让我获得SQL忍者身份?

编辑:感谢大家的快速和礼貌的回应。目前,我正在通过ODBC连接到一个名为OMNIS的快速应用程序开发框架中使用的专有数据库。因此,我真的不知道正在运行什么样的优化,但我相信它基于MSSQL。

5 个答案:

答案 0 :(得分:4)

我会像这样重写它,并确保在i.INNUMii.INNUMi.IN_DATE上有索引。您的LEFT JOIN条款将INNER JOIN变为WHERE,因此我将其改写为:

SELECT ii.II_IVNUM, ii.IIQSHP 
FROM INVOICE_ITEMS ii
INNER JOIN INVOICES i ON i.INNUM = ii.II_INNUM 
WHERE i.IN_DATE BETWEEN '2010-08-29' AND '2010-08-30' 

根据您正在使用的数据库,可能发生的情况是INVOICE_ITEMS中的所有记录都被加入(由于LEFT JOIN),无论是否与{{INVOICE匹配1}}或者不是,然后WHERE子句将过滤到匹配范围内日期的子句。通过切换到INNER JOIN,您可以通过仅将WHERE子句应用于具有匹配INVOICES记录的INVOICE_ITEMS记录来提高查询效率。

答案 1 :(得分:3)

这是一个非常基本的查询,优化器应该可以正常使用它,可能你的问题是索引不正确。您是否在In_date字段和INVOICE_ITEMS.II_INNUM字段中有索引?如果您已正确设置PK Fk关系,则INVOICES.INNUM应已编入索引,但FK未自动编入索引。

答案 2 :(得分:2)

您的查询很好,这是您必须查看的索引。

INVOICES.INNUMINVOICE_ITEMS.II_INNUM是否已编入索引?

如果不是SQL必须做一些叫做'扫描'的事情 - 它会搜索每一条记录。

您可以将索引视为电话簿侧面的标签 - 您知道从何处开始根据姓氏的首字母查找人员。如果没有索引(比如你想找到以'...儿子'结尾的名字),你必须搜索整本书。

有不同类型的索引 - 它们可以被订购(如电话簿索引 - 所有按姓氏排序)或不(如书后面的索引 - 找到索引的开销,然后是实际的页)。

您还应该能够查看查询计划 - 这是服务器执行SQL语句的方式。这可以告诉你各种更高级的东西 - 例如,有多种方法可以完成这项工作:如果两个表都按连接字段排序,则嵌套连接是可能的,或者嵌套连接将循环遍历较小的表中的每个记录。较大的桌子。

答案 3 :(得分:1)

没有理由说这个查询很慢......唯一想到的是,你有INVOICES.INNUM = INVOICE_ITEMS.II_INNUM的索引吗?如果你添加它们可以加快选择速度,但它会减慢更新/插入...

答案 4 :(得分:1)

连接不会在概念级别上创建“虚拟表”。

查询的性能问题很可能在于索引编制不佳或不足。你应该有索引:

INVOICE_ITEMS.II_INNUM
INVOICES.IN_DATE

您还应该在INVOICES.INNUM上有一个索引,但如果这是该表的主键,那么它已经有一个。

此外,请勿在此处使用左连接。如果INVOICE_ITEMS.II_INNUMINVOICES.INNUM之间存在外键(且INVOICE_ITEMS.II_INNUM不可为空),那么您将永远不会遇到INVOICE_ITEMS中无法匹配的记录INVOICES中的记录。即使有,WHERE条件正在使用INVOICES中的值,因此您无论如何都要消除任何不匹配的行。只需使用常规JOIN