【问题标题】:Search and Extract a word from rows in SQL从 SQL 中的行中搜索和提取单词
【发布时间】:2015-12-19 02:57:18
【问题描述】:

我有一张下面的表格

表 A

Text,id,Cid,CName,Aid,AName
Acc.sa is very Acc.pa and Acc.ba is awesome, 1,2,AB,1,CC
Acc.aa is awesome and Acc.sas is great,2,3,CC,1,CC
Acc.ee is not only great but Acc.sew is best,4,3,FF,1,CC

它应该获取与 Acc 关联的所有单词,因此结果应该是

 Did,id,Cid,CName,Aid,AName
 Acc.sa,1,2,AB,1,CC
 Acc.pa,1,2,AB,1,CC
 Acc.ba,1,2,AB,1,CC
 Acc.aa,2,3,CC,1,CC
 Acc.sas,2,3,CC,1,CC
 Acc.ee,4,3,FF,1,CC
 Acc.sew,4,3,FF,1,CC

即每个搜索都应该有一个新行

我尝试了 CHARINDEX 和 substring,但我不确定如何在 SELECT 语句中继续使用 CHARINDEX 和 substring 任何帮助都非常感谢。

【问题讨论】:

  • 您是否有您要查找的“acc”字词列表?
  • 任何有acc的东西。是我的目标词
  • 您需要在递归 CTE 中使用带有 SUBSTRING 的 PATINDEX
  • 您对点后面的字符数有限制吗?还是限制每行“acc”匹配的数量?
  • 您的 TableA 说的是 6 列,但您的示例数据只有 4 列。您能发布一些明确的内容吗?也许是一个 sqlfiddle?你的结果同样令人困惑。您列出了 6 列,但只显示 4。正如现在发布的那样,这是不可能回答的。

标签: sql sql-server


