【问题标题】:SELECT first 10 words from SQL Table从 SQL 表中选择前 10 个单词
【发布时间】:2017-03-06 07:54:06
【问题描述】:

我需要从 SQL 表字段中选择查询前 10 个单词。

我有字段名称:描述,所以我需要描述字段中的前 10 个单词。

EX: 说明:我对这个答案感觉不好;我不喜欢 GROUP BY,我宁愿不做数字表。希望有人可以接我。

输出:我对这个答案感觉不太好;我不喜欢

【问题讨论】:

  • 请添加预期的输出、输入以及到目前为止您尝试了什么
  • 查看这里了解更多关于如何提出完美问题的信息:spaghettidba.com/2015/04/24/…
  • 什么你在说什么答案?在任何情况下,数字表都能让事情变得更快。问题是问题本身——它非常模糊。在任何情况下,SQL 都不是字符串操作语言。这不是您是否喜欢查询,而是它是否以最有效的方式产生结果
  • 这个问题的写作方式,您可能会问如何拆分描述并选择前 10 个单词作为结果。或者您可能会问如何从每个描述中提取前 10 个单词而不是完整描述。你为什么不在客户端上做呢?

标签: sql-server sql-server-2008 tsql


【解决方案1】:

另一种选择是使用 CROSS APPLY 和一些 XML。 CROSS APPLY 中的逻辑可以很容易地移植到 UDF(标量或表值)中。

示例

Declare @YourTable table (ID int,LongDesc varchar(max))
Insert into @YourTable values
(1,'I don''t feel good about this answer; I don''t like the GROUP BY and I would rather not do the table of numbers. Hopefully somebody can pick me up on this.')

