【问题标题】:How to perform Complex SQL join with multiple approximate matches and return only the first match如何使用多个近似匹配执行复杂 SQL 连接并仅返回第一个匹配
【发布时间】:2020-11-23 09:03:53
【问题描述】:

我正在尝试在 SQL 中执行左连接,我需要 检查多个匹配条件,并且仅在某个 之后在右表中保留 第一个匹配 >对右表进行排序操作。

下面是我的左桌。 (无 Null 值)

Date Customer Shop Product Customer_Score
1/1/2020 C1 S1 P1 2
1/2/2020 C2 S1 P2 8
1/5/2020 C3 S2 P1 6
1/6/2020 C4 S2 P2 10
1/7/2020 C1 S2 P3 2
1/8/2020 C2 S2 P4 4

这是正确的表 (Null 值只能在 Product 列中使用)

Shop Product Min_Customer_Score Valid_From Valid_To Percent_Discount
S1 P1 4 1/1/2020 1/5/2020 10
S1 P1 5 1/1/2020 1/5/2020 11
S1 P1 7 1/1/2020 1/5/2020 12
S1 5 1/1/2020 1/5/2020 13
S2 P1 4 1/1/2020 1/5/2020 14
S2 P2 4 1/1/2020 1/5/2020 15
S2 6 1/1/2020 1/5/2020 16
S2 9 1/1/2020 1/5/2020 17
S2 P1 4 1/6/2020 1/8/2020 18
S2 P2 4 1/6/2020 1/8/2020 19
S2 6 1/6/2020 1/8/2020 20
S2 9 1/6/2020 1/8/2020 21

我想首先按 Product(最后为 null)然后按 Min_Customer_Score(升序)对正确的表进行排序。 然后我想从匹配以下条件的第一行中提取 Min_Customer_Score 和 Discount 值:

  1. Left.Date >= Right.Valid_From
  2. Left.Date
  3. Left.Shop = Right.Shop
  4. Left.Product = Right.Product Right.Product = null
  5. Left.Customer_Score >= Right.Min_Customer_Score

我的最终结果应该如下所示。

Date Customer Shop Product Customer_Score Min_Customer_Score Percent_Discount
1/1/2020 C1 S1 P1 2 null null
1/2/2020 C2 S1 P2 8 5 13
1/5/2020 C3 S2 P1 6 4 14
1/6/2020 C4 S2 P2 10 4 19
1/7/2020 C1 S2 P3 2 null null
1/8/2020 C2 S2 P4 4 null null

基本上,我想为每次购买找到合适的折扣,考虑 Right.Product 中的空值作为默认折扣,适用于所有其他产品。

我熟悉左连接以及在 SQL 中使用子查询。但我什至不明白从哪里开始做如此复杂的查询。我还提到了其他建议使用ROW_NUMBER() OVER (PARTITION BY 的答案,但无法解决这种情况。

编辑: 到目前为止,这是我能够解决的问题。

SELECT left_table.*, right_table.Percent_Discount, right_table.Min_Customer_Score
  , ROW_NUMBER() OVER (
  PARTITION BY left_table.Date, left_table.Customer, left_table.Shop, left_table.Product 
  ORDER BY right_table.Product DESC right_table.Min_Customer_Score ASC) as row_num
LEFT JOIN right_table
  ON left_table.Date >= right_table.Valid_From
  AND left_table.Date <= right_table.Valid_To
  AND left_table.Shop>= right_table.Shop
  AND (left_table.Product = right_table.Product OR right_table.Product is NULL)
  AND left_table.Customer_Score >= right_table.Min_Customer_Score
WHERE row_num = 1

但它给了我以下错误

ERROR:  column "row_num" does not exist
LINE: WHERE row_num = 1

【问题讨论】:

  • 使用row_number 是正确的方法。按照您在问题中概述的方式执行left join,在select 中包含row_number,然后过滤row_number = 1 中的那些。您的partition by 是您只想从中返回一行的值组,order by 确定该partition 中的行顺序。尝试编写此脚本并将您尝试过的内容添加到此问题中,如果您无法使其正常工作。
  • 感谢@iamdave,我按照您的建议尝试了并在问题中添加了该代码

标签: sql postgresql join


【解决方案1】:

使用apply:

select l.*, r.*
from left l outer apply
     (select top (1)
      from right r
      where l.Date >= r.Valid_From and
            l.Date <= r.Valid_To and
            l.Shop = r.Shop and
            (l.Product = r.Product or r.Product = null) and
            (l.Customer_Score >= r.Min_Customer_Score)
      order by (case when product is not null then 1 else 2 end),
               Min_Customer_Score asc
     ) r

【讨论】:

  • 谢谢。这种方法有效。虽然我的左桌子很大,但我有点担心性能。这是否会对左表中的每一行进行单独的数据库查询?
  • @ArjunAriyil “我有点担心性能问题” - 您是否对数据的两个查询进行了测试以进行比较?如果没有,请这样做并使用效果最好的。
  • @ArjunAriyil 。 . .您必须遍历左表的所有行。正确的表可能对性能的影响更大。
【解决方案2】:

最后,我能够解决它,如下所示。感谢@iamdave 的评论

SELECT Date, Customer, Shop, Product, Customer_Score, Min_Customer_Score, Percent_Discount
FROM
(
    SELECT left_table.*, right_table.Percent_Discount, right_table.Min_Customer_Score
      , ROW_NUMBER() OVER (
      PARTITION BY left_table.Date, left_table.Customer, left_table.Shop, left_table.Product 
      ORDER BY right_table.Product DESC right_table.Min_Customer_Score ASC) as row_num
    LEFT JOIN right_table
      ON left_table.Date >= right_table.Valid_From
      AND left_table.Date <= right_table.Valid_To
      AND left_table.Shop = right_table.Shop
      AND (left_table.Product = right_table.Product OR right_table.Product is NULL)
      AND left_table.Customer_Score >= right_table.Min_Customer_Score
) as sub_query
WHERE row_num = 1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多