MySQL Sub Sub Query似乎循环

时间:2014-09-26 11:35:27

标签: mysql

我有这个MySQL查询,只能得到每个客户在给定月份的第一张发票总额:

SELECT SUM(InvoiceProductTotal)
FROM tblinvoiceproduct
WHERE InvoiceID IN (
        SELECT MIN(tblinvoice.InvoiceID) AS InvoiceID
        FROM tblinvoice
        WHERE tblinvoice.ClientID IN (
                SELECT tblclient.ClientID
                FROM tblclient
                LEFT JOIN tblenquiry ON tblclient.EnquiryID = tblenquiry.EnquiryID
                WHERE NOT tblclient.EnquiryID IS NULL
                    AND YEAR(EnquiryDate) = 2014
                    AND MONTH(EnquiryDate) = 9
                )
        GROUP BY tblinvoice.ClientID
        );

当我运行它时,它似乎永远循环。如果我删除第一部分,它会立即给我发票清单。我确定这是一个很小的语法细节,但是在尝试解决它的近一个小时后,我们无法弄清楚问题是什么。

感谢您的协助。

这个查询可能在没有所有子查询的情况下以更好的方式完成,只是我对子查询不太熟悉。 :)

已经给出了解决方案,但我应该包含完整的查询,而不仅仅是我遇到问题的部分。完整查询是:

SELECT AdvertisingID, AdvertisingTitle, AdvertisingYear,
       AdvertisingMonth, AdvertisingTotal, AdvertisingVisitors, 
       IFNULL(
             (SELECT SUM(InvoiceProductTotal)
              FROM tblinvoiceproduct
              JOIN
                  (SELECT MIN(tblinvoice.InvoiceID) AS InvoiceID
                   FROM tblinvoice
                   JOIN
                       (SELECT DISTINCT tblclient.ClientID
                        FROM tblclient
                        JOIN tblenquiry ON tblclient.EnquiryID = tblenquiry.EnquiryID
                        WHERE YEAR(tblenquiry.EnquiryDate)=tbladvertising.AdvertisingYear
                        AND MONTH(tblenquiry.EnquiryDate)=tbladvertising.AdvertisingMonth)
                   AS inq
                   ON tblinvoice.ClientID = inq.ClientID
               GROUP BY tblinvoice.ClientID) AS inq2
               ON tblinvoiceproduct.InvoiceID = inq2.InvoiceID)
       , 0)
FROM tbladvertising 
ORDER BY AdvertisingYear DESC, AdvertisingMonth DESC, AdvertisingTitle;

现在的问题是,带有子查询的列无法访问" tbladvertising.AdvertisingYear"或" tbladvertising.AdvertisingMonth"

2 个答案:

答案 0 :(得分:1)

一位评论者提到,您很难理解您在这里尝试做什么。我同意。但我会冒险尝试解决它。​​

与此类查询一样,利用结构化查询语言结构化部分很有帮助,并尝试构建此部分一块一块。这是创建实际执行您希望他们做的事情的复杂查询的秘诀。

你最内心的疑问是:

          SELECT tblclient.ClientID
            FROM tblclient
            LEFT JOIN tblenquiry ON tblclient.EnquiryID = tblenquiry.EnquiryID
            WHERE NOT tblclient.EnquiryID IS NULL
             AND YEAR(EnquiryDate) = 2014
             AND MONTH(EnquiryDate) = 9

它说,"给我2014年9月有查询的ClientID值列表。这是一种更有效的方法:

          SELECT DISTINCT tblclient.ClientID
            FROM tblclient
            JOIN tblenquiry ON tblclient.EnquiryID = tblenquiry.EnquiryID
           WHERE tblenquiry.EnquiryDate >= '2014-09-01'
             AND tblenquiry.EnquiryDate <  '2014-09-01' + INTERVAL 1 MONTH

此处有两处更改:首先,NOT ... IS NULL搜索是不必要的,因为如果您要搜索的项目为空,则您的EnquiryDate无法生效。所以我们只需将LEFT JOIN更改为普通的内部JOIN,并摆脱昂贵的NULL扫描。

其次,我们将日期匹配重新设置为范围扫描,因此它可以使用tbl.EnquiryDate上的索引。

冷却。

接下来,我们有了这个查询级别。

  SELECT MIN(tblinvoice.InvoiceID) AS InvoiceID
    FROM tblinvoice
   WHERE tblinvoice.ClientID IN (
            /* that list of Client IDs from the innermost query */
            )
  GROUP BY tblinvoice.ClientID

这非常简单。但是对于IN ()子句,MySQL并不太快,所以让我们以JOIN的形式重新编写它,如下所示:

  SELECT MIN(tblinvoice.InvoiceID) AS InvoiceID
    FROM tblinvoice
    JOIN (
           /* that list of Client IDs from the innermost query */
         ) AS inq ON tblinvoice.ClientID = inq.ClientID
  GROUP BY tblinvoice.ClientID

这会向我们提供发票ID列表,这些ID是本月第一次查询的主题,代表每个不同的ClientID。 (我很难弄清楚这个的商业含义,但我不了解你的业务。)

最后,我们来到你最外面的查询。我们也可以将其重新命名为JOIN,就像这样。

SELECT SUM(InvoiceProductTotal)
  FROM tblinvoiceproduct
  JOIN (
        /* that list of first-in-month invoices */
       ) AS inq2 ON tblinvoiceproduct.InvoiceID = inq2.InvoiceID

所以,这一切都扩展为:

SELECT SUM(InvoiceProductTotal)
  FROM tblinvoiceproduct
  JOIN (
      SELECT MIN(tblinvoice.InvoiceID) AS InvoiceID
        FROM tblinvoice
        JOIN (
               SELECT DISTINCT tblclient.ClientID
                 FROM tblclient
                 JOIN tblenquiry ON tblclient.EnquiryID = tblenquiry.EnquiryID
                WHERE tblenquiry.EnquiryDate >= '2014-09-01'
                  AND tblenquiry.EnquiryDate <  '2014-09-01' + INTERVAL 1 MONTH
             ) AS inq ON tblinvoice.ClientID = inq.ClientID
       GROUP BY tblinvoice.ClientID
       ) AS inq2 ON tblinvoiceproduct.InvoiceID = inq2.InvoiceID

那应该为你做的伎俩。总之,大的优化变化是

  1. 使用日期范围扫描。
  2. 取消NOT ... IS NULL条件。
  3. 将您的IN子句重新编辑为JOIN子句。
  4. 下一步将是创建有用的索引。 (EnquiryDate, EnquiryID)上的复合索引tblenquiry很可能会有很大帮助。但是要确保您需要进行一些EXPLAIN分析。

答案 1 :(得分:0)

如果您修改上面发布的查询,将子查询替换为JOININNER JOIN),如下所示,该怎么办?试一试。

SELECT SUM(InvoiceProductTotal)
FROM tblinvoiceproduct
JOIN
(
        SELECT MIN(ti.InvoiceID) as MinInvoice
        FROM tblinvoice ti
        JOIN
        (
                SELECT tblclient.ClientID
                FROM tblclient
                LEFT JOIN tblenquiry 
                ON tblclient.EnquiryID = tblenquiry.EnquiryID
                WHERE NOT tblclient.EnquiryID IS NULL
                    AND YEAR(EnquiryDate) = 2014
                    AND MONTH(EnquiryDate) = 9
        ) tab 
         on ti.ClientID = tab.ClientID
        GROUP BY ti.ClientID
) tab1
on tblinvoiceproduct.InvoiceID = tab1.MinInvoice