按匹配词数排序

Posted

技术标签:

【中文标题】按匹配词数排序【英文标题】:Order by number of matching words 【发布时间】:2018-10-25 15:53:59 【问题描述】:

我写了一个查询,但它不完全符合我的需求。 我想获取包含单词列表的所有行,并按在字符串中找到的这些单词的数量对其进行排序。

这是我的数据示例:

+--------+------------------------------+
| ITM_ID |           ITM_Name           |
+--------+------------------------------+
|      1 | Blue Shirt with white collar |
|      2 | Party dress                  |
|      3 | Black derbies with cap toe   |
|      4 | Sky blue dress               |
+--------+------------------------------+

如果我用“blue”和“dress”这两个词搜索,我想要以下结果:

+---+------------------------------+
| 4 | Sky blue dress               |
| 1 | Blue Shirt with white collar |
| 2 | Party dress                  |
+---+------------------------------+

“天蓝色连衣裙”位于列表顶部,因为我们在其他字符串中找到了这两个词而不是仅一个词。

我想出了如何用CONTAINS 搜索单词:

SELECT ITM_ID, ITM_Name, CHARINDEX(
FROM T_Item_ITM
WHERE CONTAINS(ITM_Name, 'dress OR blue')

但我没有找到订单的解决方案。你是否有一个 ?

谢谢。

【问题讨论】:

一个词可以在一个字符串中出现多次,例如: “出去”在“出去,该死的地方!出去,我说!”?这会影响排名吗? 哪个版本的 SQL Server 是您的目标版本? 【参考方案1】:

您可以使用string_split 分隔单个单词,然后使用group by 对它们进行计数。

declare @tmp table (ITM_ID int ,  ITM_Name nvarchar(100))
insert @tmp values
     (1 ,'Blue Shirt with white collar')
    ,(2 ,'Party dress')
    ,(3 ,'Black derbies with cap toe')
    ,(4 ,'Sky blue dress')

select a.*
from (
    select t.ITM_ID
        ,count(*) as result_count
    from @tmp t
    cross apply string_split(ITM_Name, ' ') w
    where w.[value] in (
            'blue'
            ,'dress'
            )
    group by ITM_ID
    ) c
inner join @tmp a
    on a.ITM_ID = c.ITM_ID
order by c.result_count desc

结果:

请注意,如果您有额外的标点符号(逗号、分号等),则必须在拆分前用空格替换它们。

【讨论】:

【参考方案2】:

这是一种不同的方法:

DECLARE @Data TABLE (
    Id INT,
    Name VARCHAR(100)
);
INSERT INTO @Data VALUES
    (1, 'Blue Shirt with white collar'),
    (2, 'Party dress'),
    (3, 'Black derbies with cap toe'),
    (4, 'Sky blue dress');

DECLARE @Terms TABLE (
    Value VARCHAR(100)
);
INSERT INTO @Terms VALUES 
    ('dress'),
    ('blue');

WITH TermOccurrences AS (
    SELECT *, (LEN(d.Name) - LEN(REPLACE(d.Name, t.Value, ''))) / LEN(t.Value) AS NumOccurrences
    FROM @Data d
        INNER JOIN @Terms t ON d.[Name] LIKE '%' + t.Value + '%'
), TotalOccurrences AS (
    SELECT Id, SUM(NumOccurrences) AS TotalMatches
    FROM TermOccurrences
    GROUP BY Id
)
SELECT d.Id, d.Name, tot.TotalMatches
FROM TotalOccurrences tot
    INNER JOIN @Data d ON d.Id = tot.Id
ORDER BY tot.TotalMatches DESC

这会通过将术语替换为空字符串并将原始名称的长度与不包含该术语的名称的长度进行比较来检查术语的出现次数。

我无法谈论它的性能,但您可以尝试另一种方法。

【讨论】:

以上是关于按匹配词数排序的主要内容,如果未能解决你的问题,请参考以下文章

请问solr如何按匹配度和更新时间排序,设置权重

按最大条件匹配排序

postgres查询按AND部分匹配排序,然后OR匹配

转MySql模糊搜索结果按匹配度排序

基于词典的中文分词算法2:最少分词法

如何按名称对具有匹配元素的值进行排序?