【问题标题】:Replacing a loop query替换循环查询
【发布时间】:2016-12-06 02:25:17
【问题描述】:

我设置了以下表格:

   CREATE TABLE app_detail
(
CustID1 int,
CustID2 int,
AppDate datetime
)

CREATE TABLE inv_detail
(
CustID1 int,
CustID2 int,
PostDate datetime,
ClearDate datetime,
Amt float
)

INSERT INTO app_detail
VALUES(583,246,'2013-04-30 00:00:00.000')
INSERT INTO app_detail
VALUES(583,246,'2015-06-17 00:00:00.000')

INSERT INTO inv_detail
VALUES(583,246,'2013-04-05 00:00:00.000',NULL,17.56)
INSERT INTO inv_detail
VALUES(583,246,'2013-04-05 00:00:00.000','2013-12-31 00:00:00.000',667.97)
INSERT INTO inv_detail
VALUES(583,246,'2013-04-05 00:00:00.000','2014-10-05 00:00:00.000',3.96)
INSERT INTO inv_detail
VALUES(583,246,'2013-04-05 00:00:00.000',NULL,48.40)
INSERT INTO inv_detail
VALUES(583,246,'2013-01-08 00:00:00.000','2013-12-31 00:00:00.000',332.03)
INSERT INTO inv_detail
VALUES(583,246,'2013-01-08 00:00:00.000','2013-12-31 00:00:00.000',63.10)


INSERT INTO inv_detail
VALUES(583,246,'2013-07-09 00:00:00.000',NULL,1062.29)
INSERT INTO inv_detail
VALUES(583,246,'2013-04-05 00:00:00.000',NULL,17.56)
INSERT INTO inv_detail
VALUES(583,246,'2013-04-05 00:00:00.000',NULL,48.40)

select * from app_detail
select * from inv_detail

我想得到以下输出:

SELECT
 '583' AS CustID1
,'246' AS CustID2
,'2013-04-30 00:00:00.000' AS AppDate
,'1133.02' AS TotalAmount

UNION
SELECT
 '583'
,'246'
,'2015-06-17 00:00:00.000'
,'1128.25'

CustID1 CustID2 AppDate    TotalAmount
583      246    2013-04-30  1133.02
583      246    2015-06-17  1128.25

第一个表包含有关在特定日期提出申请的客户的数据。 第二个表包含这些客户的发票详细信息,发票发送时间和付款时间(清算时间)。 我想知道客户在申​​请日期未结清的发票金额。 如果发票已支付,则 ClearedDate 列中将显示一个日期。如果发票从未付款,则为 NULL。

我想到的唯一方法是通过一个循环,我一次只将一个 AppDate 传递给WHERE 子句。但我希望我可以取消它。

任何帮助将不胜感激。

【问题讨论】:

  • 如何计算总量?我跟不上。对不起。

标签: sql sql-server loops sql-server-2005


【解决方案1】:

从 Blorgbeard 的回答中借来展示如何在没有子查询的情况下完成此操作。

DECLARE @app_detail TABLE (CustID1 int,CustID2 int,AppDate datetime)
DECLARE @inv_detail TABLE(CustID1 int,CustID2 int,PostDate datetime,ClearDate datetime,Amt float)

INSERT INTO @app_detail VALUES(583,246,'2013-04-30 00:00:00.000')
INSERT INTO @app_detail VALUES(583,246,'2015-06-17 00:00:00.000')

INSERT INTO @inv_detail VALUES(583,246,'2013-04-05 00:00:00.000',NULL,17.56)
INSERT INTO @inv_detail VALUES(583,246,'2013-04-05 00:00:00.000','2013-12-31 00:00:00.000',667.97)
INSERT INTO @inv_detail VALUES(583,246,'2013-04-05 00:00:00.000','2014-10-05 00:00:00.000',3.96)
INSERT INTO @inv_detail VALUES(583,246,'2013-04-05 00:00:00.000',NULL,48.40)
INSERT INTO @inv_detail VALUES(583,246,'2013-01-08 00:00:00.000','2013-12-31 00:00:00.000',332.03)
INSERT INTO @inv_detail VALUES(583,246,'2013-01-08 00:00:00.000','2013-12-31 00:00:00.000',63.10)
INSERT INTO @inv_detail VALUES(583,246,'2013-07-09 00:00:00.000',NULL,1062.29)
INSERT INTO @inv_detail VALUES(583,246,'2013-04-05 00:00:00.000',NULL,17.56)
INSERT INTO @inv_detail VALUES(583,246,'2013-04-05 00:00:00.000',NULL,48.40)

select a.*, sum(Amt) AS [Total]
from @app_detail a
LEFT JOIN @inv_detail i ON i.CustID1=a.CustID1
    AND i.CustID2=a.CustID2
    AND i.PostDate <= a.AppDate
    AND (i.ClearDate is null or i.ClearDate > a.AppDate)
GROUP BY a.CustID1,a.CustID2,a.AppDate

【讨论】:

  • 这看起来不错,而且更简单 - 但 OP 可能希望根据我对他的真实数据的回答来测试它的性能。我对最小测试数据的测试表明这实际上更慢 - 它执行嵌套循环连接和排序,而 outer apply 仅执行嵌套循环连接。
  • @Blorgbeard 如果我要为自己重写它,我会使用联合并摆脱 OR。我一直觉得这样更有效率。在基准测试之后并且只有当 OR 实现当然不够快时:)
【解决方案2】:

您可以使用outer apply 而不是手动循环来做到这一点:

select a.*, due.Total
from app_detail a
outer apply (
    select sum(Amt) [Total]
    from inv_detail i
    where 
        i.CustID1=a.CustID1 and i.CustID2=a.CustID2 and 
        i.PostDate <= a.AppDate and (i.ClearDate is null or i.ClearDate > a.AppDate)
) due

在我从您的测试数据中删除两个重复的行(17.5648.40 都有两个条目)之后,它为我返回了正确的结果。

这不是一种特别有效的方法——它会导致app_detail 中的每一行都进行inv_detail 的表扫描(或者可能是索引扫描,如果您有适当的索引)。但是,我认为在这种情况下没有办法解决这个问题 - 这不是一个简单的聚合,因为 inv_detail 中的一行可能会参与到 app_detail 的许多行的计算中。

【讨论】:

    猜你喜欢
    • 2021-10-21
    • 2013-11-11
    • 1970-01-01
    • 1970-01-01
    • 2014-09-30
    • 1970-01-01
    • 2019-10-23
    • 2022-01-14
    • 2011-09-22
    相关资源
    最近更新 更多