【问题标题】:Join tables based on best matching collection of multiple foreign keys, each of which are nullable基于多个外键的最佳匹配集合的连接表,每个外键都可以为空
【发布时间】:2019-08-03 08:11:01
【问题描述】:

我正在尝试创建一个 tSQL 查询,该查询能够将两个表连接到我需要的数据中。这两个表中的每一个都具有与其他表相同的一组 3 个外键(例如表 {x、y、z})。在 table1 上,外键是非空的,而 table2 所有三个键都是可以空的。

示例表架构:

表 1 [id, fk_x (non null), fk_y (non null), fk_z (non null), table1_data]

表 2 [id, fk_x (null), fk_y (null), fk_z (null), table2_data]

我需要将 table1 中的每一行与表 2 中与 table1 中的键最匹配的行连接起来。最佳匹配是指以下内容。

  1. table2 中的所有非空键必须与 table1 中的键匹配。
  2. table2 中的 Null 键与 table1 中的任何键值匹配。
  3. 包含最多匹配键的行获胜。
  4. 具有相同数量的匹配键的行将加权 fk_x > fk_y > fk_z 所在的键以确定决胜局。

注意:table2 有一个唯一约束,保证表中仅存在 3 个键的 1 个组合。

示例

[Table1]
+----+-----+-----+-----+------+
| id |fk_x |fk_y |fk_z | data |
+-----------------------------+
| 1  | 9   |  3  | 11  |  a   |
+-----------------------------+
| 2  | 9   |  4  |  2  |  b   |
+-----------------------------+
| 3  | 7   |  5  |  2  |  c   |
+-----------------------------+
| 4  | 7   |  6  |  2  |  d   |
+----+-----+-----+-----+------+

[Table2]
+----+-----+-----+-----+------+
| id |fk_x |fk_y |fk_z | data |
+-----------------------------+
| 10 | null| null| null|   A  |
+-----------------------------+
| 20 | null|  5  |  2  |   B  |
+-----------------------------+
| 30 | 7   |  5  | null|   C  |
+-----------------------------+
| 40 | 7   | null|  2  |   D  |
+----+-----+-----+-----+------+

[Results]
+----+-----+-----+-----+------+
| id |  t1_data  |  t2_data   |
+-----------------------------+
| 1  |    a      |     A      |
+-----------------------------+
| 2  |    b      |     A      |
+-----------------------------+
| 3  |    c      |     C      |
+-----------------------------+
| 4  |    d      |     D      |
+----+-----+-----+-----+------+

我在尝试解决要求 3 和 4 时遇到了麻烦。如果有人可以为此提供任何提示或解决方案,我将不胜感激。

顺便说一句:我将在 SQL Server 2014 上运行查询。

【问题讨论】:

  • 你能发布你正在尝试的查询吗?

标签: sql sql-server tsql


【解决方案1】:

你可以使用apply:

select t1.*, t2.t2_data
from table1 t1 outer apply
     (select top (1) t2.*
      from table2 t2
      where (t2.fk_x is null or t2.fk_x = t1.fk_x) and
            (t2.fk_y is null or t2.fk_y = t1.fk_y) and
            (t2.fk_z is null or t2.fk_z = t1.fk_z)
      order by ( (case when t2.fk_x is not null then 4 else 0 end) +
                 (case when t2.fk_y is not null then 2 else 0 end) +
                 (case when t2.fk_z is not null then 1 else 0 end)
               ) desc
     ) t2;

子查询根据您的规则查找table2 中的所有匹配行——NULL 匹配或完全匹配。

order by 然后按完全匹配的数量排序,因此可以返回最佳匹配行。

【讨论】:

  • 谢谢,这个解决方案可以满足我的需要。我只需要修改案例条件的值来处理关键优先级要求。我将键 {x, y, z} 的值分别修改为 {4, 2, 1}。我会将其标记为具有这些修改的解决方案。
  • @NickC。 . .我现在看到了决胜局的要求。干杯。
【解决方案2】:

首先使用CTE 应用条件,然后使用NOT EXISTS

with cte as (
  select t1.id, t1.data t1_data, t2.data t2_data,
    case when t1.fk_x = t2.fk_x then 100 else 0 end + 
    case when t1.fk_y = t2.fk_y then 10 else 0 end +
    case when t1.fk_z = t2.fk_Z then 1 else 0 end matchxyz
  from table1 t1 left join table2 t2
  on t1.fk_x = coalesce(t2.fk_x, t1.fk_x)
  and t1.fk_y = coalesce(t2.fk_y, t1.fk_y)
  and t1.fk_z = coalesce(t2.fk_z, t1.fk_z)
)

select c.id, c.t1_data, c.t2_data from cte c
where not exists (
  select 1 from cte 
  where id = c.id and t1_data = c.t1_data and matchxyz > c.matchxyz
)

demo:
结果:

> id | t1_data | t2_data
> -: | :------ | :------
>  1 | a       | A      
>  2 | b       | A      
>  3 | c       | C      
>  4 | d       | D      

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多