Синтаксический анализ строки SQL Server в поле varchar - PullRequest
6 голосов
/ 10 октября 2009

У меня есть столбец varchar в таблице, которая используется для хранения данных XML. Да, я знаю, что есть тип данных xml, который я должен использовать, но я думаю, что он был настроен до того, как тип данных xml был доступен, поэтому сейчас мне нужно использовать varchar. :)

Сохраненные данные выглядят примерно так:

<xml filename="100100_456_484351864768.zip"  
     event_dt="10/5/2009 11:42:52 AM">
    <info user="TestUser" />
</xml>

Мне нужно разобрать имя файла, чтобы получить цифры между двумя подчеркиваниями, которые в этом случае были бы "456". Первая часть имени файла «не должна» меняться по длине, но среднее число будет. Мне нужно решение, которое будет работать, если первая часть действительно изменится по длине (вы знаете, что она изменится, потому что «не должно меняться» всегда означает, что она изменится).

Для того, что у меня сейчас есть, я использую XQuery, чтобы вытащить имя файла, потому что я подумал, что это, вероятно, лучше, чем манипуляции с прямыми строками. Для этого я привел строку в xml, но я не эксперт по XQuery, поэтому, конечно, у меня возникают проблемы. Я нашел функцию для XQuery (substring-before), но не смог заставить ее работать (я даже не уверен, что функция будет работать с SQL Server). Может быть, есть функция XQuery, чтобы сделать это легко, но если она есть, я не знаю об этом.

Итак, я получаю имя файла из таблицы с помощью запроса, подобного следующему:

select CAST(parms as xml).query('data(/xml/@filename)') as p
from Table1

Исходя из этого, я предполагаю, что я смогу CAST это обратно в строку, а затем выполнить некоторую функцию instring или charindex, чтобы выяснить, где находятся подчеркивания, чтобы я мог инкапсулировать все это в функцию подстроки, чтобы выбрать часть, в которой я нуждаюсь. Не вдаваясь слишком далеко в это, я почти уверен, что смогу сделать это таким образом, но я знаю, что должен быть более легкий путь. Таким образом, в операторе SQL появилось бы огромное нечитаемое поле, которое, даже если бы я переместил его в функцию, все равно не смогло бы понять, что происходит.

Я уверен, что это проще, так как это кажется простой обработкой строк. Возможно, кто-то может указать мне правильное направление. Спасибо

Ответы [ 3 ]

5 голосов
/ 10 октября 2009

Вы можете использовать XQuery для этого - просто измените ваше утверждение на:

SELECT
   CAST(parms as xml).value('(/xml/@filename)[1]', 'varchar(260)') as p
FROM 
   dbo.Table1

Это дает вам VARCHAR (260) достаточно долго, чтобы содержать любое допустимое имя файла и путь - теперь у вас есть строка и вы можете работать с ней с помощью SUBSTRING и т. Д.

Марк

4 голосов
/ 10 октября 2009

Простой способ сделать это с помощью SUBSTRING и CHARINDEX. Предполагая (мудро или нет), что первая часть имени файла не меняет длину, но вы все еще хотите использовать XQuery для поиска имени файла, вот короткое повторение, которое делает то, что вы хотите:

declare @t table (
  parms varchar(max)
);
insert into @t values ('<xml filename="100100_456_484351864768.zip" event_dt="10/5/2009 11:42:52 AM"><info user="TestUser" /></xml>');

with T(fName) as (
  select cast(cast(parms as xml).query('data(/xml/@filename)') as varchar(100)) as p
  from @t
)
  select
    substring(fName,8,charindex('_',fName,8)-8) as myNum
  from T;

Существуют хитрые решения, в которых используются другие строковые функции, такие как REPLACE и PARSENAME или REVERSE, но ни одна из них не может быть более эффективной или удобочитаемой. Одной из возможностей для рассмотрения является написание подпрограммы CLR, которая переносит обработку регулярных выражений в SQL.

Кстати, если ваш xml всегда такой простой, я не вижу особой причины использовать XQuery вообще. Вот два запроса, которые извлекут желаемое число. Второе безопаснее, если у вас нет контроля над лишним пробелом в строке xml или существует вероятность того, что длина первой части имени файла изменится:

  select
    substring(parms,23,charindex('_',parms,23)-23) as myNum
  from @t;

  select
    substring(parms,charindex('_',parms)+1,charindex('_',parms,charindex('_',parms)+1)-charindex('_',parms)-1) as myNum
  from @t;
1 голос
/ 10 октября 2009

К сожалению, SQL Server не является совместимой реализацией XQuery - скорее, это довольно ограниченное подмножество черновой версии спецификации XQuery. Мало того, что у него нет fn:substring-before, у него также нет fn:index-of, чтобы сделать это самостоятельно, используя fn:substring или fn:string-to-codepoints. Итак, насколько я могу судить, вы застряли здесь с SQL.

...