【问题标题】:Using Scoring to find Best Match in SQL使用评分在 SQL 中查找最佳匹配
【发布时间】:2015-04-02 14:01:24
【问题描述】:

假设我有一个像这样的 DATA 表:

ID | Col1 | Col2 | Col3
 1    a      b      23
 2    a      c      14
 3    f      g      11

假设我有一个 POSSIBLE_MATCHES 表,例如:

MatchID  | Col1 | Col2 | Col3
101         a      a      11
102         a      b      11
103         a      b      14
104         a      c      23
105         f      a      1

假设我有一个 WEIGHTS 表,例如(如果你想为了讨论和简单起见假设所有权重为 1 - 我可以稍后即兴解决我的解决方案以合并权重):

Col | Weight
Col1    1
Col2    1.5
Col3    2

因此,对于每个可能的匹配,我们将在每个匹配列上计算一个 SCORE

    Score = Col1 Weight * (CASE WHEN DATA.COL1 = POSSIBLE_MATCHES.Col1 THEN 1 ELSE 0) + 
            Col2 Weight * (CASE WHEN DATA.COL2 = POSSIBLE_MATCHES.Col2 THEN 1 ELSE 0) +
            Col3 Weight * (CASE WHEN DATA.COL3 = POSSIBLE_MATCHES.Col3 THEN 1 ELSE 0)

例如,第一行的 BEST MATCH:Col1 = a, Col2 = b, Col3 = 23:

MatchID  | Col1 | Col2 | Col3 | Score
101         a      a      11     1*1 + 1.5*0 + 2*0 = 1
102         a      b      11     1*1 + 1.5*1 + 2*0 = 2.5
103         a      b      14     1*1 + 1.5*1 + 2*0 = 2.5
104         a      c      23     1*1 + 1.5*0 + 2*1 = 3
105         f      a      1      1*0 + 1.5*0 + 2*0 = 0

所以在这种情况下,ID:1 的最佳匹配是 MatchID:104。如果分数相同,则取最低的 MatchID。

如果你想玩这个,这里有一个 sql fiddle: http://sqlfiddle.com/#!6/9df45/1

对于 DATA 中的每个 ID,我如何在 POSSIBLE MATCHES 中找到最佳匹配?

【问题讨论】:

  • Best Match 是得分最高的 MatchID
  • 所以解决方案是正确加入表格,评估分数并选择得分最高的匹配项。你到底卡在哪里了?
  • @ThorstenKettner:是的。只需要一些关于如何做这样的事情的指导。
  • 除了粗体(真的,跳过粗体),这个最新的编辑使这是一个很好的问题。

标签: sql sql-server


【解决方案1】:

在这个解决方案中,我们进行完全连接以获取所有可能性并评估所有可能性的分数。然后,我们使用 ROW_NUMBER 为它们分配一个从最好到最低的数字。最后,我们用“WHERE Rank = 1”排除所有不是最好的

SELECT * 
FROM 
(SELECT data.ID,
        possible_matches.MatchID,
        Score =  (CASE WHEN data.Col1 = possible_matches.Col1 THEN 1 ELSE 0 END) * 1 +
                (CASE WHEN data.Col2 = possible_matches.Col2 THEN 1 ELSE 0 END) * 1.5 +
                (CASE WHEN data.Col3 = possible_matches.Col3 THEN 1 ELSE 0 END) * 2,
        [Rank] = ROW_NUMBER() OVER(PARTITION BY data.ID ORDER BY (CASE WHEN data.Col1 = possible_matches.Col1 THEN 1 ELSE 0 END) * 1 +
                                                            (CASE WHEN data.Col2 = possible_matches.Col2 THEN 1 ELSE 0 END) * 1.5 +
                                                            (CASE WHEN data.Col3 = possible_matches.Col3 THEN 1 ELSE 0 END) * 2 DESC)
from data, possible_matches) AS AllScore
WHERE AllScore.[Rank] = 1

【讨论】:

  • 刚刚注意到我忘了放 DESC 排序。我加了
【解决方案2】:

试试这个:

DECLARE @d TABLE(ID INT, Col1 CHAR(1), Col2 CHAR(1), Col3 INT)
DECLARE @m TABLE(ID INT, Col1 CHAR(1), Col2 CHAR(1), Col3 INT)


INSERT INTO @d VALUES
(1, 'a', 'b', 23),
(2, 'a', 'c', 14),
(3, 'f', 'g', 11)

INSERT INTO @m VALUES
(101, 'a', 'a', 11),
(102, 'a', 'b', 11),
(103, 'a', 'b', 14),
(104, 'a', 'c', 23),
(105, 'f', 'a', 1)


SELECT DataID, MatchID FROM 
(
    SELECT d.ID AS DataID, 
           m.ID AS MatchID, 
               ROW_NUMBER() OVER(PARTITION BY d.ID ORDER BY 
                CASE WHEN d.Col1 = m.Col1 THEN 1 ELSE 0 END * 1 +
                CASE WHEN d.Col2 = m.Col2 THEN 1 ELSE 0 END * 1.5 +
                CASE WHEN d.Col3 = m.Col3 THEN 1 ELSE 0 END * 2 DESC) AS rn

    FROM @d d
    CROSS JOIN @m m
) t WHERE rn = 1

输出:

DataID  MatchID
1       104
2       103
3       102

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-22
    • 1970-01-01
    • 1970-01-01
    • 2014-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多