我有这个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"
答案 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
那应该为你做的伎俩。总之,大的优化变化是
NOT ... IS NULL
条件。下一步将是创建有用的索引。 (EnquiryDate, EnquiryID)
上的复合索引tblenquiry
很可能会有很大帮助。但是要确保您需要进行一些EXPLAIN
分析。
答案 1 :(得分:0)
如果您修改上面发布的查询,将子查询替换为JOIN
(INNER 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