1

我有两个表 X 和 Y,它们的架构相同但记录不同。给定来自 X 的记录,我需要一个查询来查找 Y 中最接近的匹配记录,其中包含非匹配列的 NULL 值。标识列应从比较中排除。例如,如果我的记录如下所示:

------------------------
id | col1 | col2 | col3
------------------------
0  |'abc' |'def' | 'ghi'

表 Y 看起来像这样:

------------------------
id | col1 | col2 | col3
------------------------
6  |'abc' |'def' | 'zzz'
8  | NULL |'def' | NULL

那么最接近的匹配将是记录 8,因为列不匹配的地方有 NULL 值。6 本来是最接近的比赛,但“zzz”取消了比赛资格。

这个问题的独特之处在于除了 id 列和数据类型之外,表的模式是未知的。可能有 4 列,也可能有 7 列。我们只是不知道——它是动态的。我们所知道的是会有一个“id”列,并且这些列将是字符串,varchar 或 nvarchar。

给定来自 X 的记录,在这种情况下,从 Y 中挑选最接近匹配记录的最佳查询是什么?我实际上正在编写一个函数。输入是一个整数(X 中记录的 id),输出是整数(Y 中记录的 id,或 NULL)。我是 SQL 新手,所以简要解释一下您的解决方案中发生的事情会对我有很大帮助。

4

1 回答 1

3

可能有 4 列,或者可能有 7 列....我实际上正在编写一个函数。

这是一项不可能完成的任务。因为函数是确定性的,所以你不能使用动态 SQL 来处理任意表结构的函数。一个存储过程,当然,但不是一个函数。

但是,下面向您展示了一种使用 FOR XML 和一些 XML 分解的方法,以将行反透视为列名和值,然后可以进行比较。此处使用的技术和查询可以合并到存储过程中。

MS SQL Server 2008 架构设置

-- this is the data table to match against
create table t1 (
    id int,
    col1 varchar(10),
    col2 varchar(20),
    col3 nvarchar(40));
insert t1
select 6, 'abc', 'def', 'zzz' union all
select 8, null , 'def', null;

-- this is the data with the row you want to match
create table t2 (
    id int,
    col1 varchar(10),
    col2 varchar(20),
    col3 nvarchar(40));
insert t2
select 0, 'abc', 'def', 'ghi';
GO

查询 1

;with unpivoted1 as (
    select n.n.value('local-name(.)','nvarchar(max)') colname,
           n.n.value('.','nvarchar(max)') value
    from (select (select * from t2 where id=0 for xml path(''), type)) x(xml)
    cross apply x.xml.nodes('//*[local-name()!="id"]') n(n)
), unpivoted2 as (
    select x.id,
           n.n.value('local-name(.)','nvarchar(max)') colname,
           n.n.value('.','nvarchar(max)') value
    from (select id,(select * from t1 where id=outr.id for xml path(''), type) from t1 outr) x(id,xml)
    cross apply x.xml.nodes('//*[local-name()!="id"]') n(n)
)
select TOP(1) WITH TIES
       B.id,
       sum(case when A.value=B.value then 1 else 0 end) matches
from unpivoted1 A
join unpivoted2 B on A.colname = B.colname
group by B.id
having max(case when A.value <> B.value then 1 end) is null
ORDER BY matches;

结果

| ID | MATCHES |
----------------
|  8 |       1 |
于 2012-11-13T21:42:34.947 回答