暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

SQL进阶技巧:非等值连接--单向近距离匹配

会飞的一十六 2024-12-19
88

点击上方【蓝色】字体   关注我们



01 场景描述 


  • 非等值连接

  • 在 SQL 中,连接操作通常基于相等条件(如JOIN... ON table1.column = table2.column
    ),这是等值连接。而非等值连接是指连接条件使用的不是相等运算符,例如>
    <
    BETWEEN
    等。这种连接方式允许根据一定的范围或者顺序关系来关联两个表中的数据。
  • 单向近距离匹配
    单向近距离匹配是一种特殊的非等值连接情况。假设我们有两个表,表 A 和表 B,单向近距离匹配是指对于表 A 中的每一条记录,在表 B 中找到与之最接近(按照某种距离度量,如数值差的绝对值)的一条记录,并且这种匹配是单向的,即只考虑从表 A 到表 B 的匹配,而不考虑从表 B 到表 A 的匹配。这种匹配方式在很多实际场景中都有应用,比如时间序列数据的匹配、地理位置数据的近似匹配等。


表 t_1 和表 t_2 通过 a 和 b 关联时,有相等的取相等的值匹配,不相等时每一 个 a 的值在 b 中找差值最小的来匹。

表 t_1:a 中无重复值

表 t_1:a 中无重复值
a 1 2 4 5 8 10
表 t_2:b 中无重复值 b 2 3 7 11 13
问题:单向最近匹配
 输出结果如下所示:
