从SQL中的一列字符串中获取最常用的单词

时间:2016-05-26 01:14:14

标签: sql sql-server tsql

所以我们this database填充了一堆字符串,在这种情况下是帖子标题。

我想做的是:

  1. 将字符串拆分为单词
  2. 计算字符串中出现的字数
  3. 给我前50个单词
  4. 在data.se查询中没有此超时
  5. 我尝试使用适用于data.se的this SO question信息,如下所示:

    select word, count(*) from (
    select (case when instr(substr(p.Title, nums.n+1), ' ') then substr(p.Title, nums.n+1)
                 else substr(p.Title, nums.n+1, instr(substr(p.Title, nums.n+1), ' ') - 1)
            end) as word
    from (select ' '||Title as string
          from Posts p
         )Posts cross join
         (select 1 as n union all select 2 union all select 10
         ) nums
    where substr(p.Title, nums.n, 1) = ' ' and substr(p.Title, nums.n, 1) <> ' '
    ) w
    group by word
    order by count(*) desc
    

    不幸的是,这给了我一些错误:

      

    'substr'不是公认的内置函数名称。语法不正确   靠近'|'。 'nums'附近的语法不正确。

    因此,在SQL中给出一列字符串,每个字符串中有一个可变数量的文本,如何获得最常用的X字列表?

3 个答案:

答案 0 :(得分:10)

正如Blogbeard所说,您提供的查询不适用于SQL Server。这是计算最常用单词的一种方法。这是基于一个由Jeff Moden编写并由SQL Server Central社区成员改进的函数DelimitedSplitN4K

ONLINE DEMO

WITH E1(N) AS (
    SELECT 1 FROM (VALUES
        (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
    ) t(N)
),
E2(N) AS (SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS (SELECT 1 FROM E2 a CROSS JOIN E2 b)
SELECT TOP 50
    x.Item,
    COUNT(*)
FROM Posts p
CROSS APPLY (
    SELECT 
        ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
        Item       = LTRIM(RTRIM(SUBSTRING(p.Title, l.N1, l.L1)))
        FROM (
            SELECT s.N1,
                L1 = ISNULL(NULLIF(CHARINDEX(' ',p.Title,s.N1),0)-s.N1,4000)
            FROM(
                SELECT 1 UNION ALL
                SELECT t.N+1 
                FROM(
                    SELECT TOP (ISNULL(DATALENGTH(p.Title)/2,0))
                        ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
                    FROM E4
                ) t(N)
                WHERE SUBSTRING(p.Title ,t.N,1) = ' '
            ) s(N1)
        ) l(N1, L1)
) x
WHERE x.item <> ''
GROUP BY x.Item
ORDER BY COUNT(*) DESC

由于不允许创建函数,我已经这样写了。如果您有兴趣,这是函数定义:

CREATE FUNCTION [dbo].[DelimitedSplitN4K](
    @pString NVARCHAR(4000), 
    @pDelimiter NCHAR(1)
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN

WITH E1(N) AS (
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
),
E2(N) AS (SELECT 1 FROM E1 a, E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
cteTally(N) AS(
    SELECT TOP (ISNULL(DATALENGTH(@pString)/2,0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (
    SELECT 1 UNION ALL 
    SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
),
cteLen(N1,L1) AS(
    SELECT s.N1,
        ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,4000)
    FROM cteStart s
)
SELECT 
    ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
    Item       = SUBSTRING(@pString, l.N1, l.L1)
FROM cteLen l
;

以下是您将如何使用它:

SELECT TOP 50
    x.Item,
    COUNT(*)
FROM Posts p
CROSS APPLY dbo.DelimitedSplitN4K(p.Title, ' ') x
WHERE LTRIM(RTRIM(x.Item)) <> ''
GROUP BY x.Item
ORDER BY COUNT(*) DESC

结果:

Item             
-------- ------- 
to       3812411 
in       3331522 
a        2543636 
How      1770915 
the      1534298 
with     1341632 
of       1297468 
and      1166664 
on       970554  
from     964449  
for      886007  
not      835979  
is       704724  
using    703007  
I        633838  
-        632441  
an       548450  
when     449169  
file     409717  
how      358745  
data     335271  
do       323854  
can      310298  
get      305922  
or       266317  
error    263563  
use      258408  
value    254392  
it       251254  
my       238902  
function 235832  
by       231025  
Android  228308  
as       216654  
array    209157  
working  207445  
does     207274  
Is       205613  
multiple 203336  
that     197826  
Why      196979  
into     196591  
after    192056  
string   189053  
PHP      187018  
one      182360  
class    179965  
if       179590  
text     174878  
table    169393  

答案 1 :(得分:1)

查询解决方案(无需拆分功能)

的PostgreSQL

select word, count(*) from 
(
    -- get 1st words
    select split_part(title, ' ', 1) as word
    from posts

    union all

    -- get 2nd words
    select split_part(title, ' ', 2) as word
    from posts

    union all

    -- get 3rd words
    select split_part(title, ' ', 3) as word
    from posts

    -- can do this as many times as the number of words in longest title

) words
where word is not null
and word NOT IN ('', 'and', 'for', 'of', 'on')
group by word
order by count desc
limit 50;

如需简明版本,请参阅:https://dba.stackexchange.com/a/82456/95929

答案 2 :(得分:0)

使用现在可用的STRING_SPLIT函数(自SQL Server 2016,兼容性级别130开始),此查询变得更加容易:

SELECT TOP 50
       value [word]
     , COUNT(*) [#times]
FROM posts p
CROSS APPLY STRING_SPLIT(p.title, ' ')
GROUP BY value
ORDER BY COUNT(*) DESC

Stack Exchange Data Explorer上查看它的运行情况,对于Stack Overflow数据库中当前的帖子数,它仍在2分钟内运行。在Stack Overflow emPortuguês上运行时,不必担心可怕的超时。

结果类似于您在answer from Felix中看到的结果:

result of words and their frequency