Извлекать числа из строки, где бы она ни появлялась - PullRequest
0 голосов
/ 11 марта 2020

С примерами, приведенными ниже, как вы могли бы извлечь числа, поскольку SourceID и DestinationID, учитывая, что «Состояние изменено с X на X», могут появляться в любом месте текста?

Create Table #temp
(
    LogValue varchar(100)
)

insert into #temp
(
    LogValue
)
select
    'Status changed from 1 to 10' --SourceID = 1, DestinationID = 10
union all
select
    'Mary had a little lamb Status changed from 4 to 12'--SourceID = 4, DestinationID = 12
union all
select
    'Mary had a little lamb Status changed from 5 to 17 Its fleece was white as snow,' --SourceID = 5, DestinationID = 17
union all
select
    'And every where that Mary went Status changed from 2 to 8 45 Address Street'  --SourceID = 2, DestinationID = 8

Ответы [ 4 ]

2 голосов
/ 11 марта 2020

Без какой-либо функции или внешнего RegEx вы можете пройти этот маршрут:

;WITH cte AS
(
    SELECT *
           ,CAST(CONCAT('<x><y>',REPLACE(REPLACE(LogValue,' to ','</y><y>'),' ','</y></x><x><y>'),'</y></x>') AS XML) AS CastedToXml
    FROM #temp
)
SELECT cte.*
      ,numberY.value('/y[1]','int') AS SourceID
      ,numberY.value('/y[2]','int') AS DestinationID
FROM cte
CROSS APPLY (SELECT CastedToXml.query('/x[not(empty(y[1] cast as xs:int?)) 
                                      and not(empty(y[2] cast as xs:int?))]/y')) A(numberY);

Идея вкратце:

  • Мы используем некоторые замены для преобразования вашей строки в XML (особенно ' to ')

Нажмите одну из XML -гиперссылок, чтобы увидеть промежуточную XML.

<x>
  <y>Status</y>
</x>
<x>
  <y>changed</y>
</x>
<x>
  <y>from</y>
</x>
<x>
  <y>1</y>
  <y>10</y>
</x>
  • Будет APPLY используйте .query() для выбора каждого <x>, где мы находим два <y> с возможностью преобразования в int.
  • Теперь мы можем выбрать первое и второе, чтобы вернуть ваши значения.

ОБНОВЛЕНИЕ

Использование этого в cte добавило бы атрибут маркировки, когда from или to было до числа:

,CAST(CONCAT('<x><y>',REPLACE(REPLACE(REPLACE(REPLACE(LogValue,' from ',' to '),' to ','</y><y*>'),' ','</y></x><x><y>'),'<y*>','<y takeThis="true">'),'</y></x>') AS XML) AS CastedToXml

Промежуточный XML был бы тогда выглядит так
( внимание: Я изменил входные данные с шум , например from Vienna to London или числа в других местах):

<x>
  <y>Mary</y>
  <y takeThis="true">vienna</y>
  <y takeThis="true">london</y>
</x>
<x>
  <y>had</y>
  <y takeThis="true">3</y>
</x>
<!-- shortened for brevity -->
<x>
  <y>changed</y>
  <y takeThis="true">4</y>
  <y takeThis="true">12</y>
</x>

Это позволяет избежать ложных срабатываний (например, два числа в адресе)

Довольно толерантный запрос lly:

;WITH cte AS 
(
    SELECT *
           ,CAST(CONCAT('<x><y>',REPLACE(REPLACE(REPLACE(REPLACE(LogValue,' from ',' to '),' to ','</y><y*>'),' ','</y></x><x><y>'),'<y*>','<y takeThis="true">'),'</y></x>') AS XML) AS CastedToXml
    FROM #temp
)
SELECT cte.*
      ,numberY.value('/y[1]','int') AS SourceID
      ,numberY.value('/y[2]','int') AS DestinationID
FROM cte
CROSS APPLY (SELECT CastedToXml.query('for $x in /x[count(y[@takeThis="true"])=2]
                                       for $y in $x/y 
                                       return if(not(empty($y cast as xs:int?))) then $y else null')) A(numberY);

Он использует XQuery-FLWOR, чтобы уменьшить набор до нужных вам фигур. Это поможет справиться с нарушением других чисел в вашей строке.

Фильтр будет искать элементы <x>, где есть два элемента <y> с атрибутом takeThis="true, и будет возвращать эти <y> элементы, только если преобразуются в int.

0 голосов
/ 11 марта 2020

Вы также можете использовать CROSS APPLY для извлечения необходимых значений.

select PARSENAME(SD.VAL,3) AS SOURCE,PARSENAME(SD.VAL,1) AS DESTINATION from #temp
CROSS APPLY (VALUES(CHARINDEX(' from ',LogValue))) FRM(LOC) -- extract the  position of 'from'
CROSS APPLY (VALUES(CHARINDEX(' to ',LogValue,FRM.LOC+1))) TOO(LOC) --extract the position of 'to' after 'from'
CROSS APPLY (VALUES(CHARINDEX(' ',LogValue,TOO.LOC+4))) SPC(LOC) -- extract the position of space 2nd space after 'to'
CROSS APPLY (VALUES(TRIM(SUBSTRING(LogValue,FRM.LOC, CASE WHEN SPC.LOC!=0 THEN SPC.LOC-FRM.LOC
                                                     ELSE LEN(LogValue)-FRM.LOC+1 END )))) FT(val)  --extract the words from 'from' to after value of 'to'
CROSS APPLY (VALUES(REPLACE(FT.VAL,' ','.'))) SD(val) -- replacing the space with dot to use PARSENAME

Проверьте демонстрацию здесь

Выход

enter image description here

0 голосов
/ 11 марта 2020

Вы можете потратить некоторое время на реализацию SQL функций CLR для выполнения операций с регулярными выражениями ( String Utility создана Microsoft и предоставляет множество полезных функций).

Например, вы можете использовать функцию соответствия регулярному выражению, чтобы получить первое и второе вхождение числа, а затем повернуть его:

SELECT [LogValue]
      ,[0] AS [SourceID]
      ,[1] AS [DestinationID]
FROM
(
    SELECT LogValue
          ,RM.[MatchID]
          ,RM.[CaptureValue]
    FROM #temp T
    CROSS APPLY [dbo].[fn_Utils_RegexMatches] (LogValue,  '\d+') RM
) DS
PIVOT
(
    MAX([CaptureValue]) FOR [MatchID] IN ([0], [1])
) PVT;

enter image description here

0 голосов
/ 11 марта 2020

Вы можете использовать это: Не самый последний код

select 
substring(LogValue, charindex('from',LogValue) + len('from'), charindex('to',LogValue) - charindex('from',LogValue) - len('from')) as SourceID,

case when charindex(' ',right(LogValue, len(LogValue) - (charindex('to',LogValue) + len('to')))) > 0 then
    left(right(LogValue, len(LogValue) - (charindex('to',LogValue) + len('to'))), charindex(' ',right(LogValue, len(LogValue) - (charindex('to',LogValue) + len('to')))))
else
    right(LogValue, len(LogValue) - (charindex('to',LogValue) + len('to')))
end as DestinationID 
from #temp

Вывод:

SourceID    DestinationID
 1            10
 4            12
 5            17 
 2            8 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...