【解决方案1】:
with base10 as (
    select n
    from (values (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) v(n)
), k as (
    select d2.n * 100 + d1.n * 10 + d0.n + 1 as n
    from base10 d0 cross join base10 d1 cross join base10 d2
)
select
    substring(a.Text, k.n, charindex(' ', a.Text, k.n) - k.n) as Did,
    Id, Cid, CName, Aid, AName
from TableA a inner join k
    on substring(a.Text, k.n, 4) = 'Acc.'
        and k.n < len(a.Text) /* not necessary but optimizer might use it??? */

http://sqlfiddle.com/#!6/a0f87/4输出一些额外的列

这只会处理大约一千个字符长的字符串。我怀疑这可能已经足够了,如果这很慢,您甚至可能想要缩小搜索长度。

我假设您的“Acc”后面会立即出现一个空格。值,这意味着它不会出现在行尾。必要时可以处理。

由于您看到错误,您的输入行似乎与您指定的格式不同。除了我提到的空格字符分隔符之外,我没有看到任何其他假设。

对于调试,您可以用此输出替换整个 substring() 行,以便更好地了解正在发生的事情。还要添加where 子句以将行限制为会导致错误的行:

select
    'Bad offset' as Msg,
    a.Text, k.n as StartOfAccBlick, charindex(' ', a.Text, k.n) as EndOfAccBlock
from ...    
where
    k.n - charindex(' ', a.Text, k.n) <= 0

【讨论】:

  • 它给了我一个类似Incorrect syntax near the keyword 'values'的错误信息
  • 我的错。修复了那个问题。我也修复了另一个列引用。
  • 我还在子串长度上修正了一个关闭。
  • Msg 537,级别 16,状态 5,行 1 传递给 LEFT 或 SUBSTRING 函数的长度参数无效。我对此有点困惑
  • 您收到我查询的最新版本了吗?我将 k.n 的值更改为 1 到 1000 而不是 0 到 999。这是解决该错误的一种方法。
【解决方案2】:

xml 节点方法可以方便地将单元格值解析为这样的行。例如:

select n.value('@s[1]', 'varchar(max)'), id, Cid, CName, Aid, AName
from Table_A ta
cross apply (select convert(xml, '<x s="' + replace(replace(replace(replace(replace(replace(ta.[Text],'&','&amp;'),'>','&gt;'),'<','&lt;'),'''','&apos;'),'"','&quot;'),' ','"/><x s="') + '"/>') xval) r
cross apply r.xval.nodes('*') x(n)
where n.value('@s[1]', 'varchar(max)') like 'Acc.%'

SqlFiddle

编辑 - 转义 5 个无效的 xml 字符

【讨论】:

  • Msg 9415, Level 16, State 1, Line 3 XML parsing: line 1, character 7, well formed check: no '&lt;' in attribute value 你能帮我解决这个错误吗
  • @Zack 对于那些你需要为 xml 转义它们的人,这是另一个问题的函数 - stackoverflow.com/a/17088055/99691
【解决方案3】:

您将需要使用递归 CTE 将每个出现的子串取出。我今天早上工作很忙,所以我无法创建与您的特定模式匹配的东西,但这是我过去做过的一个示例,用于从字符串中提取所有 PDF 文件名。所有注释掉的行都是为了显示我如何获得需要提取的 PDF 文件名的构建块。您将需要针对您的场景相应地修改模式搜索。这是SQL Fiddle

DECLARE @f TABLE (fieldName VARCHAR(255), IDField int)
INSERT INTO @f VALUES('>>>>>>1.pdf test> >b>c>xyz.pdf bob >hello world.pdf foo >womp womp.pdf>', 1)
INSERT INTO @f VALUES('>2.pdf other unnecssary stuff > bar.pdf', 2)

; WITH cte2 AS (
   SELECT 
       IDField,
       --PATINDEX('%.pdf%', fieldName) + 3 AS PDFLocation,
       --SUBSTRING(fieldName, 1, (PATINDEX('%.pdf%', fieldName) + 3)) AS PDFSubstring,
       --REVERSE(SUBSTRING(fieldName, 1, (PATINDEX('%.pdf%', fieldName) + 3))) AS PDFSubstringReverse,
       --PATINDEX('%>%', REVERSE(SUBSTRING(fieldName, 1, (PATINDEX('%.pdf%', fieldName) + 3)))) AS ReverseSymbolLocationBeforePDF,
       --LEN(SUBSTRING(fieldName, 1, (PATINDEX('%.pdf%', fieldName) + 3))) - PATINDEX('%>%', REVERSE(SUBSTRING(fieldName, 1, (PATINDEX('%.pdf%', fieldName) + 3)))) + 2 AS SymbolLocationBeforePDF,

       CONVERT(VARCHAR(255), SUBSTRING(fieldName, 
           LEN(SUBSTRING(fieldName, 1, (PATINDEX('%.pdf%', fieldName) + 3))) - PATINDEX('%>%', REVERSE(SUBSTRING(fieldName, 1, (PATINDEX('%.pdf%', fieldName) + 3)))) + 2, 
           PATINDEX('%.pdf%', fieldName) + 3 - (LEN(SUBSTRING(fieldName, 1, (PATINDEX('%.pdf%', fieldName) + 3))) - PATINDEX('%>%', REVERSE(SUBSTRING(fieldName, 1, (PATINDEX('%.pdf%', fieldName) + 3)))) + 2) + 1
       )) AS PDFName,

       CONVERT(VARCHAR(255), STUFF(fieldName, 1, PATINDEX('%.pdf%', fieldName) + 3, '')) AS strWhatsLeft
   FROM @f

   UNION ALL

   SELECT 
       IDField,
       --PATINDEX('%.pdf%', strWhatsLeft) + 3 AS PDFLocation,
       --SUBSTRING(strWhatsLeft, 1, (PATINDEX('%.pdf%', strWhatsLeft) + 3)) AS PDFSubstring,
       --REVERSE(SUBSTRING(strWhatsLeft, 1, (PATINDEX('%.pdf%', strWhatsLeft) + 3))) AS PDFSubstringReverse,
       --PATINDEX('%>%', REVERSE(SUBSTRING(strWhatsLeft, 1, (PATINDEX('%.pdf%', strWhatsLeft) + 3)))) AS ReverseSymbolLocationBeforePDF,
       --LEN(SUBSTRING(strWhatsLeft, 1, (PATINDEX('%.pdf%', strWhatsLeft) + 3))) - PATINDEX('%>%', REVERSE(SUBSTRING(strWhatsLeft, 1, (PATINDEX('%.pdf%', strWhatsLeft) + 3)))) + 2 AS SymbolLocationBeforePDF,

       CONVERT(VARCHAR(255), SUBSTRING(strWhatsLeft, 
           LEN(SUBSTRING(strWhatsLeft, 1, (PATINDEX('%.pdf%', strWhatsLeft) + 3))) - PATINDEX('%>%', REVERSE(SUBSTRING(strWhatsLeft, 1, (PATINDEX('%.pdf%', strWhatsLeft) + 3)))) + 2, 
           PATINDEX('%.pdf%', strWhatsLeft) + 3 - (LEN(SUBSTRING(strWhatsLeft, 1, (PATINDEX('%.pdf%', strWhatsLeft) + 3))) - PATINDEX('%>%', REVERSE(SUBSTRING(strWhatsLeft, 1, (PATINDEX('%.pdf%', strWhatsLeft) + 3)))) + 2) + 1
       )) AS PDFName,

       CONVERT(VARCHAR(255), STUFF(strWhatsLeft, 1, PATINDEX('%.pdf%', strWhatsLeft) + 3, '')) AS strWhatsLeft
   FROM cte2
   WHERE strWhatsLeft LIKE '%.pdf%'
)

SELECT * FROM cte2 ORDER BY IDField

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-14
    • 2014-08-18
    • 1970-01-01
    • 2018-07-18
    相关资源
    最近更新 更多