Select A.ID
      ,ShortDesc = B.S
 From  @YourTable A
 Cross Apply (
                Select S = xDim.value('/x[1]','varchar(100)')+' '
                          +xDim.value('/x[2]','varchar(100)')+' '
                          +xDim.value('/x[3]','varchar(100)')+' '
                          +xDim.value('/x[4]','varchar(100)')+' '
                          +xDim.value('/x[5]','varchar(100)')+' '
                          +xDim.value('/x[6]','varchar(100)')+' '
                          +xDim.value('/x[7]','varchar(100)')+' '
                          +xDim.value('/x[8]','varchar(100)')+' '
                          +xDim.value('/x[9]','varchar(100)')+' '
                          +xDim.value('/x[10]','varchar(100)')
                From  (Select Cast('<x>' + replace((Select replace(A.LongDesc,' ','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A 
             ) B

退货

ID  ShortDesc
1   I don't feel good about this answer; I don't like

【讨论】:

  • @maSTARrHiAn 我对基准很感兴趣,并且愿意承认我错了。但是,在这种情况下,我的方法更快。对 100K 记录进行系列基准测试。每次跑 5 次。我的方法平均为 161 毫秒。您的方法 377 毫秒。
  • 我应该说你用的是什么工具?
  • 远程连接到客户端的 BIG ASS 服务器。在我的笔记本电脑上也有类似的结果。就测试而言,我导入/刷新了所有 FRED 系列定义……大约 579,733 条记录,平均注释长度为 544 个字符或 74 个单词。我的建议是运行您自己的测试并确定最适合您的测试。顺便说一句-我在辩论:)
  • @JohnCappelletti,你的脚本是硬编码的,假设在另一个例子中我想要前 15 个然后我修改你的脚本。我不能传递参数
  • @KumarHarsh 如果你想要变量,你将不得不去垂直,或者一个 UDF
【解决方案2】:

left() 函数可以吗?

从 your_table 中选择 left(Description, 10);

【讨论】:

  • 这将返回前 10 个字符,而不是前 10 个单词
【解决方案3】:

您可以查看this 答案以了解如何在SQL Server 的上下文中实现和使用.net 函数。在这里,我使用SQLSCLR实现.netRegex.Match函数。

DECLARE @DataSource TABLE
(
    [Description] NVARCHAR(MAX)
);

INSERT INTO @DataSource ([Description])
VALUES ('word01, word02,         word03, word04, word05, word06')
      ,('word01,word02, word03, word04, word05, word06')
      ,('word01!word02, word03: word04, word05, word06');

SELECT *
FROM @DataSource DS
CROSS APPLY [dbo].[fn_Utils_RegexMatches] ([Description], '^(\w+\b.*?){3}') RM;

这将为您提供以下输出:

这太详细了(并且只提取了第一个three 字)。您的最终查询可能是这样的:

SELECT DS.[Description], RM.[CaptureValue]
FROM @DataSource DS
CROSS APPLY [dbo].[fn_Utils_RegexMatches] ([Description], '^(?n)(\w+\b.*?){3}') RM;

无论如何,使用正则表达式你可以使用任何你想要的分隔符,但更重要的是你可以在 T-SQL 的上下文中执行 .net 代码,这是巨大的。

您可以从这里阅读和学习很多内容。前面的答案实现起来更简单、更快捷。

【讨论】:

  • 大量内存消耗和延迟。即使使用最仔细的编码,无论您是否需要结果,正则表达式都会生成匹配树。这些查询的编写方式将针对 all 行执行。 SQLCLR 字符串拆分方法使用正则表达式是有原因的
  • 是的,你是对的。您需要在每次解决方案时都进行测试,以确保它能够按预期使用您的生产数据。这只是 SQLCLR 的演示。
  • 这是一个演示链接。如果您搜索T-SQL string split,您会发现各种 SQLCLR 实现。最快的只在需要时直接枚举字符。它的速度与 SQL Server 2016 的 STRING_SPLIT 相当。如果你只想要 100 个单词中的 10 个,那将是一个巨大的进步
  • 如果正则表达式可以返回直到第 10 个空格出现的所有内容,或者仅返回第 10 个空格的索引,那么它会有所帮助。这将是一个可以直接使用的匹配项,或者作为LEFT() 的参数。它可能会比STRING_SPLIT 更快,因为它不会生成任何子字符串
  • 我认为^(?:[^\s]*\s){10} 将返回所有内容,包括第 10 个空格作为单个字符串结果
【解决方案4】:

你可以试试这个

declare @message nvarchar(max) = 'I don''t feel good about this answer; I don''t like the GROUP BY and I would rather not do the table of numbers. Hopefully somebody can pick me up on this'

SELECT left(@message,
CHARINDEX(' ', @message,
  CHARINDEX(' ', @message,
    CHARINDEX(' ',@message,
     CHARINDEX(' ',@message,
      CHARINDEX(' ',@message,
       CHARINDEX(' ',@message,
        CHARINDEX(' ',@message,
         CHARINDEX(' ',@message,
          CHARINDEX(' ',@message,
            CHARINDEX(' ',@message) + 1 ) + 1 ) + 1 ) + 1) + 1) + 1) + 1 )+ 1 ) + 1))

问候

【讨论】:

    【解决方案5】:

    用大数据试试这个。

    DECLARE @T TABLE(COL1 NVARCHAR(MAX))
    INSERT INTO @T VALUES
    ('I dont feel good about this answer; I dont like the GROUP BY and I would rather not do the table of numbers. Hopefully somebody can pick me up on this.')
    
    ;WITH CTE AS
    (
    select col1,substring(col1,1,CHARINDEX(' ',col1))Words
    ,substring(col1,CHARINDEX(' ',col1)+1,len(col1))Residue
    , 1 rn from @t
    
    union ALL
    select col1,substring(Residue,1,CHARINDEX(' ',Residue))
    ,substring(Residue,CHARINDEX(' ',Residue)+1,len(Residue))
    ,rn+1
    from cte
    where len(Words)>0 and rn<10
    )
    SELECT Words FROM cte
    

    【讨论】:

    • 运行需要 60 毫秒
    • @maSTArHiAn ,我刚刚测试了 10,000 行(这意味着输出为 1,00,000 行)平均时间为 6 毫秒。你可以抛出示例数据
    • 执行时间不是写入时间。
    • @maSTArHiAn,你是对的。我的脚本需要 6232 毫秒来处理 10000 行。John Cappelletti 脚本只需要 279 毫秒。我很快就会提供其他解决方案。但在另一个示例中,递归 CTE 比 xml 方法花费的时间要少得多。如果我得到那个链接,我把它贴在这里。
    【解决方案6】:

    我知道你已经有了答案,但我只是想换一种方式。

    DECLARE @WORD VARCHAR(8000)= 'I don''t feel good about this answer; I don''t like the GROUP BY and I would rather not do the table of numbers. Hopefully somebody can pick me up on this.'
    DECLARE @LOOP INT = 1
    DECLARE @STR_POS INT = 1
    DECLARE @CI INT
    DECLARE @RES_STR VARCHAR(8000) = ''
    
    WHILE(@LOOP<=10)
        BEGIN
            SELECT @CI = CHARINDEX(' ',@WORD,@STR_POS)
            SELECT @RES_STR = @RES_STR +' '+ SUBSTRING(@WORD,@STR_POS,(CHARINDEX(' ',@WORD,@STR_POS)-@STR_POS))
            SET @STR_POS = @CI+1
            SET @LOOP=@LOOP+1
    
        END
    PRINT @RES_STR
    

    【讨论】:

      【解决方案7】:

      这并没有给你一个行集解决方案,而是另一种选择,使用类似的技术用更少的代码解析前十个单词,纯粹是为了说明。我想这可以转换为用户定义的函数,但我们知道他们表现不佳。它可能可以使用 CrossApply 组合以消除对 UDF 的需要。我必须将此归功于 xml 语法的帮助:https://www.mssqltips.com/sqlservertip/1771/splitting-delimited-strings-using-xml-in-sql-server/

      DECLARE 
      @xml as xml,
      @str as varchar(1000)= 'I don''t feel good about this answer; I don''t like the GROUP BY and I would rather not do the table of numbers. Hopefully somebody can pick me up on this.',
      @delimiter as char(1) = char(32),
      @xmlspace  as char(13) = '<y>&#160;</y>'
      
      SET @xml = cast(('<x>'+replace(@str,@delimiter,@xmlspace)+'</x>') as xml)
      
      SELECT TOP 10 N.value('.', 'varchar(50)') FROM @xml.nodes('x') as T(N) 
      

      【讨论】:

      • 这需要 43 毫秒才能运行
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-08
      • 2011-09-22
      • 1970-01-01
      • 2011-01-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多