【问题标题】:Select Records against all months of a given year form one table with records from second table从一个表中选择一个给定年份的所有月份的记录,其中包含第二个表中的记录
【发布时间】:2020-03-12 22:09:00
【问题描述】:

我的数据库中有两个表:

  • MembershipInstallments (ProjectId, AllotmentId, InstallmentId, EntryDate, AmountPaid)
  • Memberships (ProjectId, AllotmentId, ClientId, ClientName, RegistrationNo)

我想选择客户在一年中按月支付的款项。以及当年年初之前支付的金额。我想要的结果集是

假设 2019 年:

ClientName, RegistrationNumbers, PreviouslyPaidAmount (i.e. all amounts paid in 2018), AmountPaidInJuly 2019, AmountPaidInAugust2019. ..... AmountPaidIn Jun2020

其中月份是来自MembershipInstallments 表的EntryDate

我是这样尝试的,但是对于大数据集来说时间太长了:

SELECT 
    ClientName, RegistrationNo,
    (SELECT ISNULL(SUM(AmountPaid),0)
     FROM MembershipInstallments 
     WHERE ProjectId = Membership.ProjectId 
       AND AllotmentId = Membership.AllotmentId 
       AND EntryDate < @fromDate) ASPreviouslyPaid,
    (SELECT ISNULL(SUM(AmountPaid), 0)
     FROM MembershipInstallments 
     WHERE ProjectId = Membership.ProjectId 
       AND AllotmentId = Memberhsip.AllotmentId 
       AND (MONTH(EntryDate) = 7) AND (YEAR(EntryDate) = YEAR(@fromDate)) AS JulyPayment
FROM 
    Memberships 
WHERE 
    ProjectId = @projectId

我们将不胜感激。

【问题讨论】:

    标签: sql database tsql sql-server-2014


    【解决方案1】:

    标量子查询往往被严重优化。在您的情况下(相同的连接条件),您可以简单地利用 条件聚合

    SELECT ms.ClientName,ms.RegistrationNo,
       Sum(CASE WHEN mi.EntryDate<@fromDate THEN mi.AmountPaid ELSE 0 end) AS PreviouslyPaid,
       Sum(CASE WHEN (Month(mi.EntryDate) = 7) AND (Year(mi.EntryDate) = Year(@fromDate)) THEN mi.AmountPaid ELSE 0)
    FROM Memberships AS ms
    LEFT JOIN MembershipInstallments AS mi -- don't know if Outer join is really needed
      ON mi.ProjectId=ms.ProjectId
      AND mi.AllotmentId=ms.AllotmentId
    WHERE ms.ProjectId=@projectId
    

    您也许可以在加入之前进行聚合,即在 CTE 中以进一步提高性能:

    WITH cte AS
     (
       SELECT  
          ProjectId, AllotmentId,
          Sum(CASE WHEN EntryDate<@fromDate THEN AmountPaid ELSE 0 END) AS PreviouslyPaid,
          Sum(CASE WHEN (Month(EntryDate) = 7) AND (Year(EntryDate) = Year(@fromDate)) THEN AmountPaid ELSE 0 END) AS JulyPayment
       FROM MembershipInstallments 
       WHERE ProjectId=@projectId
       GROUP BY ProjectId, AllotmentId
     )
    SELECT ms.ClientName, ms.RegistrationNo, cte.PreviouslyPaid, cte.JulyPayment
    FROM Memberships AS ms
    JOIN cte
      ON cte.ProjectId=ms.ProjectId
      AND cte.AllotmentId=ms.AllotmentId
    WHERE ms.ProjectId=@projectId
    

    在这两种情况下,仔细检查结果是否正确(取决于这些表之间的实际关系)

    【讨论】:

    • 我是否需要写 12 次 CASE 语句才能获得所有月份的总和。有 c# foreach 替代方案吗?
    • 不了解 C#,但它只是 Cut&Paste&Modify,通常执行效率很高。事实上,它是一种 pivot 查询,当您在 SQL Server 中 PIVOT 时,它实际上是使用 CASE 重写的
    • 它在性能上提高了很多。你能给我一个关于在这个 cas 中使用 CTE 的提示吗
    • 通过使用 CTE Query 执行时间从 900 毫秒下降到 130 毫秒
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 2018-09-12
    • 2012-07-16
    相关资源
    最近更新 更多