【问题标题】:How to count a number of times a set of words appear in a given data in t-sql?如何计算一组单词在t-sql中给定数据中出现的次数?
【发布时间】:2021-01-28 20:48:26
【问题描述】:

我的目标是得到这个数据集中“the”、“when”和“wolf”的总数。

我将在返回查询中总共有 14 个计数。

编辑:“the”也被添加到单词“they”的计数中

CREATE TABLE #BaseTable([Text] VARCHAR (500))

INSERT INTO #BaseTable ([Text]) VALUES 
('When the villagers heard the cry, they came running up the hill to drive the wolf away.'),
('But, when they arrived, they saw no wolf.'),
('The boy was amused when seeing their angry faces.')

version of SQL Server:

Microsoft SQL Server 2014 - 12.0.2000.8 (Intel X86) 
    Feb 20 2014 19:20:46 
    Copyright (c) Microsoft Corporation
    Express Edition on Windows NT 6.3 <X64> (Build 9600: ) (WOW64) (Hypervisor)

【问题讨论】:

  • 这不是我想说的典型 RDBMS 任务。无论如何,您需要使用 REGEX 定义它,因为它是一项复杂的任务,例如,They 也包含 the 但您想按照我的理解排除它。
  • 对此我深表歉意。如果我把“the”这个词改成“but”怎么样? @TedoG。编辑:“他们”一词中的“the”也被添加到计数中。在帖子中添加到
  • 你有没有尝试过?你想要区分大小写吗?是否要将they 包含在the 中?
  • 我尝试分别数单词,但我很难数出所有三个单词@HoneyBadger
  • 什么版本的 SQL Server?

标签: sql sql-server string tsql count


【解决方案1】:

你可以拆分字符串并计数:

select count(*)
from t cross apply
     string_split(replace(replace(t.text, '.', ''), ',', ''), ' ') s
where s.value in ('the', 'when', 'wolf');

SQL Server 对于此类字符串操作不是最佳的,但它是可能的。

【讨论】:

  • 什么错误? docs.microsoft.com/en-us/sql/t-sql/functions/…。如果您使用的是旧版本的 SQL Server,您可以在线找到等效的“拆分”功能。
  • 是的,我看到 string_split 也是一个函数。也许我使用的是旧版本。谢谢!
【解决方案2】:

一个非常简单的方法可能如下所示:

DECLARE @BaseTable TABLE ([Text] VARCHAR(255))

INSERT INTO @BaseTable ([Text]) VALUES 
('When the villagers heard the cry, they came running up the hill to drive the wolf away.'),
('But, when they arrived, they saw no wolf.'),
('The boy was amused when seeing their angry faces.')

DECLARE @Val1 VARCHAR(255) = 'when'
,       @Val2 VARCHAR(255) = 'but'
,       @Val3 VARCHAR(255) = 'wolf'

SELECT  (LEN(BT.Text) - LEN(REPLACE(BT.Text, @Val1, ''))) /LEN(@Val1) 
,       (LEN(BT.Text) - LEN(REPLACE(BT.Text, @Val2, ''))) /LEN(@Val2)
,       (LEN(BT.Text) - LEN(REPLACE(BT.Text, @Val3, ''))) /LEN(@Val3)
FROM    @BaseTable AS BT

这个想法是计算目标词被替换为空时的长度差异。但是,如果您有选择的话,您并不想在数据库端执行此操作。

编辑: 将所有值相加非常简单:

SELECT  SUM(val1) + SUM(val2) + SUM(val3)
FROM    (
            SELECT  (LEN(BT.Text) - LEN(REPLACE(BT.Text, @Val1, ''))) /LEN(@Val1) AS val1
            ,       (LEN(BT.Text) - LEN(REPLACE(BT.Text, @Val2, ''))) /LEN(@Val2) AS val2
            ,       (LEN(BT.Text) - LEN(REPLACE(BT.Text, @Val3, ''))) /LEN(@Val3) AS val3
            FROM    @BaseTable AS BT
        ) AS subQ

【讨论】:

【解决方案3】:

您可以使用字符串函数和横向连接来做到这一点:

select bt.*, x.*
from #basetable bt
cross apply (
    select sum(no_matches) no_matches
    from (
        select (len(bt.text) - len(replace(bt.text, w.word, ''))) / len(w.word) no_matches
        from (values ('the'), ('when'), ('wolf')) w(word)
    ) x
) x

如果您只想要结果集中的匹配总数,那么:

select sum(no_matches) total_matches
from #basetable bt
cross apply (
    select (len(bt.text) - len(replace(bt.text, w.word, ''))) / len(w.word) no_matches
    from (values ('the'), ('when'), ('wolf')) w(word)
) x

Demo on DB Fiddle

【讨论】:

  • 有效!!谢谢!我只是想知道这是如何工作的。 w(word) 是否包含集合“the”“when”和“wolf”?最后 x 有什么用?非常感谢!
  • @Guine024:是的,w coutans 所有你想搜索的词。它可以轻松扩展以处理更多单词。 x 是子查询的别名,是 SQL 语言的重要组成部分(每个派生表都必须有别名)。
  • 我明白了。我会记住这一点。太感谢了!祝你有美好的一天!
【解决方案4】:

如果您的 SQL Server 版本早于 2016 年且 STRING_SPLIT 不可用,则可以使用序号拆分器函数(例如 dbo.DelimitedSplit8K)来分隔单词。

dbo.DelimitedSplit8K

CREATE FUNCTION dbo.DelimitedSplit8K
--===== Define I/O parameters
        (@pString VARCHAR(8000), @pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE!  IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
     -- enough to cover VARCHAR(8000)
  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
                ),                          --10E+1 or 10 rows
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
 cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
                     -- for both a performance gain and prevention of accidental "overruns"
                 SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                ),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                 SELECT 1 UNION ALL
                 SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
                ),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
                 SELECT s.N1,
                        ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                   FROM cteStart s
                )
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
 SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
        Item       = SUBSTRING(@pString, l.N1, l.L1)
   FROM cteLen l
;

查询

select count(*)
from #BaseTable bt
     cross apply dbo.DelimitedSplit8K(bt.[Text], ' ') ds
where ds.Item in ('the', 'when', 'wolf');

输出

9

【讨论】:

  • 谢谢你。我无法测试它,因为我可能使用的是旧的 SQL 服务在线编译器。
  • 在您发布的在线编译器上,CREATE FUNCTION 必须是批处理中的第一个语句,并通过 GO 与后面的语句分开。然后临时表和查询工作。序数拆分器通常比替换更有效
  • Gordon 的代码去掉了句点 '.'我没有这样做。我会复制它,然后使用拆分器:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-09
相关资源
最近更新 更多