【问题标题】:What is a good approach in MS SQL Server 2008 to join on a "best" match?MS SQL Server 2008 中加入“最佳”匹配的好方法是什么?
【发布时间】:2011-04-12 04:19:15
【问题描述】:

本质上,我想根据“呼叫”表中的电话号码字段从“费率”表中选择前缀的最佳匹配。鉴于下面的示例数据,“0123456789”最适合匹配前缀“012”,而“0100000000”最适合匹配前缀“01”。

我在 SQL cmets 中包含了一些带有更多正确匹配示例的 DML。

费率表将有大约 70,000 行,调用表将有大约 2000 万行。但是基于 dateTime 列的 Call 表中的 Select 会有一个限制,因此实际上查询只需要运行超过 50 万个调用行。

费率表中的前缀最长可达 16 个字符。

我不知道如何在 SQL 中解决这个问题,我目前正在考虑编写一个 C# SQLCLR 函数来完成它。有没有人做过类似的事情?如果您有任何建议,我将不胜感激。

示例数据

调用表:

Id  TelephoneNumber
1   0123456789
2   0100000000
3   0200000000
4   0780000000
5   0784000000
6   0987654321

费率表:

Prefix Scale
       1
01   1.1
012 1.2
02   2
078    3
0784   3.1

DML

create table Rate
(
    Prefix nvarchar(16) not null,
    Scale float not null
)

create table [Call]
(
    Id bigint not null,
    TelephoneNumber nvarchar(16) not null
)

insert into Rate (Prefix, Scale) values ('', 1)
insert into Rate (Prefix, Scale) values ('01', 1.1)
insert into Rate (Prefix, Scale) values ('012', 1.2)
insert into Rate (Prefix, Scale) values ('02', 2)
insert into Rate (Prefix, Scale) values ('078', 3)
insert into Rate (Prefix, Scale) values ('0784', 3.1)

insert into [Call] (Id, TelephoneNumber) values (1, '0123456789') --match 1.2
insert into [Call] (Id, TelephoneNumber) values (2, '0100000000') --match 1.1
insert into [Call] (Id, TelephoneNumber) values (3, '0200000000') --match 2
insert into [Call] (Id, TelephoneNumber) values (4, '0780000000') --match 3
insert into [Call] (Id, TelephoneNumber) values (5, '0784000000') --match 3.1
insert into [Call] (Id, TelephoneNumber) values (6, '0987654321') --match 1

注意:最后一个 '0987654321' 匹配空白字符串,因为没有更好的匹配项。

【问题讨论】:

  • 性能有多重要?如果您需要大量的瞬间响应,您可能需要设置额外的索引前缀表以避免模式匹配。
  • 谢谢 - 我会做一些性能测试,看看它是如何运行的 - 虽然报告会每月运行一次,所以性能并不重要。

标签: c# tsql join sqlclr


【解决方案1】:

由于这是基于部分匹配,子选择将是唯一可行的选择(除非像 LukeH 假设的那样,每个调用都是唯一的)

select
    c.Id,
    c.TelephoneNumber,
    (select top 1 
         Scale 

         from Rate r 

         where c.TelephoneNumber like r.Prefix + '%' order by Scale desc
    ) as Scale

from Call c

【讨论】:

  • 接受作为答案谢谢。我稍微改变了一下,根据前缀匹配的长度而不是比例来选择“最好的”——尽管我相信财务人员更喜欢我总是选择最昂贵的!因此,按照 Scale desc 排序,它是按照 len(r.Prefix) desc 排序的。
【解决方案2】:
SELECT t.Id, t.TelephoneNumber, t.Prefix, t.Scale
FROM
(
    SELECT *, ROW_NUMBER() OVER
              (
                  PARTITION BY c.TelephoneNumber
                  ORDER BY r.Scale DESC
              ) AS RowNumber
    FROM [call] AS c
        INNER JOIN [rate] AS r
            ON c.TelephoneNumber LIKE r.Prefix + '%'
) AS t
WHERE t.RowNumber = 1
ORDER BY t.Id

【讨论】:

  • @Adam:我的第一次尝试是由于误读了问题。现已修复。
【解决方案3】:

试试这个:

select Prefix, min(c.TelephoneNumber)
from Rate r
left outer join Call c on c.TelephoneNumber like left(Prefix + '0000000000', 10) 
    or c.TelephoneNumber like Prefix + '%'
group by Prefix

【讨论】:

  • 谢谢,但还不完全是这样,我不知道你可以在 Join 中使用 like 运算符 - 这很酷。我正在根据您的回答尝试不同的变体。
【解决方案4】:

您可以使用左连接来尝试找到“更好”的匹配项,然后在 where 子句中消除此类匹配项。例如:

select
 *
from
 Call c
  inner join
 Rate r
  on
   r.Prefix = SUBSTRING(c.TelephoneNumber,1,LEN(r.Prefix))
  left join
 Rate r_anti
  on
   r_anti.Prefix = SUBSTRING(c.TelephoneNumber,1,LEN(r_anti.Prefix)) and
   LEN(r_anti.Prefix) > LEN(r.Prefix)
where
 r_anti.Prefix is null

【讨论】:

    猜你喜欢
    • 2016-12-18
    • 1970-01-01
    • 1970-01-01
    • 2010-09-20
    • 2019-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多