【问题标题】:Optimizing NOT IN query in Access SQL在 Access SQL 中优化 NOT IN 查询
【发布时间】:2011-05-31 05:26:16
【问题描述】:

我是 Access 新手,正在使用 Access 2007。

我正在对包含访问研讨会的客户列表的数据库进行简单查询。

我想在客户上次访问后 3 个月内向他们发送服务提醒。我创建了一个查询,以便能够返回从当月起 3 个月访问过的客户列表。比如现在是五月,3个月前就是三月(包括五月)。

但是,3 个月前访问过的客户可能在 2 个月前再次访问过。例如,客户 A 在 3 月和 4 月到来。他的最后一次访问是在 4 月,因此,如果我要在 5 月运行查询,则不应出现在结果中,因为他的提醒应该只在 6 月发出。

我的查询已经解决了这个问题,但是速度很慢。它需要一些时间才能加载到 Access 中。任何帮助将不胜感激优化它。

这里唯一重要的字段是Invoice.DebCode,即数据库中的customersID。还有另外一张表DEBTOR,这是一张客户表以及他们的详细信息。

我使用了 INNER JOIN,因为我需要在结果中显示客户(债务人)地址和详细信息。

SELECT Invoice.InvNo, Invoice.InvDate, Invoice.DebCode, Debtor.DebName, Debtor.AddL1, Debtor.AddL2, Debtor.AddL3, Invoice.CarNo, Invoice.ChaNo, Invoice.ExcReason
  FROM Debtor 
  JOIN Invoice ON Debtor.DebCode = Invoice.DebCode
 WHERE Year(InvDate) = Year(Now())   
  AND Month(InvDate) = Month(Now()) - 2
  AND Invoice.DebCode NOT IN (SELECT Invoice.DebCode
                                FROM Invoice
                               WHERE Year(InvDate) = Year(Now()) 
  AND (   (Month(InvDate) = Month(Now()) -1) 
       OR (Month(InvDate) = Month(Now())) )

【问题讨论】:

  • 据我了解,本月您想有一个客户查询提醒。所以我认为您可以首先检索每个客户的最后一次访问,也许使用'SELECT ... TOP ...'。然后将其作为子查询,查看每个客户的最后一次访问是否在最近 3 个月内。

标签: sql ms-access query-optimization


【解决方案1】:

您可以通过调整 WHERE 子句来显着加快查询速度,以便直接针对日期字段进行比较(即,无需通过 Month() 和 Year() 函数传递它)。这样做可以让 Jet 引擎利用您在 Invoice.InvDate 字段上的索引(您确实为该字段编制了索引,对吗?)。

SELECT I.InvNo, I.InvDate, I.DebCode, D.DebName, D.AddL1, D.AddL2, D.AddL3,
       I.CarNo, I.ChaNo, I.ExcReason
FROM Debtor AS D
  INNER JOIN Invoice AS I 
  ON D.DebCode = I.DebCode
WHERE I.InvDate Between DateSerial(Year(Now()), Month(Now()) - 2, 1)
                    And DateSerial(Year(Now()), Month(Now()) - 1, 0)
  AND I.DebCode NOT IN 
(SELECT Invoice.DebCode FROM Invoice
 WHERE Invoice.InvDate > DateSerial(Year(Now()), Month(Now()) - 1, 0))

【讨论】:

  • 您好 mwolfe,非常感谢您的帮助! DateSerial 实际上将查询加速到不到一秒(我没有计时,但查询立即出来)我没有创建数据库,所以我去索引访问中的 InvDate 字段。我编辑了下一篇文章中显示的子查询。非常感谢您的帮助!
  • 关键不是 DateSerial() 本身,而是它将处理从表中的各个行移开。由于我们没有将任何表字段作为参数传递给 DateSerial() 它只需要评估一次。然后使用 DateSerial 的结果直接与日期字段进行比较。
【解决方案2】:

类似的东西呢:

SELECT a.debcode, a.debname, a.debstuff, b.most_recent AS last_over_three_months
FROM debtor AS a INNER JOIN 
(
SELECT debcode, Max(invdate) AS most_recent
FROM invoice
GROUP BY debcode
)
as b 
ON a.debcode= b.debcode
WHERE (month(now()) - Month(most_recent) >2);

您将不得不调整您的资料,但想法是一个子查询,它选择最近的客户访问,然后从中仅选择符合您月份条件的记录。

【讨论】:

    【解决方案3】:

    感谢 mwolfe02 的建议,我设法加快了查询速度。 为了存档和完成,我将在下面解释我的sql语句。

    SELECT I.InvNo, I.InvDate, I.DebCode, D.DebName, D.AddL1, D.AddL2, D.AddL3,
           I.CarNo, I.ChaNo, I.ExcReason
    FROM Debtor AS D
      INNER JOIN Invoice AS I 
      ON D.DebCode = I.DebCode
    WHERE I.InvDate Between DateSerial(Year(Now()), Month(Now()) - 2, 1)
                                  And         DateSerial(Year(Now()), Month(Now())- 1, 0)
    
      AND I.DebCode NOT IN 
    (SELECT Invoice.DebCode FROM Invoice
     WHERE Invoice.InvDate Between DateSerial(Year(Now()), Month(Now()) - 1, 1)
                                              And         DateSerial(Year(Now()), Month(Now()), 0))
    

    我编辑了底部子查询,因为 mwolfe 仅检查了当月的客户。只有在 3 个月前来过的客户才有资格获得提醒。也就是说,他们不可能在本月和前一个月之间访问过。

    例如,客户 A 在 4 月和 5 月访问过。当前月份是 6 月,因此他没有资格获得提醒,因为他上次访问是在 5 月。

    客户 B 在 4 月和 6 月访问过,因此他没有资格获得提醒,因为他上次访问是在 6 月。

    因此,查询的英文版本为:

    获取从当月最后一天到 3 个月前到过且在当月和前一个月未到过的客户。

    希望对遇到同样问题的人有所帮助。

    正如 darkjh 和 mikey 所建议的,我们可以“选择最近的客户访问,然后从中仅选择符合您月份条件的记录。”

    谢谢大家!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多