这是我的查询,它非常简单:
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。
答案 0 :(得分:4)
我会像这样重写它,并确保在i.INNUM
,ii.INNUM
和i.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.INNUM
和INVOICE_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_INNUM
和INVOICES.INNUM
之间存在外键(且INVOICE_ITEMS.II_INNUM
不可为空),那么您将永远不会遇到INVOICE_ITEMS
中无法匹配的记录INVOICES
中的记录。即使有,WHERE
条件正在使用INVOICES
中的值,因此您无论如何都要消除任何不匹配的行。只需使用常规JOIN
。