SQL: len()的单词计数有问题



我正试图计算在表的一列中写入的文本单词。因此,我使用以下查询:

SELECT LEN(ExtractedText) - 
LEN(REPLACE(ExtractedText, ' ', '')) + 1 from EDDSDBO.Document where ID='100'.

我收到一个错误的结果,这是高得多。另一方面,如果我直接将文本复制到语句中,那么它就可以工作,即

SELECT LEN('blablabla text') - LEN(REPLACE('blablabla text', ' ', '')) + 1.

现在数据类型是nvarchar(max),因为文本很长。我已经尝试将列转换为textntext,并应用datalength()而不是len()。尽管如此,我还是得到了相同的结果,它确实可以作为字符串工作,但不能从表中工作。

你计算的是空格而不是单词。这通常会得到一个近似的答案。

'  this     string    will    give an    incorrect      result   '

试试这个方法:http://www.sql-server-helper.com/functions/count-words.aspx

CREATE FUNCTION [dbo].[WordCount] ( @InputString VARCHAR(4000) ) 
RETURNS INT
AS
BEGIN
DECLARE @Index          INT
DECLARE @Char           CHAR(1)
DECLARE @PrevChar       CHAR(1)
DECLARE @WordCount      INT
SET @Index = 1
SET @WordCount = 0
WHILE @Index <= LEN(@InputString)
BEGIN
    SET @Char     = SUBSTRING(@InputString, @Index, 1)
    SET @PrevChar = CASE WHEN @Index = 1 THEN ' '
                         ELSE SUBSTRING(@InputString, @Index - 1, 1)
                    END
    IF @PrevChar = ' ' AND @Char != ' '
        SET @WordCount = @WordCount + 1
    SET @Index = @Index + 1
END
RETURN @WordCount
END
GO
使用

DECLARE @String VARCHAR(4000)
SET @String = 'Health Insurance is an insurance against expenses incurred through illness of the insured.'
SELECT [dbo].[WordCount] ( @String )

前导空格,尾随空格,相邻单词之间的两个或多个空格-这些都可能导致您得到错误的结果。

函数LTRIM()RTRIM()可以帮助您消除前两个问题。至于第三个,您可以使用REPLACE(ExtractedText, ' ', ' ')用单个替换双空格,但我不确定您是否没有三个空格(在这种情况下,您需要重复替换)。


下面是一个UDF,它使用cte和排序来消除额外的空格,然后对剩余的空格进行计数,以返回作为单词数的数量:

CREATE FUNCTION fnCountWords (@Str varchar(max))
RETURNS int
AS BEGIN
  DECLARE @xml xml, @res int;
  SET @Str = RTRIM(LTRIM(@Str));
  WITH split AS (
    SELECT
      idx = number,
      chr = SUBSTRING(@Str, number, 1)
    FROM master..spt_values
    WHERE type = 'P'
      AND number BETWEEN 1 AND LEN(@Str)
  ),
  ranked AS (
    SELECT
      idx,
      chr,
        rnk = idx - ROW_NUMBER() OVER (PARTITION BY chr ORDER BY idx)
      FROM split
  )
  SELECT @res = COUNT(DISTINCT rnk) + 1
  FROM ranked
  WHERE chr = ' ';
  RETURN @res;
END
使用这个函数,您的查询将简单地像这样:
SELECT fnCountWords(ExtractedText)
FROM EDDSDBO.Document
WHERE ID='100'

更新2

函数使用一个系统表master..spt_values作为统计表。所使用的特定子集仅包含从0到2047的值。这意味着对于长度超过2047个字符的输入(在修剪前导和尾随空格之后),如@t-clausen,该函数将无法正确工作。Dk在他的评论中正确地指出。因此,如果可以使用更长的输入字符串,则应该使用自定义计数表。

将空格替换为文本中从未出现过的内容,如' $!’或者选择另一个值。然后替换所有的"$!"'和'$!这样的话,每个单词后面的空格永远不会超过1个。然后使用当前脚本。我把一个词定义为空格后跟非空格。

这是一个例子

DECLARE @T TABLE(COL1 NVARCHAR(2000), ID INT)
INSERT @T VALUES('A B  C   D', 100)
SELECT LEN(C) - LEN(REPLACE(C,' ', '')) COUNT FROM (
SELECT REPLACE(REPLACE(REPLACE(' ' + COL1, ' ', ' $!'), '$! ',''), '$!', '') C
FROM @T ) A

这是一个递归解

DECLARE @T TABLE(COL1 NVARCHAR(2000), ID INT)
INSERT @T VALUES('A B  C   D', 100)
INSERT @T VALUES('have a nice day with 7 words', 100)
;WITH CTE AS
(
SELECT 1 words, col1 c, col1 FROM @t WHERE id = 100
UNION ALL
SELECT words +1, right(c, len(c) - patindex('% [^ ]%', c)), col1 FROM cte
WHERE patindex('% [^ ]%', c) > 0
)
SELECT words, col1 FROM cte WHERE patindex('% [^ ]%', c) = 0

您应该使用varchar数据类型声明列,如:

create table emp(ename varchar(22));
insert into emp values('amit');
select ename,len(ename) from emp;

输出:4

最新更新