注意:b 的值可能会被丢弃
a   b
1   2
2   2
4   3
5   3
5   7
8   7
10 11


 02 数据准备 

    create table t_1 as
        (select stack(
                        6,
                        1,
                        2,
                        4,
                        5,
                        8,
                        10
                ) as (a));


    create table t_2 as
        (select stack(
                        5,
                        2,
                        3,
                        7,
                        11,
                        13
                ) as (b));




    03 问题分析 

    步骤1:自关联,生成全量的数据集。并按照关联的结果集,按照abs(a-b)差值排序。注意差值有可能重复,因而采用dense_rank()
      select a
           , b
           , abs(a-b) diff
           , dense_rank()  over (partition by a order by abs(a-b) )
      from t_1,
           t_2



      步骤2:过滤出rn=1 的值则为最终结果
        select a, b
        from (select a
                   , b
                   , abs(a - b)                                              diff
                   , dense_rank() over (partition by a order by abs(a - b) ) rn
              from t_1,
                   t_2) t
        where rn = 1


         04  小 结      

          非等值连接--单向近距离匹配应用

          (1)时间序列数据分析

          • 场景描述
            • 在金融领域,有股票交易数据和公司财务报告发布日期的数据。股票交易数据包含交易日期(trade_date
              )、股票代码(stock_code
              )和交易价格(trade_price
              )等字段;财务报告数据包含公司代码(company_code
              ,与股票代码相关)、报告发布日期(report_date
              )和财务指标(如每股收益eps
              )等字段。
          • 应用方式
            • 单向近距离匹配可以用于为每个股票交易记录找到在交易日期之前最近发布的财务报告。通过非等值连接(如trade_date >= report_date
              )和计算日期差值(如DATEDIFF(trade_date, report_date)
              ),并选择差值最小的记录,可以分析交易价格与最新财务信息之间的关系。例如,投资者可以根据最新的财务报告来评估股票价格是否合理。
            -- 假设已有股票交易表trades和财务报告表reports
            -- 找到每个交易记录对应的最近财务报告
            SELECT
                t.stock_code,
                t.trade_date,
                t.trade_price,
                r.report_date,
                r.eps
            FROM
                trades t
            JOIN
                (
                    SELECT
                        t.id AS trade_id,
                        MIN(DATEDIFF(t.trade_date, r.report_date)) AS min_date_diff
                    FROM
                        trades t
                    JOIN
                        reports r ON t.stock_code = r.company_code AND t.trade_date >= r.report_date
                    GROUP BY
                        t.id
                ) subquery ON t.id = subquery.trade_id
            JOIN
                reports r ON t.stock_code = r.company_code AND DATEDIFF(t.trade_date, r.report_date) = subquery.min_date_diff;
            (2)地理位置数据匹配
            • 场景描述
              • 假设有一个商店位置数据表(store_locations
                ),包含商店编号(store_id
                )、商店经度(store_longitude
                )和纬度(store_latitude
                );还有一个用户位置数据表(user_locations
                ),包含用户编号(user_id
                )、用户经度(user_longitude
                )和纬度(user_latitude
                )。
            • 应用方式
              • 可以使用单向近距离匹配来为每个用户找到最近的商店。这里的距离可以通过地理空间距离公式(如哈弗辛公式)来计算,不过为了简化,也可以先计算经度差和纬度差的绝对值之和作为距离的近似值。通过非等值连接和选择差值最小的记录,就可以找到每个用户最近的商店,这对于推荐附近的商店或者分析店铺的服务范围很有帮助。
              -- 找到每个用户位置对应的最近商店位置
              SELECT
                  u.user_id,
                  u.user_longitude,
                  u.user_latitude,
                  s.store_id,
                  s.store_longitude,
                  s.store_latitude
              FROM
                  user_locations u
              JOIN
                  (
                      SELECT
                          u.id AS user_id,
                          MIN(ABS(u.user_longitude - s.store_longitude) + ABS(u.user_latitude - s.store_latitude)) AS min_distance
                      FROM
                          user_locations u
                      JOIN
                          store_locations s ON 1 = 1  -- 这里可以添加更精确的地理范围筛选条件
                      GROUP BY
                          u.id
                  ) subquery ON u.id = subquery.user_id
              JOIN
                  store_locations s ON ABS(u.user_longitude - s.store_longitude) + ABS(u.user_latitude - s.store_latitude) = subquery.min_distance;
              (3)供应链管理中的物料匹配

                • 场景描述
                  • 在制造业的供应链系统中,有原材料入库时间数据表(material_inventory
                    ),包含物料编号(material_id
                    )、入库日期(in_date
                    )和入库数量(in_quantity
                    );还有生产订单数据表(production_orders
                    ),包含订单编号(order_id
                    )、产品编号(product_id
                    )、生产开始日期(start_date
                    )和所需物料编号(required_material_id
                    )。
                • 应用方式
                  • 单向近距离匹配可以用于为每个生产订单找到在生产开始日期之前最近入库的原材料。通过非等值连接(start_date >= in_date
                    )和计算日期差值,选择差值最小的记录,能够确定生产订单所使用的原材料批次,这对于追溯产品质量、计算成本等方面非常重要。
                  -- 找到每个生产订单对应的最近入库原材料
                  SELECT
                      po.order_id,
                      po.start_date,
                      po.required_material_id,
                      mi.in_date,
                      mi.in_quantity
                  FROM
                      production_orders po
                  JOIN
                      (
                          SELECT
                              po.id AS order_id,
                              MIN(DATEDIFF(po.start_date, mi.in_date)) AS min_date_diff
                          FROM
                              production_orders po
                          JOIN
                              material_inventory mi ON po.required_material_id = mi.material_id AND po.start_date >= mi.in_date
                          GROUP BY
                              po.id
                      ) subquery ON po.id = subquery.order_id
                  JOIN
                      material_inventory mi ON po.required_material_id = mi.material_id AND DATEDIFF(po.start_date, mi.in_date) = subquery.min_date_diff;
                  (4)医疗数据关联

                  • 场景描述
                    • 考虑医院的病历数据(medical_records
                      ),包含患者编号(patient_id
                      )、症状记录日期(symptom_date
                      )和症状描述(symptom_description
                      );还有检查报告数据(test_reports
                      ),包含患者编号(patient_id
                      )、检查日期(test_date
                      )和检查结果(test_result
                      )。
                  • 应用方式
                    • 单向近距离匹配可以为每个症状记录找到在症状记录日期之前最近的检查报告。这有助于医生更好地结合检查结果来分析患者的病情,通过非等值连接(symptom_date >= test_date
                      )和日期差值最小化的记录选择来实现。
                    -- 找到每个症状记录对应的最近检查报告
                    SELECT
                        mr.patient_id,
                        mr.symptom_date,
                        mr.symptom_description,
                        tr.test_date,
                        tr.test_result
                    FROM
                        medical_records mr
                    JOIN
                        (
                            SELECT
                                mr.id AS record_id,
                                MIN(DATEDIFF(mr.symptom_date, tr.test_date)) AS min_date_diff
                            FROM
                                medical_records mr
                            JOIN
                                test_reports tr ON mr.patient_id = tr.patient_id AND mr.symptom_date >= tr.test_date
                            GROUP BY
                                mr.id
                        ) subquery ON mr.id = subquery.record_id
                    JOIN
                        test_reports tr ON mr.patient_id = tr.patient_id AND DATEDIFF(mr.symptom_date, tr.test_date) = subquery.min_date_diff;


                    往期精彩

                    SQL进阶技巧:如何分析互逆记录?| 相互关注为例分析

                    SQL进阶技巧:如何对货场剩余货位进行查询统计?

                    SQL进阶技巧:如何利用Bitmap优化留存指标计算?

                    SQL进阶技巧:用户订单日期间隔异常问题分析

                    数据科学与SQL:如何计算排列熵?| 基于SQL实现

                    SQL进阶技巧:如何计算复合增长率?

                    会飞的一十六

                    微信号:ddan_hashcode

                    扫描右侧二维码关注我们






                    点个【在看】 你最好看







                    文章转载自会飞的一十六,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                    评论