【问题标题】:Sqlserver SQL join on date(no validto fields)Sqlserver SQL 加入日期(无有效字段)
【发布时间】:2021-10-18 18:01:38
【问题描述】:

priceQuote 表,给出一个日期范围内的类别和价格,但是有效的日期总是一个非常大的日期。

另一个表是Item表,它给出了itemID和它的类别以及日期

想法是根据类别和日期将这两个表连接起来,例如,ITEMID 0503848220,类别 1117,日期 2020-07-02 应该返回 $730(金额字段)。

下面是我的尝试,当然它并没有像我想要的那样工作

SELECT
Item.ItemID
,Item.Date
,ValidFROM
,ValidTo
,priceQuote.AMOUNT
From
Item
JOIN
priceQuote
on
Item.category=priceQuote.Category
and
Item.Date between ValidFROM and ValidTo

我想知道执行此操作的最佳方法是什么(考虑到这两个表非常大,并且在加入日期范围时可能会出现性能问题)。如果没有在 SQL 中进行联接的好方法,我愿意接受其他解决方案,例如将这两个表加载到 C# 应用程序中并通过 C# 进行计算。

感谢您的帮助。

数据示例(不知道如何在此处附加 Excel 文件):

+----------+-----------+------------+--------+
| Category | ValidFROM |  ValidTo   | AMOUNT |
+----------+-----------+------------+--------+
|     1113 | 4/1/2020  | 12/31/9999 |    665 |
|     1113 | 7/1/2020  | 12/31/9999 |    925 |
|     1113 | 5/10/2021 | 12/31/9999 |    491 |
|     1117 | 4/1/2020  | 12/31/9999 |    495 |
|     1117 | 7/1/2020  | 12/31/9999 |    730 |
|     1117 | 1/1/2021  | 12/31/9999 |    556 |
|     1117 | 5/10/2021 | 12/31/9999 |    555 |
+----------+-----------+------------+--------+

物品表:

+-----------+----------+-----------+
|  ItemID   | category |   Date    |
+-----------+----------+-----------+
| 503848220 |     1117 | 7/2/2020  |
| 503848221 |     1117 | 7/2/2020  |
| 503848225 |     1117 | 7/3/2020  |
| 503848227 |     1117 | 7/6/2020  |
| 503848228 |     1117 | 7/6/2020  |
| 503848266 |     1113 | 6/26/2020 |
+-----------+----------+-----------+

【问题讨论】:

  • 不应该 ValidTo 成为下一个 ValidFrom - 1 天吗?这些数据尚无定论。
  • 这能回答你的问题吗? Get top 1 row of each group您想要一个行号解决方案或APPLY
  • 第一个表中的 ValidTo 似乎毫无用处。您是否可以选择将此列更新为有用的值(即下一个 ValidFrom 的前一天)
  • @Nick.McDermaid 谢谢。我同意。这是完全没用的。我无法修改该表。
  • @Charlieface 谢谢。但我在这里的问题并不一定意味着 ITEM 使用 pricequote 表上的最新日期。 ITEM 可以是任何日期。

标签: sql sql-server join


【解决方案1】:

正如 cmets 中所说,如果 ValidTo 列包含的日期等于下一个 ValidFROM 日期减去 1 天,它就会有意义。

幸运的是,在 Sql Server 查询中使用window functions 即时生成所需的列相对简单。在这种情况下,LEAD

我使用略有不同的数据来展示它是如何工作的。我将无用的ValidFROM 列全部删除,以证明它没有被使用。

DECLARE @PriceQuote table (
    Category int,
    ValidFrom date,
    Amount int
);

DECLARE @Item table (
    ItemID char(10) primary key,
    Category int,
    Date date
);

INSERT INTO @Item (ItemID, Category, Date) VALUES
    ('503848266', 1113, '6/26/2020'),

    ('503848220', 1117, '3/01/2020'),
    ('503848221', 1117, '4/01/2020'),
    ('503848225', 1117, '7/01/2020'),
    ('503848227', 1117, '8/01/2020'),
    ('503848228', 1117, '4/10/2021'),
    ('503848229', 1117, '5/10/2021');


INSERT INTO @PriceQuote (Category, ValidFrom, Amount) VALUES
    (1113, '4/01/2020', 665 ),
    (1113, '7/01/2020', 925 ),
    (1113, '5/10/2021', 491 ),

    (1117, '4/01/2020', 495 ),
    (1117, '7/01/2020', 730 ),
    (1117, '1/01/2021', 556 ),
    (1117, '5/10/2021', 555 );

第一步,为了澄清这个过程,对于每个PriceQuote,我们通过使用LEAD得到下一个ValidFrom日期:

SELECT Category,
    ValidFrom,
    LEAD(ValidFrom, 1) -- or just LEAD(ValidFrom)
        OVER (PARTITION BY (Category) ORDER BY ValidFrom) As NextValidFrom,
    Amount
