【问题标题】:Can we sum data without sub query?我们可以在没有子查询的情况下对数据求和吗?
【发布时间】:2016-01-20 09:08:41
【问题描述】:

我想要相关的合同,其中包含费率及其差额作为保证金

我想在 SQL Server 中进行此求和,而不在子查询中自行加入表。

我的 Schema 设置如下:

合同

ContractId  relatedContractId   Discriminator
-----------------------------------------------------
8041        8040                VendorContract
8040        8041                CustomerContract
8042        NULL                CustomerContract
8043        8044                CustomerContract
8044        8043                VendorContract

费率

RateId  RatePerUnit ContractId
---------------------------------
8052    120.00      8041
8053    123.00      8041
8050    121.00      8040
8051    127.00      8040
8052    120.00      8042  
8053    137.00      8042
8054    102.00      8043
8055    100.00      8044

预期输出是

CContract     VContract    Marging
------------------------------------
8040          8041         5.00 
8042          Null         257.00
8043          8044         2.00

我已经通过下面的查询获得了正确的结果,它使用了子查询,但我需要在不使用子查询的情况下这样做。

SELECT  cts.ContractId,
        cts.RelatedContractId,
        (ISNULL(SUM(r.RatePerUnit),0) - ISNULL(vr.VendorContractRate,0)) AS Margin
FROM    Contracts cts
        LEFT OUTER JOIN Contracts vcts 
            ON cts.RelatedContractId = vcts.ContractId
        LEFT OUTER JOIN Rates r 
            ON cts.ContractId = r.ContractId
        LEFT OUTER JOIN 
        (   SELECT  c.ContractId, 
                    ISNULL(SUM(r.RatePerUnit),0) VendorContractRate 
            FROM    Contracts c
                    LEFT OUTER JOIN Rates r 
                        ON r.ContractId = c.contractId
            WHERE c.Discriminator = 'VendorContract' 
            GROUP BY c.ContractId
        ) vr 
            ON cts.RelatedContractId = vr.ContractId           
WHERE   cts.Discriminator = 'CustomerContract'
GROUP BY cts.ContractId, cts.RelatedContractId, vr.VendorContractRate;

我尝试在不使用子查询的情况下重写此查询,如下所示:

SELECT  vc.ContractId, 
        SUM(r.RatePerUnit), 
        SUM(vr.RatePerUnit) 
FROM    Contracts c
        LEFT OUTER JOIN Contracts vc 
            ON c.RelatedContractId = vc.ContractId
        LEFT OUTER JOIN Rates r 
            ON r.ContractId= c.ContractId
        INNER JOIN Rates vr 
            ON vr.ContractId = vc.ContractId
WHERE   c.Discriminator = 'CustomerContract' 
AND     c.ContractStatus = 0
GROUP BY vc.ContractId;

不幸的是,这是重复我的结果,所以它是不正确的。对于多个费率,它将费率乘以存在的费率数。所以我得到了结果:

CContract     VContract    Marging
------------------------------------
8040          8041         10.00       ---- in this case 496-486(248-243)
8042          Null         514.00      
8043          8044         2.00        

【问题讨论】:

  • 为什么不想使用子查询?
  • 性能问题。请在没有子查询的情况下帮助我

标签: sql sql-server-2008 join


【解决方案1】:

如果没有某种子查询,我看不到一种简单的方法,但也没有理由尝试避免子查询。您已经引用了希望避免子查询的性能原因,但这不是一个正当理由,SQL 是一种声明性语言,即您告诉优化器您想要什么而不是如何获取它,所以仅仅因为您使用了子查询就可以并不意味着你有必要改变计划。

虽然不需要去掉子查询来提高性能,但这并不是说你的查询的性能仍然不能提高。这可以通过删除不必要的连接来完成。以下应该表现更好:

WITH SummedRates AS
(   SELECT  ContractID, RatePerUnit  = SUM(RatePerUnit)
    FROM    Rates
    GROUP BY ContractID
)
SELECT  c.ContractID, 
        c.RelatedContractID,
        Margin = ISNULL(r.RatePerUnit, 0) - ISNULL(r2.RatePerUnit, 0)
FROM    Contracts AS c
        LEFT JOIN SummedRates AS r
            ON r.ContractId = c.ContractId
        LEFT JOIN SummedRates AS r2
            ON r2.ContractId = c.RelatedContractId
WHERE   c.Discriminator = 'CustomerContract'

完整的工作演示

DECLARE @Contracts TABLE (ContractId INT, relatedContractId INT, Discriminator VARCHAR(20));
INSERT @Contracts (ContractId, relatedContractId, Discriminator)
VALUES
    (8041, 8040, 'VendorContract'),
    (8040, 8041, 'CustomerContract'),
    (8042, NULL, 'CustomerContract'),
    (8043, 8044, 'CustomerContract'),
    (8044, 8043, 'VendorContract');

DECLARE @Rates TABLE (RateId INT, RatePerUnit DECIMAL(5, 2), ContractId INT);
INSERT @Rates (RateId, RatePerUnit, ContractId)
VALUES
    (8052, 120.00, 8041),
    (8053, 123.00, 8041),
    (8050, 121.00, 8040),
    (8051, 127.00, 8040),
    (8052, 120.00, 8042),
    (8053, 137.00, 8042),
    (8054, 102.00, 8043),
    (8055, 100.00, 8044);

WITH SummedRates AS
(   SELECT  ContractID, RatePerUnit  = SUM(RatePerUnit)
    FROM    @Rates
    GROUP BY ContractID
)
SELECT  c.ContractID, 
        c.RelatedContractID,
        Margin = ISNULL(r.RatePerUnit, 0) - ISNULL(r2.RatePerUnit, 0)
FROM    @Contracts AS c
        LEFT JOIN SummedRates AS r
            ON r.ContractId = c.ContractId
        LEFT JOIN SummedRates AS r2
            ON r2.ContractId = c.RelatedContractId
WHERE   c.Discriminator = 'CustomerContract';

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-30
    • 2016-04-14
    • 2021-08-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多