我们可以在没有子查询的情

时间:2016-01-20 09:08:42

标签: sql sql-server-2008 join

我想要与费率及其差异作为保证金的相关合同

我想在SQL Server中执行此操作,而无需在子查询中自行加入表。

我的架构设置如下:

合同

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        

1 个答案:

答案 0 :(得分: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';