FROM @PriceQuote

结果:

Category ValidFrom NextValidFrom Amount
1113 2020-04-01 2020-07-01 665
1113 2020-07-01 2021-05-10 925
1113 2021-05-10 NULL 491
1117 2020-04-01 2020-07-01 495
1117 2020-07-01 2021-01-01 730
1117 2021-01-01 2021-05-10 556
1117 2021-05-10 NULL 555

因此,在一个类别 (PARTITION BY) 中,按 ValidFrom 排序,下一个 ValidFrom 被视为 NextValidFrom(如果有)。

NextValidFrom,加上一天并设置为较大的日期值,当null 时,可用于子查询中,您可以加入以查找报价金额:

SELECT item.ItemID, item.Category, quote.ValidFrom, item.Date, quote.ValidTo, quote.Amount
FROM @Item item
INNER JOIN
(
    SELECT Category,
        ValidFrom, 
        Amount,
        COALESCE(DATEADD(d, -1, LEAD(ValidFrom, 1) 
            OVER (PARTITION BY (Category) 
                ORDER BY ValidFrom)), '12/31/9999') [ValidTo]
    FROM @PriceQuote
) AS quote
ON item.Category = quote.Category
AND item.Date BETWEEN quote.ValidFrom AND quote.ValidTo

返回您所追求的数据:

ItemID Category ValidFrom Date ValidTo Amount
503848266 1113 2020-04-01 2020-06-26 2020-06-30 665
503848221 1117 2020-04-01 2020-04-01 2020-06-30 495
503848225 1117 2020-07-01 2020-07-01 2020-12-31 730
503848227 1117 2020-07-01 2020-08-01 2020-12-31 730
503848228 1117 2021-01-01 2021-04-10 2021-05-09 556
503848229 1117 2021-05-10 2021-05-10 9999-12-31 555

项目 503848220 未显示在结果中,因为它不在报价范围内。

【讨论】:

    【解决方案2】:

    这里的日期管理不太理想,鉴于您现有的数据结构,我怀疑是否有可靠的方法来完成您的任务,但以下是我的尝试。

    我已经假设您将根据商品日期和商品 ID 进行过滤。

    /* MOCK TABLES AND DATA */
    
    DECLARE @Quote table (
        Category int, ValidFrom date, ValidTo date, Amount decimal(18,2)
    );
    
    DECLARE @Item table (
        ItemID varchar(10), Category int, [Date] date
    );
    
    INSERT INTO @Item ( ItemID, Category, [Date] ) VALUES
        ( '503848220', 1117, '7/2/2020' ),
        ( '503848221', 1117, '7/2/2020' ),
        ( '503848225', 1117, '7/3/2020' ),
        ( '503848227', 1117, '7/6/2020' ),
        ( '503848228', 1117, '7/6/2020' ),
        ( '503848266', 1113, '6/26/2020' );
    
    INSERT INTO @Quote ( Category, ValidFrom, ValidTo, Amount ) VALUES
        ( 1113, '4/1/2020 ', '12/31/9999', 665 ),
        ( 1113, '7/1/2020 ', '12/31/9999', 925 ),
        ( 1113, '5/10/2021', '12/31/9999', 491 ),
        ( 1117, '4/1/2020 ', '12/31/9999', 495 ),
        ( 1117, '7/1/2020 ', '12/31/9999', 730 ),
        ( 1117, '1/1/2021 ', '12/31/9999', 556 ),
        ( 1117, '5/10/2021', '12/31/9999', 555 );
    
    /*
        Return matching row based on an item's id and date
    */
    
    SELECT
        *
    FROM @Item AS i
    LEFT JOIN @Quote AS q
        ON i.Category = q.Category
        AND MONTH( q.ValidFrom ) >= MONTH ( i.[Date] )
        AND YEAR ( i.[Date] ) <= YEAR ( q.ValidTo )
    WHERE
        i.ItemID = '503848220'
        AND i.[Date] = '07/02/2020'
    ORDER BY
        i.Category, i.[Date], q.ValidFrom, q.ValidTo;
    

    返回

    +-----------+----------+------------+----------+------------+------------+--------+
    |  ItemID   | Category |    Date    | Category | ValidFrom  |  ValidTo   | Amount |
    +-----------+----------+------------+----------+------------+------------+--------+
    | 503848220 |     1117 | 2020-07-02 |     1117 | 2020-07-01 | 9999-12-31 | 730.00 |
    +-----------+----------+------------+----------+------------+------------+--------+
    

    如果您希望将数据限制在特定月份,您可以更改

    AND MONTH( q.ValidFrom ) >= MONTH ( i.[Date] )
    

    AND MONTH( q.ValidFrom ) = MONTH ( i.[Date] )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-23
      • 2016-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多