LEFT外部联接返回即使添加了不同的行也返回重复的行

时间:2019-06-25 08:47:44

标签: sql tsql join left-join

限制价格变动表以选择特定商品

select * from price
where item = '13'

上面查询的结果

item       Date_Changed     New     Old          START_DATE         end_DATE
13  01/11/2018 00:00    5.61    4.88    01/11/2018 00:00    30/11/2018 00:00
13  30/11/2018 00:00    2.84    5.61    01/11/2018 00:00    17/12/2018 00:00
13  17/12/2018 00:00    2.39    2.84    30/11/2018 00:00    17/12/2018 00:00

销售表

Date    Item    Qty Amount
05/07/2018 00:00    13  3   14.64
05/07/2018 00:00    13  3   14.64
04/07/2018 00:00    13  3   14.64
02/07/2018 00:00    13  1   4.88
02/07/2018 00:00    13  6   29.28
06/07/2018 00:00    13  7   34.16
03/07/2018 00:00    13  4   19.52
12/07/2018 00:00    13  2   9.76
10/08/2018 00:00    13  1   4.88
  

示例代码

SELECT distinct a.[Inv]
  , (CASE 
        WHEN a.Date <=  b.START_DATE  THEN  (b.Old * a.Qty)
        WHEN a.Date between  b.START_DATE  and b.dt_end_DATE  THEN  (b.New * a.Qty)
        ELSE 0
   END) as calc_amount
   ,(a.[amount] - (CASE 
        WHEN a.Date <=  b.START_DATE  THEN  (b.Old * a.Qty)
        WHEN a.Date between  b.START_DATE  and b.end_DATE  THEN  (b.New * a.Qty)
        ELSE 0
   END)) as variance
[sales] a 
left outer join price b
on a.[Item] = b.item 
where b.item = '13' 

然后,脚本返回27行而不是9行。有人可以协助我改善我的脚本以使其更准确吗

3 个答案:

答案 0 :(得分:1)

使用outer apply。我假设您想要的是price的最新开始日期,所以看起来像这样:

select s.*,
       (case when s.date <= p.start_date
             then p.Old * s.Qty
             else p.New * s.Qty
        end) as calc_amount
from sales s outer apply
     (select top (1) p.*
      from prices p
      where p.item = s.item and
            p.start_date <= s.date
      order by p.date desc
     ) p

答案 1 :(得分:0)

我不确定这是否是您想要的。但是也许您可以跳过加入?

我用可能需要的列创建了2个样本数据。


DECLARE @price table (item varchar(2),date_start date, new_price numeric(9,2))

Insert into @price (item , date_start,new_price) 
values
    ( '13', '20190101', '1.00'),
    ( '13', '20190102', '1.01'),
    ( '13', '20190103', '1.02')


DECLARE @sales table (item varchar(2),date_sales date,qty int)

Insert into @sales (item , date_sales,qty) 
values
    ( '13', '20190101', '5'),
    ( '13', '20190101', '2'),
    ( '13', '20190102', '5'),
    ( '13', '20190102', '2'),
    ( '13', '20190103', '5'),
    ( '13', '20190103', '2')


declare @item as varchar(2) = '13'
SELECT (select top (1) new_price from @price b where a.date_sales>=b.date_start and b.item = @item order by b.date_start desc ) * a.qty as 'new_price* qty'
from @sales a 
where a.item = @item

我尚未在具有庞大数据集的表中对此进行测试,因此我也无法保证此查询的速度。我相信最好有某种其他ID来加入表格

答案 2 :(得分:0)

您的问题尚不清楚...您添加了示例数据,但我怀疑这是正确的...

您的价格表对错误数据开放。最好只存储价格和validFrom日期。在这种情况下,您可以轻松地给定日期选择价格。您的格式容易出现重叠的时期,因此没有充分的理由再次存储以前的价格。这就是为什么我忽略所有不应使用的字段的原因...

尝试一下。我已经更改了日期以模拟有效期。

一个模拟场景(请下次再试):

CREATE TABLE priceMock(item INT, Date_Changed DATE, New DECIMAL(10,4), Old DECIMAL(10,4), [START_DATE] DATE, end_DATE DATE);

SET DATEFORMAT dmy;
INSERT INTO priceMock VALUES
 (13,'01/11/2018 00:00',5.61,4.88,'01/07/2018 00:00','06/07/2018 00:00')
,(13,'30/11/2018 00:00',2.84,5.61,'07/07/2018 00:00','10/07/2018 00:00')
,(13,'17/12/2018 00:00',2.39,2.84,'11/07/2018 00:00','15/08/2018 00:00');
GO

CREATE TABLE salesMock ([Date] DATE, Item INT, Qty INT, Amount DECIMAL(10,4));

SET DATEFORMAT dmy;
INSERT INTO salesMock VALUES
 ('05/07/2018 00:00',13,3,14.64)
,('05/07/2018 00:00',13,3,14.64)
,('04/07/2018 00:00',13,3,14.64)
,('02/07/2018 00:00',13,1,4.88 )
,('02/07/2018 00:00',13,6,29.28)
,('06/07/2018 00:00',13,7,34.16)
,('03/07/2018 00:00',13,4,19.52)
,('10/07/2018 00:00',13,2,9.76 )
,('10/08/2018 00:00',13,1,4.88 );
GO

我将添加一个 inline-table-valued-function 来获得一条单行。

CREATE FUNCTION dbo.GetPriceForItemOnDate(@item INT,@ValidOn DATE)
RETURNS TABLE
AS
RETURN
    SELECT TOP 1 *
    FROM priceMock
    WHERE item=@item
    AND [START_DATE] <= @ValidOn
    ORDER BY [START_DATE] DESC
GO

-此查询将结合您的销售数据和给定日期的有效价格

SELECT s.[Date]
      ,s.Item
      ,s.Qty
      ,p.New AS CurrentPrice
      ,s.Qty * p.New AS ComputedAmount
FROM salesMock s
OUTER APPLY dbo.GetPriceForItemOnDate(s.item,s.[Date]) p 
GO

-清理(仔细处理真实数据)

DROP FUNCTION dbo.GetPriceForItemOnDate;
DROP TABLE priceMock;
DROP TABLE salesMock;

简而言之:

该功能将首先过滤给定项目的价格线。第二个过滤器将剪切列表,并仅返回给定日期和给定日期之前的价格。当我们按日期降序排序时,我们将获得最新的价格。通过使用TOP 1,我们只返回我们想要的一行。

一般性说明:我在这里使用validFrom方法。但是您可以反过来使用validTo方法。想法是一样的。