在某种程度上投射稀疏的结果

时间:2011-05-07 10:35:13

标签: sql-server-2005

我真的不知道该怎么称呼它,但解释起来并不难。

基本上我拥有的是像这样的结果

Similarity ColumnA   ColumnB   ColumnC
1          SomeValue NULL      SomeValue
2          NULL      SomeB     NULL
3          SomeValue NULL      SomeC
4          SomeA     NULL      NULL

此结果是通过将一组字符串与另一个表进行匹配来创建的。每个字符串还包含这些ColumnA..C的一些值,这些值是我不会以某种方式聚合

像min / max这样的东西效果非常好,但我无法弄清楚如何让它考虑最高相似度而不仅仅是最小/最大值。我真的不想要min / max,我想要第一个具有最高相似度的非null值。

理想情况下,结果看起来像这样

ColumnA   ColumnB   ColumnC
SomeA     SomeB     SomeC

我希望能够有效地加入临时结果来计算其余部分,我一直在探索不同的选择。我一直在考虑的是创建一个SQL Server CLR聚合产生“第一个”非空值,但我不确定在结果上运行聚合时是否存在第一个或最后一个这样的事情。

1 个答案:

答案 0 :(得分:1)

好吧,所以我想出来了,我最初遇到的问题是UPDATE FROMJOIN并没有很好地融合在一起。我指望UPDATE只会发生多次,这会给我正确的结果,但是,SQL Server没有这样的保证(它实际上是未定义的行为,尽管它似乎工作我们将没有那个)但是因为你可以针对CTE运行UPDATE我将它与OUTER APPLY结合起来,如果可能的话,选择恰好1行来补充缺失值。

这也是测试数据的全部内容。

DECLARE @cost TABLE (
    make nvarchar(100) not null,
    model nvarchar(100),
    a numeric(18,2),
    b numeric(18,2)
);

INSERT @cost VALUES ('a%', null, 100, 2);
INSERT @cost VALUES ('a%', 'a%', 149, null);
INSERT @cost VALUES ('a%', 'ab', 349, null);
INSERT @cost VALUES ('b', null, null, 2.5);
INSERT @cost VALUES ('b', 'b%', 249, null);
INSERT @cost VALUES ('b', 'b', null, 3);

DECLARE @unit TABLE (
    id int,
    make nvarchar(100) not null,
    model nvarchar(100)
);

INSERT @unit VALUES (1, 'a', null);
INSERT @unit VALUES (2, 'a', 'a');
INSERT @unit VALUES (3, 'a', 'ab');
INSERT @unit VALUES (4, 'b', null);
INSERT @unit VALUES (5, 'b', 'b');

DECLARE @tmp TABLE (
    id int,
    specificity int,
    a numeric(18,2),
    b numeric(18,2),
    primary key(id, specificity)
);

INSERT @tmp 
OUTPUT inserted.* --FOR DEBUGGING
SELECT 
    unit.id
    , ROW_NUMBER() OVER (
        PARTITION BY unit.id 
        ORDER BY cost.make DESC, cost.model DESC
    ) AS specificity
    , cost.a
    , cost.b
FROM @unit unit
INNER JOIN @cost cost ON unit.make LIKE cost.make
    AND (cost.model IS NULL OR unit.model LIKE cost.model)
;

--fix the holes
WITH tmp AS (
    SELECT * 
    FROM @tmp 
    WHERE specificity = 1 
        AND (a IS NULL OR b IS NULL) --where necessary
)
UPDATE tmp
SET 
    tmp.a = COALESCE(tmp.a, a.a)
    , tmp.b = COALESCE(tmp.b, b.b)
OUTPUT inserted.* --FOR DEBUGGING
FROM tmp
OUTER APPLY ( 
    SELECT TOP 1 a 
    FROM @tmp a 
    WHERE a.id = tmp.id 
        AND a.specificity > 1 
        AND a.a IS NOT NULL 
    ORDER BY a.specificity
    ) a
OUTER APPLY ( 
    SELECT TOP 1 b 
    FROM @tmp b 
    WHERE b.id = tmp.id 
        AND b.specificity > 1 
        AND b.b IS NOT NULL 
    ORDER BY b.specificity
    ) b
;