根据关键字的优惠顺序返回大多数匹配结果

时间:2017-05-22 14:46:27

标签: sql sql-server greatest-n-per-group

我已经构建了一个程序来索引文本文件中的关键字并将它们放到数据库中。

我的表很简单:

FILE_ID|Name
------------
1      | a.txt
2      | b.txt
3      | c.txt

KEYWORD_ID|FILE_ID|Hits
-----------------------
1         | 1     | 55
2         | 1     | 10
3         | 1     | 88
1         | 2     | 44
2         | 2     | 15
1         | 3     | 199
2         | 3     | 1
3         | 3     | 4

此表中没有主键。我没有发现它是必要的。

现在,我想搜索哪个文件对某些关键字的匹配率最高。

如果我只有一个关键字,那么很容易:

select top 10 * 
from words 
where keyword_id=1
order by hits desc

假设我想搜索带有关键字1和3的文件(两者都必须存在且第一个关键字具有最高重要性)。几个小时后,我来到这里:

select top 10 k.* 
from 
(
   select file_id, 
     max(hits) as maxhits 
   from words 
   where keyword_id=3 
   group by file_id
) as x 
inner join keyword as k 
  on (k.file_id = x.file_id 
      and k.keyword=1) 
order by k.hits desc

如何做到这一点?特别是如果我想用N个关键字搜索。使用临时表并使用它会更好吗?

如果使用关键字1和3进行搜索,我希望按此顺序返回FILE_ID 3和1(因为file_id 3对关键字1的命中次数较高)

4 个答案:

答案 0 :(得分:1)

不确定,但(根据您的评论)可能是您需要的吗?

(我使用@scsimon回答的表声明)

declare @words table (KEYWORD_ID int, [FILE_ID] int, HITS int)
insert into @words
values
(1,1,55),
(2,1,10),
(3,1,88),
(1,2,44),
(2,2,15),
(1,3,199),
(2,3,1),
(3,3,4)

select [FILE_ID] from (
    select *, row_number() over(partition by KEYWORD_ID order by HITS desc) rn from @words
    where KEYWORD_ID in(1,3)
)t
where rn = 1
order by hits desc

答案 1 :(得分:1)

假设要找到的所有相关关键字都存储在表KTable中,该表有两列IDKEYWORD_ID

然后查询应该是

SELECT 
   FileID,
   SUM(Hits) NetHits,
   SUM(Hits/K.ID) WeightedHits
FROM
   Words w JOIN Ktable K 
    on w.KEYWORD_ID= K.KEYWORD_ID
GROUP BY FileID
HAVING count(1) = (SELECT COUNT(1) FROM Ktable )
ORDER BY 2 DESC,3 DESC

使用Windowing函数的相同查询将是

SELECT 
DISTINCT
FileID,
NetHitsPerFile 
FROM 
(
SELECT 
   FileID,
   SUM(Hits) OVER (PARTITION BY FileID ORDER BY K.ID ASC) NetHitsPerFile,
   SUM(FileID) OVER(PARTITION BY K.ID) Files,
   SUM(Hits/K.ID) OVER (PARTITION BY FileID ORDER BY K.ID ASC) weightedHits

FROM
   Words w JOIN Ktable K 
    on w.KEYWORD_ID= K.KEYWORD_ID
)T
   WHERE Files= (SELECT COUNT(1) FROM Ktable)
   ORDER BY NetHitsPerFile, weightedHits

答案 2 :(得分:0)

以下单行方式...如果您只想查看指定KEYWORD_ID的行,只需在底部添加WHERE CLAUSE即可。 INNER JOINFILE_ID限制为包含您指定的 KEYWORD_ID的{​​{1}},方法是检查非重复计数是否=关键字数量。因此,在下面的示例中,我们将结果集限制为2 KEYWORD_ID并检查以确保每个FILE_ID有2个不同的KEYWORD_ID关联,HAVING子句

declare @words table (KEYWORD_ID int, [FILE_ID] int, HITS int)
insert into @words
values
(1,1,55),
(2,1,10),
(3,1,88),
(1,2,44),
(2,2,15),
(1,3,199),
(2,3,1),
(3,3,4)

select top 10 w.* 
from @words w
inner join
    (select [FILE_ID]
    from @words
    where KEYWORD_ID in (1,3)
    group by [FILE_ID]
    having count(distinct KEYWORD_ID) = 2
    ) x on x.[FILE_ID] = w.[FILE_ID]
order by HITS desc

答案 3 :(得分:0)

您可以将top(n)与tie结合使用,如下所示:

declare @n int = 10 --10 in your scenario
select top (@n) with ties *
from (
    select w.*, f.name from #words w inner join #files f
        on w.[FILE_ID] = f.[file_id]
    ) a 
    order by (row_number() over (partition by a.[file_id] order by hits desc)-1)/@n +1