【问题标题】:Need to make a query more efficient需要使查询更有效
【发布时间】:2013-07-02 13:24:14
【问题描述】:

我有一个查询需要提高效率。

我将其分解为几个部分以查看效率底线在哪里,我目前有一些嵌套选择语句,这些是性能问题吗?

这是其中之一的示例:

SELECT AgreementID,
       DueDate,
       UpdatedAmountDue AS AmountDue,
       COALESCE((SELECT SUM(UpdatedAmountDue)
                 FROM RepaymentBreakdown AS B
                 WHERE CONVERT(datetime, CONVERT(varchar, DueDate, 103), 103) <= 
                       CONVERT(datetime, CONVERT(varchar, R.DueDate, 103), 103)
                 AND B.AgreementID = R.AgreementID),0) AS DueTD,
       RN=ROW_NUMBER() OVER (Partition BY R.AgreementID ORDER BY DueDate)
FROM RepaymentBreakdown AS R

有没有更干净高效的方式获取DueTD的数据?

基本上,对于还款计划结果的每一行,我想得到:

AgreementID,
DueDate,
AmountDue,
AmountDueToDate (DueTD)
RowNumber.

我查询的表结构如下:

AgreementID (int), 
DueDate (datetime),
AmountDue (decimal(9,2)),
UpdatedAmountDue (decimal(9,2))*

*UpdatedAmountDue 总是被引用,因为它是移动的图形,AmountDue 总是固定的,作为一个参考值。

【问题讨论】:

  • 您似乎可以在stackoverflow.com/questions/860966/… 找到有用的提示,您的DueDate 专栏中有哪些日期? (AgreementID , DueDate) 是唯一的吗?
  • 嗨 Roman,谢谢你的链接,我会看看那个......我的数据,DueDate 包含格式化的日期:> '2012-08-24 00:00:00.000' , 协议 ID 和到期日期始终是唯一的,因为协议每天只能支付 1 次。
  • 最后一个问题 - 您的 DueDate 是连续的还是可能有间隔?
  • 日期会有间隔,从 1 周到 1 个月不等。

标签: performance sql-server-2008 query-performance


【解决方案1】:

所以,我认为你可以通过删除转换来提高性能,如下所示:

select
    AgreementID,
    DueDate,
    UpdatedAmountDue as AmountDue,
    (
        select sum(B.UpdatedAmountDue)
        from RepaymentBreakdown as B
        where B.DueDate <= R.DueDate and B.AgreementID = R.AgreementID
    ) as UpdatedAmountDue 
from RepaymentBreakdown AS R

我知道在 SQL Server 2008 中计算运行总数的最快方法是使用递归 CTE,请在此处查看我的答案 Calculate a Running Total in SqlServer。在您的情况下,查询将是这样的:

create table #t (....., primary key (AgreementID, ord))

insert into #t (AgreementID, DueDate, UpdatedAmountDue, ord)
select AgreementID, DueDate, UpdatedAmountDue, row_number() over (partition by AgreementID, DueDate order by DueDate asc)

;with 
CTE_RunningTotal
as
(
    select T.ord, T.AgreementID, T.DueDate, T.UpdatedAmountDue as T.AmountDue, T.UpdatedAmountDue
    from #t as T
    where T.ord = 1
    union all
    select T.ord, T.AgreementID, T.DueDate, T.UpdatedAmountDue as T.AmountDue, T.UpdatedAmountDue + C.UpdatedAmountDue as UpdatedAmountDue
    from CTE_RunningTotal as C
        inner join #t as T on T.ord = C.ord + 1 and T.AgreementID = C.AgreementID
)
select AgreementID, DueDate, AmountDue, UpdatedAmountDue
from CTE_RunningTotal as C
option (maxrecursion 0)

【讨论】:

    【解决方案2】:

    您将日期时间转换为日期有几个问题。

    首先,根据您的服务器语言设置,不能保证总是产生正确的结果。如果您需要对日期时间值进行字符串操作,请始终使用 CONVERT(,,126)。

    但更重要的是,它可以防止索引的使用。而是使用CAST(DueDate AS DATE),因为优化器认为该转换是索引安全的。

    之后,您可能希望在 AgreementId、DueDate 和 INCLUDE UpdatedAmountDue 上添加索引,或者更好地使其成为集群。

    假设 UpdatedAmountDue 不能为 NULL,你也可以去掉 COALESCE,因为总和总是包括当前行。

    【讨论】:

    • OK,那么对于嵌套的Select,会不会造成性能问题?
    • 很可能不是。它是一个单值相关子查询,SQL Server 应该这样对待它。您可以改用CROSS APPLY 进行更改,但很可能不需要。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-22
    • 2019-10-27
    • 2012-06-29
    • 1970-01-01
    • 1970-01-01
    • 2020-04-08
    相关资源
    最近更新 更多