这是一个巨大的怪物,它进入SP所以变量可用:
SELECT OwnerName, SUM(AmountPaid) AS Paid, SUM(AmountOwedComplete) AS Owed, SUM(AmountOwedThisMonth) AS OwedMonth,
SUM(PaidForPast) AS PaidPast, SUM(PaidForPresent) AS PaidPresent, SUM((AmountPaid - PaidForPast - PaidForPresent)) AS PaidFuture, [Description] FROM (
SELECT OwnerName, AmountPaid, AmountOwedComplete, AmountOwedThisMonth, PaidForPast, [Description],
(SELECT CASE WHEN (AmountPaid - PaidForPast) < ABS(AmountOwedThisMonth) THEN AmountPaid - PaidForPast
ELSE ABS(AmountOwedThisMonth) END) AS PaidForPresent
FROM (
SELECT OwnerName, AmountPaid, AmountOwedTotal - AmountPaid AS AmountOwedComplete,
AmountOwedThisMonth,
(SELECT CASE WHEN (AmountPaid < ABS((AmountOwedTotal - AmountPaid)) + AmountOwedThisMonth)
THEN AmountPaid ELSE ABS((AmountOwedTotal - AmountPaid)) + AmountOwedThisMonth END) AS PaidForPast,
Description, TransactionDate
FROM (
SELECT DISTINCT t.TenantName, p.PropertyName, ISNULL(p.OwnerName, 'Uknown') AS OwnerName, (
SELECT SUM(Amount) FROM tblTransaction WHERE
Amount > 0 AND TransactionDate >= @StartDate AND TransactionDate <= @EndDate
AND TenantID = t.ID AND TransactionCode = trans.TransactionCode
) AS AmountPaid, (
SELECT SUM(Amount) FROM tblTransaction WHERE
tblTransaction.TransactionCode = trans.TransactionCode AND tblTransaction.TenantID = t.ID
) AS AmountOwedTotal, (
SELECT SUM(Amount) FROM tblTransaction WHERE tblTransaction.TransactionCode = trans.TransactionCode AND tblTransaction.TenantID = t.ID
AND Amount < 0 AND TransactionDate >= @StartDate AND TransactionDate <= @EndDate
) AS AmountOwedThisMonth, code.Description, trans.TransactionDate FROM tblTransaction trans
LEFT JOIN tblTenantTransCode code ON code.ID = trans.TransactionCode
LEFT JOIN tblTenant t ON t.ID = trans.TenantID
LEFT JOIN tblProperty p ON t.PropertyID = p.ID
WHERE trans.TransactionDate >= @StartDate AND trans.TransactionDate <= @EndDate AND trans.Amount > 0
) q
) q2
)q3
GROUP BY OwnerName, Description
这就是它的作用。它会访问所有租户并获得他们本月支付的费用,以及他们所欠的一切。然后计算以前的费用,本月和未来费用的支付额。然后根据收费说明和业主名称对它们进行汇总。
看起来很糟糕,我想知道是否有一些我可以使用的捷径。
答案 0 :(得分:7)
这里的杀手是PaidForPast
(和派生PaidForPresent
)计算,似乎是在试图表明租客是否已经支付了他的余额(如果我理解正确的话 - 这并不容易)。这个计算必须按照每一笔支付交易执行,并依赖于从整个租户历史派生的另一个聚合,这保证了O(n ^ 2)操作。
对你的数据库来说,这是一个残酷,残酷的事情,虽然我可能会让你的查询看起来更漂亮,但只要你被迫生产,你就无法从性能问题中解脱出来。基于此查询所暗示的特定可用数据集的信息。
您所拥有的不是重构/优化问题,而是严重的设计问题。实际上你有两个问题。较小的问题是您正在将业务逻辑编码到数据层中;更大的问题是系统的设计并不是为了跟踪它实际需要的所有信息。
您在此查询中生成的信息基本上所有应保存在某种A / R历史记录表中,该记录表记录每个事务的这些聚合/统计信息。该表可由应用程序本身或tblTransaction
表上的触发器维护。
可能不是你正在寻找的答案,但当我意识到不可能删除PaidForXYZ
嵌套时,我实际上已经进行了重构的一半。
实际上,生成这些结果的最快方法可能是使用游标,因为这样您就可以使用部分聚合并将其转换为O(n)操作。但是你真的希望游标在没有过滤器的整个事务表上运行吗?
如果你真的不关心性能而只想清理它以便更容易阅读/维护,那么这就是我能想出的最好使用一些CTE:
;WITH Transactions_CTE AS
(
SELECT
TenantID,
TransactionCode,
Amount,
CASE
WHEN Amount > 0 AND TransactionDate BETWEEN @BeginDate AND @EndDate
THEN Amount
ELSE 0
END AS AmountPaid,
CASE
WHEN Amount < 0 AND TransactionDate BETWEEN @BeginDate AND @EndDate
THEN Amount
ELSE 0
END AS AmountOwed,
FROM tblTransaction
),
Summary_CTE AS
(
SELECT
t.PropertyID,
tr.TransactionCode,
SUM(tr.Amount) AS CumulativeBalance,
SUM(tr.AmountPaid) AS CurrentPaid,
SUM(tr.AmountOwed) AS CurrentOwed
FROM Transactions_CTE tr
INNER JOIN tblTenant t ON tr.TenantID = t.ID
GROUP BY t.PropertyID, tr.TransactionCode
),
Past_CTE AS
(
SELECT
PropertyID, TransactionCode,
CumulativeBalance, CurrentPaid, CurrentOwed,
CASE
WHEN CurrentPaid <
ABS(CumulativeBalance - CurrentPaid) + CurrentOwed
THEN CurrentPaid
ELSE ABS(CumulativeBalance - CurrentPaid) + CurrentOwed
END AS PaidForPast
FROM Summary_CTE
),
Present_CTE AS
(
SELECT
PropertyID, TransactionCode,
CumulativeBalance, CurrentPaid, CurrentOwed,
PaidForPast,
CASE
WHEN (CurrentPaid - PaidForPast) < ABS(CurrentOwed)
THEN CurrentPaid - PaidForPast
ELSE ABS(CurrentOwed)
END AS PaidForPresent
FROM Past_CTE
)
SELECT
ISNULL(p.OwnerName, 'UNKNOWN') AS OwnerName,
c.[Description],
CumulativeBalance, CurrentPaid, CurrentOwed,
CumulativeBalance - CurrentPaid AS CumulativeOwed,
PaidForPast, PaidForPresent,
CurrentPaid - PaidForPast - PaidForPresent AS PaidForFuture,
[Description]
FROM Present_CTE s
LEFT JOIN tblProperty p ON p.ID = s.PropertyID
LEFT JOIN tblTenantTransCode c ON c.ID = s.TransactionCode
你可以通过完整地写出整个PaidForXYZ
表达式来消除最后两个CTE,而不是在另一个上面构建一个,但IMO最终会使它更少可读,我很确定优化器会解决它并将其全部映射到表达式而不是子查询。
另外,我必须说所有这些ABS
块让我怀疑。我不知道这里发生了什么的细节,但感觉就像那些被用来代替否定运算符,可能是一个应该在上游进一步使用(即将借方或贷方转换为负数)和这可能会导致一些微妙的错误。
答案 1 :(得分:1)
首先,学习格式化SQL,以便你(和我)可以阅读它:
SELECT OwnerName,
SUM(AmountPaid) AS Paid,
SUM(AmountOwedComplete) AS Owed,
SUM(AmountOwedThisMonth) AS OwedMonth,
SUM(PaidForPast) AS PaidPast,
SUM(PaidForPresent) AS PaidPresent,
SUM((AmountPaid - PaidForPast - PaidForPresent)) AS PaidFuture,
[Description]
FROM (SELECT OwnerName,
AmountPaid,
AmountOwedComplete,
AmountOwedThisMonth,
PaidForPast,
[Description],
(SELECT CASE WHEN (AmountPaid - PaidForPast) < ABS(AmountOwedThisMonth)
THEN AmountPaid - PaidForPast
ELSE ABS(AmountOwedThisMonth) END) AS PaidForPresent
FROM (SELECT OwnerName,
AmountPaid,
AmountOwedTotal - AmountPaid AS AmountOwedComplete,
AmountOwedThisMonth,
(SELECT CASE WHEN (AmountPaid < ABS((AmountOwedTotal - AmountPaid)) + AmountOwedThisMonth)
THEN AmountPaid
ELSE ABS((AmountOwedTotal - AmountPaid)) + AmountOwedThisMonth END) AS PaidForPast,
Description,
TransactionDate
FROM (SELECT DISTINCT
t.TenantName,
p.PropertyName,
ISNULL(p.OwnerName, 'Uknown') AS OwnerName,
(SELECT SUM(Amount)
FROM tblTransaction
WHERE Amount > 0
AND TransactionDate >= @StartDate
AND TransactionDate <= @EndDate
AND TenantID = t.ID
AND TransactionCode = trans.TransactionCode) AS AmountPaid,
(SELECT SUM(Amount)
FROM tblTransaction
WHERE tblTransaction.TransactionCode = trans.TransactionCode
AND tblTransaction.TenantID = t.ID) AS AmountOwedTotal,
(SELECT SUM(Amount)
FROM tblTransaction
WHERE tblTransaction.TransactionCode = trans.TransactionCode
AND tblTransaction.TenantID = t.ID
AND Amount < 0
AND TransactionDate >= @StartDate
AND TransactionDate <= @EndDate) AS AmountOwedThisMonth,
code.Description,
trans.TransactionDate
FROM tblTransaction trans
LEFT JOIN tblTenantTransCode code ON code.ID = trans.TransactionCode
LEFT JOIN tblTenant t ON t.ID = trans.TenantID
LEFT JOIN tblProperty p ON t.PropertyID = p.ID
WHERE trans.TransactionDate >= @StartDate
AND trans.TransactionDate <= @EndDate
AND trans.Amount > 0) q
) q2
) q3
GROUP BY OwnerName, Description;
其次,确保您拥有正确的索引 - 您需要能够读取SQL才能执行此操作。
答案 2 :(得分:0)
你对架构有什么发言权吗?就目前而言,看起来您正试图从业务数据存储生成报告,该报告本质上是事务性的。在许多高容量方案中,创建一个称为决策支持数据库的规范较小的模式并不罕见,您可以在特定的时间间隔内复制/汇总事务数据。然后,您可以针对DSS编写非常简单的查询,同时您的高度标准化的ODS继续发挥作用。