在不存在的'WHERE子句中优化使用NOT EXISTS和许多列的SQL查询

时间:2010-01-05 21:38:55

标签: sql sql-server sql-server-2005 optimization

编辑:使用SQL Server 2005。

我有一个查询,必须检查遗留数据库中的行是否已经导入新数据库并导入它们(如果它们尚不存在)。由于遗留数据库设计不当,因此遗留表中的行没有唯一ID,因此我必须使用启发式方法来确定是否已导入行。 (我无法控制遗留数据库。)新数据库的结构略有不同,我必须检查几个值,例如创建日期是否匹配,组号匹配等,以启发式方式确定该行是否存在于新数据库中或不。不是很漂亮,但它必须与传统系统接口的糟糕设计让我别无选择。

无论如何,系统的用户开始在系统中投入比我设计的数据多10倍到100倍的数据,现在查询运行得太慢了。你能建议一种让它更快的方法吗?这是代码,有些是为了保护隐私或简化,但我认为我离开了重要部分:

INSERT INTO [...NewDatabase...]
SELECT [...Bunch of columns...]
  FROM  [...OldDatabase...] AS t1
 WHERE t1.Printed = 0
  AND NOT EXISTS(SELECT *
                   FROM [...New Database...] AS s3
                  WHERE year(s3.dtDatePrinted) = 1850  --This allows for re-importing rows marked for reprint
                    AND CAST(t1.[Group] AS int) = CAST(s3.vcGroupNum AS int)
                    AND RTRIM(t1.Subgroup) = s3.vcSubGroupNum
                    AND RTRIM(t1.SSN) = s3.vcPrimarySSN
                    AND RTRIM(t1.FirstName) = s3.vcFirstName
                    AND RTRIM(t1.LastName) = s3.vcLastName
                    AND t1.CaptureDate = s3.dtDateCreated)

2 个答案:

答案 0 :(得分:2)

不知道架构是什么样的,您的第一步是EXPLAIN这些子查询。这应该会告诉你数据库在哪里咀嚼它的时间。如果没有索引,它可能会进行多次全表扫描。如果我不得不猜测,我会说t1.printeds3.dtDatePrinted是最重要的两个,因为它们会清除已经转换的内容。

还需要计算的任何内容都可能导致数据库不使用索引。例如,对RTRIMCAST的调用。这表明您在新数据库中有脏数据。永久修剪它,并看到将t1.group更改为正确的类型。

year(s3.dtDatePrinted) = 1850可能会欺骗优化器不使用s3.dtDatePrinted的索引(EXPLAIN应该让你知道)。这似乎只是您设置的标志,用于检查行是否已经转换,因此将其设置为特定日期(即1850-01-01 00:00:00)并执行特定匹配(即。{ {1}})现在这是一个简单的索引查找。

使您的比较更简单也会有所帮助。基本上你在这里有一个t1和s3之间的一对一关系(如果t1是新表的真实名称,考虑更具描述性的东西)。因此,不要将s3的每个位与t1匹配,只需给t1一列来引用其对应的s3行的主键。然后你只需要检查一件事。如果你不能改变t1那么你可以使用第3个表来跟踪t1到s3的映射。

一旦你拥有了这个,你所要做的就是一个连接来查找s3中不在t1中的行。

s3.dtDatePrinted = "1850-01-01 00:00:00"

答案 1 :(得分:0)

尝试替换它:

年(s3.dtDatePrinted)= 1850

有了这个:

s3.dtDatePrinted> =''1850-01-01'和s3.dtDatePrinted< '1851年1月1日'

在这种情况下,如果dtDatePrinted MAYBE上有索引,优化器可以使用范围索引扫描。

但我同意以前的海报,你应该避免使用RTRIM。一个想法是在s3中保留未修剪的(原始)值,或创建一个中间表,将未修剪的值映射到修剪的(新)值。甚至可以创建物化视图。但如果没有适当的索引,所有这些工作都毫无用处。