你能从SQL中的长varchar中提取特定的文本字符串吗?

时间:2017-05-04 17:40:07

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

我有一个syslog服务器,它将事件作为varchar输入到SQL Server数据库中。数据看起来像这样

Apr 27 22:03:38 ServerName MSWinEventLog 3 Application 4217 Thu Apr 27 22:03:30 2017 1009 MSExchangeHM  N/A Error ServerName 2 Microsoft Exchange Health ...

我正在尝试进行计数或任何唯一错误ID出现的次数。错误ID始终由" 2017"在这种情况下,代码是1009.我试图找到一种方法来搜索该代码,或输出"的第一个实例后的7行。 2017"在varchar

我是一个非常新的SQL,并且有一个很好的改变我缺少一些可以使这更容易的知识,但这是我的方法,到目前为止。

SELECT 
    COUNT([key]) AS CountofErrors,
    MAX(MSGTEXT) AS FULLMessage,
    CASE
       WHEN (CASE 
                WHEN LEFT(RIGHT(MSGTEXT, LEN(MSGTEXT) - 51), 12) LIKE 'a%'
                   THEN LEFT(RIGHT(MSGTEXT, LEN(MSGTEXT) - 51), 11)
                   ELSE RIGHT(LEFT(RIGHT(MSGTEXT, LEN(MSGTEXT) - 51), 12), 11) 
             END) LIKE 's%'
          THEN LEFT(LEFT(RIGHT(MSGTEXT, LEN(MSGTEXT) - 52), 12), 6)  
          ELSE (CASE
                   WHEN LEFT(RIGHT(MSGTEXT, LEN(MSGTEXT) - 51), 12) LIKE 'a%'
                      THEN LEFT(RIGHT(MSGTEXT, LEN(MSGTEXT) - 51), 11)
                      ELSE RIGHT(LEFT(RIGHT(MSGTEXT, LEN(MSGTEXT) - 51), 12), 11) 
                END) 
       END AS system2
FROM 
    SyslogDatabase
WHERE
    ...
GROUP BY
    ...

这可以确定事件是应用程序错误还是系统错误,因为这将指示在事件ID之前将有多少个字符。从这一点开始,我可以看到这变得非常混乱,因为应用程序或系统后面的数字可能是1到5位数。我希望有更好的方法来做到这一点。

2 个答案:

答案 0 :(得分:0)

在SQL Server 2016+中,您可以使用string_split()

在2016年之前的SQL Server中,使用Jeff Moden的CSV Splitter表值函数:

create table t (id int not null identity(1,1), msgtext varchar(8000))
insert into t values 
('Apr 27 22:03:38 ServerName MSWinEventLog 3 Application 4217 Thu Apr 27 22:03:30 
 2017 1009 MSExchangeHM  N/A Error ServerName 2 Microsoft Exchange Health ...
 2017 1009 MSExchangeHM  N/A Error ServerName 2 Microsoft Exchange Health ...
 2017 1010 MSExchangeHM  N/A Error ServerName 2 Microsoft Exchange Health ...')

select 
    t.id
  , s.ItemNumber
  , ErrorId = left(s.Item,charindex(' ',s.Item+' ')-1)
  , s.Item
from t
  cross apply dbo.delimitedsplit8k(replace(t.msgtext,'2017 ',char(30)),char(30)) s
where s.Item like '[0-9]%'

rextester 演示http://rextester.com/LVS48443

返回:

+----+------------+---------+-------------------------------------------------------------------------+
| id | ItemNumber | ErrorId |                                  Item                                   |
+----+------------+---------+-------------------------------------------------------------------------+
|  1 |          2 |    1009 | 1009 MSExchangeHM  N/A Error ServerName 2 Microsoft Exchange Health ... |
|  1 |          3 |    1009 | 1009 MSExchangeHM  N/A Error ServerName 2 Microsoft Exchange Health ... |
|  1 |          4 |    1010 | 1010 MSExchangeHM  N/A Error ServerName 2 Microsoft Exchange Health ... |
+----+------------+---------+-------------------------------------------------------------------------+

拆分字符串参考:

如果您需要支持最多一百万个字符的varchar(max)输入(此修改会降低性能),您可以使用Aaron Bertrand的替代品(添加ItemNumber)或其他字符串拆分选项在上面的参考文献中:

create function dbo.SplitStrings_Moden (@list varchar(max), @delimiter varchar(255))
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),
       e42(n)       as (select 1 from e4 a, e2 b),
       ctetally(n)  as (select 0 union all select top (datalength(isnull(@list,1))) 
                         row_number() over (order by (select null)) from e42),
       ctestart(n1) as (select t.n+1 from ctetally t
                         where (substring(@list,t.n,1) = @delimiter or t.n = 0))
  select 
      ItemNumber = row_number() over(order by s.n1)
    , Item = substring(@list, s.n1, isnull(nullif(charindex(@delimiter,@list,s.n1),0)-s.n1,8000))
  from ctestart s;
go

答案 1 :(得分:0)


1-让我们首先控制捕获算法逻辑:

- 我们需要搜索" ** 的格式:** 2017"或者当年的任何一个。并抓住下一个空间位置之间的下一个数字" 1009"

- 使用每个会话的数字(每日,每小时或任何会话定义)在表中插入不同的收集的错误号码。

2-让我们再考虑使用替代方法简化代码。

- Sql Cursor:首先阅读它,然后创建存储过程并对其进行编程以选择每一行并对其进行字符串操作,在这种情况下,你有一个进行字符串验证的功能并且在确定所选文本上的错误编号之前对所选文本进行任何检查(通过上一点的想法)。

- Linq Lambda用于字符串操作:
使用.net代码循环代码并使用.ToArray()或.ToList()来收集结果。


如果我为你安排了一些示例代码,我会尽快将其添加到此处,但是请继续阅读更多关于(Sql Cursor,Lambda表达式和字符串操作)的内容,这些代码会让您感动不已在接下来的大部分任务中都很快。