【问题标题】:LEFT outer join returns returns duplicate rows even after adding distinctLEFT 外连接返回返回重复的行,即使添加了不同的
【发布时间】:2019-11-07 01:27:36
【问题描述】:

限制价格变化表选择特定项目

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 * 9 = 27。(样本数据不佳,它还需要13以外的其他id。)
  • 在加入表格时,你应该根据日期条件加入,这样它就可以只映射一个价格记录和一个销售。即它应该获取与销售日期相关的 pic 记录。
  • 请在代码问题中给出minimal reproducible example--剪切&粘贴&运行代码;具有期望和实际输出的示例输入(包括逐字错误消息);标签和明确的规范和解释。这包括您可以提供的最少代码,即您显示的代码可以通过您显示的代码扩展为不可以。 (调试基础。) PS你没有说你想要什么。使用足够多的单词、句子和对部分示例的引用来清楚完整地表达你的意思。在给出一个表(基础或查询结果)时,说明其中的一行根据其列值说明了业务情况。
  • use text, not images/links, for text--including tables & ERDs. 转述或引用其他文本。仅将图像用于无法表达为文本或增强文本的内容。无法搜索或剪切和粘贴图像。在图像中包含图例/键和说明。让您的帖子自成一体。
  • 样本数据最好使用DDL + DML。请edit您的问题包括它和您想要的结果。更多详情,read this.

标签: sql tsql join left-join


【解决方案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

【讨论】:

  • 这行得通,但留下了一些产品项目。我想我只需要修复我如何计算开始和结束日期
【解决方案2】:

我不确定这是否是您正在寻找的。但也许你可以跳过加入?

我创建了 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加入表

【讨论】:

  • 请不要回答不清楚的问题,发表评论要求澄清。缺少minimal reproducible example 是关闭的原因——阻止发布答案。这个问题应该被否决和接近投票,而不是回答。如果你没有名声,那就等着吧。不幸的是,有些人还是猜到了。这通常会导致混乱、cmets 和编辑的混乱。
【解决方案3】:

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

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

试试这个。我以模拟有效期的方式更改了日期。

一个模拟场景(下次请给我们这个):

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-approach。思路是一样的。

【讨论】:

    猜你喜欢
    • 2013-04-03
    • 1970-01-01
    • 2018-12-15
    • 2017-09-28
    • 2021-09-12
    • 2016-04-11
    • 1970-01-01
    • 2018-05-23
    • 1970-01-01
    相关资源
    最近更新 更多