好吧,我完全讨厌自己这个问题;但我正在寻找另一种方法来做这个查询,这可能更快,更优雅(这看起来像垃圾)。告诉我你的想法:
SELECT TRUNCATE(SUM(sub.Total),3) AS GrpTotal, sub.ActualDate,
TRUNCATE(SUM(sub.BonusAmt),3) AS GrpBonusAmt, sub.UID,
sub.CUSTID, YEAR(MIN(sub.ActualDate)) AS Year, pusers.username
FROM ( SELECT a.UID, a.ActualDate, 'Global Report' AS Report,
SUM(a.totalpayment) AS Total, a.CUSTID,
((SUM(a.totalpayment)*IFNULL((u.retention_percent/100),1))+IFNULL(u.bonus_amount,0)) AS BonusAmt
FROM `globalreport` a
LEFT JOIN `users` u ON u.uid = a.UID
WHERE true AND a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT a.UID, a.ActualDate, 'Amex Residuals' AS Report,
SUM(a.payment) AS Total, a.CUSTID,
((SUM(a.payment)*IFNULL((u.retention_percent/100),1))+IFNULL(u.bonus_amount,0)) AS BonusAmnt
FROM `amexresiduals` a
LEFT JOIN `users` u ON u.uid = a.UID
WHERE true AND a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT a.UID, a.ActualDate, 'Compliance Fee' AS Report,
SUM(a.profit) AS Total, a.CUSTID,
((SUM(a.profit)*IFNULL((u.retention_percent/100),1))+IFNULL(u.bonus_amount,0)) AS BonusAmnt
FROM `compliancefee` a
LEFT JOIN `users` u ON u.uid = a.UID
WHERE true AND a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT a.UID, a.ActualDate, 'Checks On Demand' AS Report,
SUM(a.myprofit) AS Total, a.CUSTID,
((SUM(a.myprofit)*IFNULL((u.retention_percent/100),1))+IFNULL(u.bonus_amount,0)) AS BonusAmnt
FROM `geticheck` a
LEFT JOIN `users` u ON u.uid = a.UID
WHERE true AND a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT a.UID, a.ActualDate, 'Gift Cards on Demand' AS Report,
SUM(a.payment) AS Total, a.CUSTID,
((SUM(a.payment)*IFNULL((u.retention_percent/100),1))+IFNULL(u.bonus_amount,0)) AS BonusAmnt
FROM `gcod` a
LEFT JOIN `users` u ON u.uid = a.UID
WHERE true AND a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT a.UID, a.ActualDate, 'Global Check' AS Report,
SUM(a.myprofit) AS Total, a.CUSTID,
((SUM(a.myprofit)*IFNULL((u.retention_percent/100),1))+IFNULL(u.bonus_amount,0)) AS BonusAmnt
FROM `globalcheck` a
LEFT JOIN `users` u ON u.uid = a.UID
WHERE true AND a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT a.UID, a.ActualDate, 'Bonus True Up' AS Report,
SUM(a.finalpayment) AS Total, a.CUSTID,
((SUM(a.finalpayment)*IFNULL((u.retention_percent/100),1))+IFNULL(u.bonus_amount,0)) AS BonusAmnt
FROM `bonustrueup` a
LEFT JOIN `users` u ON u.uid = a.UID
WHERE true AND a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT a.UID, a.ActualDate, 'Bonus Take Back - Did Not Activate' AS Report,
SUM(a.amount) AS Total, a.CUSTID,
((SUM(a.amount)*IFNULL((u.retention_percent/100),1))+IFNULL(u.bonus_amount,0)) AS BonusAmnt
FROM `bonusadjnosetup` a
LEFT JOIN `users` u ON u.uid = a.UID
WHERE true AND a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT a.UID, a.ActualDate, 'Bonus Take Back - Closed Less Than 6 Months' AS Report,
SUM(a.amount) AS Total, a.CUSTID,
((SUM(a.amount)*IFNULL((u.retention_percent/100),1))+IFNULL(u.bonus_amount,0)) AS BonusAmnt
FROM `bonusadjclosed6mo` a
LEFT JOIN `users` u ON u.uid = a.UID
WHERE true AND a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT a.UID, a.ActualDate, 'Month End Fee Rejects' AS Report,
SUM(a.amount) AS Total, a.CUSTID, SUM(a.amount) AS BonusAmnt
FROM `merchantloss` a
LEFT JOIN `users` u ON u.uid = a.UID
WHERE true AND a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT a.UID, a.ActualDate, 'Direct ACH Debits and Credits' AS Report,
SUM(a.amount*-1) AS Total, a.CUSTID, SUM(a.amount*-1) AS BonusAmnt
FROM `dirachdebcred` a
LEFT JOIN `users` u ON u.uid = a.UID
WHERE true AND a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT a.UID, a.ActualDate, 'Merchant Adjustments' AS Report,
SUM(a.amount) AS Total, a.CUSTID,
((SUM(a.amount)*IFNULL((u.retention_percent/100),1))+IFNULL(u.bonus_amount,0)) AS BonusAmnt
FROM `merchantadj` a
LEFT JOIN `users` u ON u.uid = a.UID
WHERE true AND a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
) sub
LEFT JOIN `pending_users` pusers ON pusers.UID = sub.UID
WHERE sub.CUSTID = 1020
AND sub.`UID` NOT IN
( SELECT `UID`
FROM `users`
WHERE `is_admin` AND `company_id` = sub.`CUSTID`)
GROUP BY sub.ActualDate, sub.UID, sub.Report
ORDER BY sub.ActualDate ASC
显然,这是一个冗长的查询。我只是不确定它是否必须如此。基本上,我正在收集并汇总每个联合表中的不同列,并在最后对该数量进行分组,这样我就可以从所有表中获得总和。
答案 0 :(得分:1)
由于您从多个不同的表中获得结果,因此您的查询实际上看起来非常好。我同意SQL文本的长度有点令人生畏。
整体方法看起来似乎是获得指定结果的最有效方法。我假设针对各个表的内部查询正在折叠很多行。
就性能而言,我唯一要改变的是NOT IN
谓词,因为这将导致每行执行子查询。
您可以使用反连接模式获得等效结果,假设UID
是users
表中的主键,或者它至少保证为NOT NULL。 (我假设子查询不返回NULL;如果是,则NOT IN谓词不会返回TRUE,并且根本不会返回任何行。)
所以这个:
WHERE sub.CUSTID = 1020
AND sub.`UID` NOT IN
( SELECT `UID`
FROM `users`
WHERE `is_admin` AND `company_id` = sub.`CUSTID`)
可以替换为等效,但(通常)更有效:
LEFT
JOIN `users` n
ON n.is_admin
AND n.company_id = sub.`CUSTID`
AND n.`UID` = sub.`UID`
WHERE n.`UID` IS NULL
AND sub.CUSTID = 1020
反连接模式正在寻找匹配的行,然后排除任何匹配的行,所以我们剩下的就是那些不匹配的行。
实际上,不需要检查sub.CUSTID = 1020
,可以省略。我们已经确保sub
内联视图中的每个查询都是1020。
在外部查询的SELECT列表中,我认为这个MIN聚合会增加一些混淆:
YEAR(MIN(sub.ActualDate))
它看起来没必要,因为查询正在执行GROUP BY sub.ActualDate
,并且因为YEAR
函数是确定性的(即YEAR(foo)
返回的值对于相等的值将是相等的foo
。我只用以下代替:
YEAR(sub.ActualDate)
实际上,我认为在外部查询中根本不需要执行GROUP BY
操作。
Report
sub
列的返回值与每个UNION ALL
查询不同。并且每个查询都已执行GROUP BY UID, ActualDate
。因此,来自(UID,ActualDate,Report)
的{{1}}元组已经保证是唯一的。
唯一的问题是sub
表中UID
是否唯一。如果不是,那么查询返回的总数将增加一倍,三倍等,并且只从一个匹配的行中选择pending_users
值。但我不认为查询是这样做的,我强烈怀疑username
在UID
中是唯一的。 (最外面的GROUP BY中没有包含pending_users
的任何列。)除了匹配pending_users
中的多个行...
该查询已经保证最外层查询上的GROUP BY没有要折叠的行。它看起来就像GROUP BY所带来的那样是一个排序操作。这可以用pending_users
替换。
这也意味着ORDER BY
聚合(在最外层查询的SELECT列表中)不是必需的。例如,这个表达式:
SUM
可以替换为:
TRUNCATE(SUM(sub.Total),3)
并返回等效结果。
在性能方面......拥有合适的索引非常重要。在EXPLAIN中,我们宁愿看不到TRUNCATE(sub.Total,3)
这些内部查询的GROUP BY操作。我们宁愿看Using filesort
。
对于那些内部查询,理想情况下我们会看到这样的索引:
Using index
CUSTID上有一个等式谓词,并且在ActualDate,UID上有一个GROUP BY操作。按此顺序(几乎)保证MySQL将使用索引来执行GROUP BY并避免... ON merchantloss (CUSTID, ActualDate, UID, amount)
操作。包括查询中引用的其他列使其成为覆盖索引,这意味着可以完全从索引中满足查询,而无需访问基础表中的页面。
最后一点注意......如果限制行数,那么在每个单独的内部查询上执行Using filesort
排除(使用反连接模式)可能只是效率更高一点。从is_admin
返回。但我怀疑它不会产生太大的影响,甚至可能会更慢......我会把它留在最外面的查询上,如果只是为了避免多次重复。
答案 1 :(得分:1)
<强>更新强>
好像我以前的答案不够长......
我还将JOIN从内部查询移动到用户表到外部查询,看起来它在每种情况下都是相同的。
但是,我们需要计算BonusAmnt(它似乎总是基于Total
;除了两个之外,它们的计算是相同的。所以,我也移动了BonusAmnt计算也可以使用CASE表达式添加对要执行的计算的检查。
我可能错过了一些东西。
但这是我写这个查询的方式。
我会将它显示为两部分,sub
内联视图的查询与外部查询分开。
SELECT TRUNCATE(sub.Total,3) AS GrpTotal
, sub.ActualDate
, CASE
WHEN sub.Report IN ('Month End Fee Rejects','Direct ACH Debits and Credits') THEN
TRUNCATE(sub.Total,3)
ELSE
TRUNCATE(sub.Total*IFNULL((u.retention_percent/100),1)+IFNULL(u.bonus_amount,0)),3)
END AS GrpBonusAmt
, sub.UID
, sub.CUSTID
, YEAR(sub.ActualDate) AS Year
, pusers.username
FROM (
-- query to produce sub goes here
) sub
LEFT
JOIN `users` u
ON u.uid = sub.UID
LEFT
JOIN `pending_users` pusers
ON pusers.UID = sub.UID
LEFT
JOIN `users` n
ON n.UID = sub.CUSTID
AND n.is_admin
WHERE n.UID IS NULL
ORDER BY sub.ActualDate, sub.UID, sub.Report
这是第二部分。这是一个插入到上面部分中间的查询,作为内联视图。这是为外部查询生成sub
行源的查询:
SELECT 'Global Report' AS Report
, a.UID, a.ActualDate, a.CUSTID
, SUM(a.totalpayment) AS Total
FROM `globalreport` a
WHERE a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT 'Amex Residuals'
, a.UID, a.ActualDate, a.CUSTID
, SUM(a.payment) AS Total
FROM `amexresiduals` a
WHERE a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT 'Compliance Fee'
, a.UID, a.ActualDate, a.CUSTID
, SUM(a.profit) AS Total
FROM `compliancefee` a
WHERE a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT 'Checks On Demand'
, a.UID, a.ActualDate, a.CUSTID
, SUM(a.myprofit) AS Total
FROM `geticheck` a
WHERE a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT 'Gift Cards on Demand'
, a.UID, a.ActualDate, a.CUSTID
, SUM(a.payment) AS Total
FROM `gcod` a
WHERE a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT 'Global Check' AS Report
, a.UID, a.ActualDate, a.CUSTID
, SUM(a.myprofit) AS Total
FROM `globalcheck` a
WHERE a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT 'Bonus True Up'
, a.UID, a.ActualDate, a.CUSTID
, SUM(a.finalpayment) AS Total
FROM `bonustrueup` a
WHERE a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT 'Bonus Take Back - Did Not Activate'
, a.UID, a.ActualDate, a.CUSTID
, SUM(a.amount) AS Total
FROM `bonusadjnosetup` a
WHERE a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT 'Bonus Take Back - Closed Less Than 6 Months'
, a.UID, a.ActualDate, a.CUSTID
, SUM(a.amount) AS Total
FROM `bonusadjclosed6mo` a
WHERE a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT 'Month End Fee Rejects'
, a.UID, a.ActualDate, a.CUSTID
, SUM(a.amount) AS Total
FROM `merchantloss` a
WHERE a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT 'Direct ACH Debits and Credits'
, a.UID, a.ActualDate, a.CUSTID
, SUM(a.amount*-1) AS Total
FROM `dirachdebcred` a
WHERE a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID
UNION ALL
SELECT 'Merchant Adjustments'
, a.UID, a.ActualDate, a.CUSTID
, SUM(a.amount) AS Total
FROM `merchantadj` a
WHERE a.CUSTID = 1020
GROUP BY a.ActualDate, a.UID