Регулярні вирази полегшують пошук і вибірку даних

Зміст



Хоча T-SQL є виключно потужним мовою обробки даних, він погано пристосований для аналізу тексту і маніпуляцій з ним. Спроби проведення аналізу скільки-небудь складного тексту за допомогою вбудованих строкових функцій призводять до необхідності включення в код надмірно великої кількості цих функцій і процедур, внаслідок чого код стає важко налагоджувати і підтримувати. Але, може бути, існує більш відповідний шлях?


Насправді набагато ефективніше і елегантне рішення пропонують регулярні вирази. Легко переконатися в тому, як вони корисні при порівнянні тексту для ідентифікації записів, хоча при цьому вони здатні на набагато більше. Я покажу, як вирішувати різноманітні завдання, для яких кошти SQL Server 2000 вважалися незручними або непридатними, а тепер з появою SQL Server 2005 стали можливими завдяки підтримки хостингу CLR.


Регулярні вирази для SQL – не новина. Компанія Oracle вбудувала регулярні вирази ще в базу даних 10g, і багато рішень для баз даних з відкритим вихідним кодом також використовують різні бібліотеки регулярних виразів. По суті, регулярні вирази могли б застосовуватися і в більш ранніх версіях SQL Server, але механізм їх обробки був неефективний.


За допомогою збереженої процедури sp_OACreate можна задіяти будь-який об'єкт OLE Automation, який реалізує регулярні вираження, але спочатку потрібно створити COM-об'єкт, потім зробити мінімум один виклик IDispatch і знищити цей об'єкт. Для більшості задач це абсолютно неефективно і було причиною багатьох проблем з ресурсами. Єдиною альтернативою було створення розширеної збереженої процедури. Проте сьогодні існує SQLCLR, призначена для користувача CLR-функція, що дозволяє створювати ефективний і менш схильний до помилок набір функцій на основі Microsoft. NET Framework.


Користувальницькі CLR-функції


Такі функції є просто статичними методами (загальними функціями в Visual Basic), визначеними всередині. NET-збірки. Щоб використовувати об'єкти SQLCLR, необхідно зареєструвати збірку в SQL Server новим оператором CREATE ASSEMBLY, а потім створювати кожен об'єкт, вказуючи на його реалізацію в збірці. Для підтримки користувальницьких CLR-функцій вдосконалено оператор CREATE FUNCTION. При використанні SQL Server Project середовище розробки Visual Studio 2005 бере на себе весь процес реєстрації. Цей тип проекту відрізняється від більшості проектів Visual Studio, оскільки при спробі налагодження (або запуску без налагодження) проект компілюється наново, і отримується збірка, як і всі визначені в ній об'єкти SQLCLR, розгортаються і реєструються в SQL Server. Потім інтегрована середовище розробки запускає тестовий сценарій, призначений для даного проекту. Для спрощення налагодження в SQL-сценарій і в. NET-код можна ставити крапки переривання.


Додавання функції аналогічно додаванню нового класу до проекту будь-якого іншого типу. Ви просто включаєте в проект новий елемент у відповідь на запит вибираєте User-Defined Function (UDF). Новий метод додається до часткового класу, в якому містяться всі призначені для користувача функції. До нового методу також застосовується атрибут SqlFunction. Він використовується середовищем Visual Studio для створення SQL-операторів, необхідних при реєстрації функції. Поля IsDeterministic, IsPrecise, DataAccess і SystemDataAccess атрибуту SqlFunction використовуються SQL Server.


Пошук по шаблонах


Визначити, чи відповідає рядок шаблоном, – найпростіше застосування регулярних виразів і, як видно в лістингу 1, Реалізується дуже легко.


Лістинг 1. Пошук рядка за шаблоном






public static partial class UserDefinedFunctions
{
public static readonly RegexOptions Options =
RegexOptions.IgnorePatternWhitespace /
RegexOptions.Singleline;

[SqlFunction]
public static SqlBoolean RegexMatch(
SqlChars input, SqlString pattern)
{
Regex regex = new Regex( pattern.Value, Options );
return regex.IsMatch( new string( input.Value ) );
}
}


Спочатку я використовую поле Options для збереження параметрів регулярного вираження стосовно до функцій. У даному випадку я вибрав RegexOptions.SingleLine і RegexOptions.IgnorePatternWhitespace. Перший з них встановлює однорядковий режим, а другий виключає прогалини, не помічені Escape-символом, з регулярного виразу і дозволяє відзначати коментарі знаком #. Інший параметр, який вам, можливо, знадобиться після довгих роздумів, – RegexOption.Compiled. Цей параметр при інтенсивному використанні регулярних виразів, поки їх кількість не занадто велика, дає помітний приріст продуктивності. Компілювати слід вираження, застосовувані багаторазово. Але не використовуйте параметр Compiled з регулярними виразами, до яких ви звертаєтеся лише зрідка, оскільки це призведе до витрат і використанню зайвої пам'яті. Ймовірно, ви захочете доповнити мою універсальну функцію RegexMatch ще одним параметром, що визначає, чи треба компілювати даний вираз; таким чином, в кожному конкретному випадку можна було б вирішувати, наскільки виправдані витрати заради збільшення продуктивності.


Після визначення настройок RegexOptions я визначаю функцію RegexMatch, використовуючи тип даних SqlChars замість SqlString. Тип даних SqlString перетворюється в тип nvarchar (4,000), тоді як SqlChars перетвориться в тип nvarchar (max). Нова можливість завдання максимального розміру допускає роботу з рядками, довжина яких перевищує межу для SQL Server 2000, рівний 8000 байт. Для більшої гнучкості nvarchar (max) у цій статті використовується в як можна більш узагальненому вигляді. Однак, якщо відповідні рядки містять менше 4000 символів, ефективність при використанні nvarchar (4,000) може бути набагато вище. Тому спочатку оцініть конкретні потреби і пишіть код з їх обліком.


Інша частина коду даного методу досить проста. Створюється примірник Regex з певними параметрами і заданим шаблоном, після чого для визначення, чи відповідає шаблоном введене значення, застосовується метод IsMatch. Тепер необхідно додати до тестового сценарієм простий запит:






 select dbo.RegexMatch (N "123-45-6789", N "^ d {3}-d {2}-d {4} $")

Шаблоном в цьому виразі є проста перевірка на номер в системі страхування США. Встановіть точку переривання в новому запиті і, запустивши відладчик, починайте покроково виконувати функцію. Ця функція дозволяє робити масу різних перевірок, але тут ми розглянемо лише деякі особливості, які багато хто не беруть до уваги. Наприклад, дуже важливо дотримуватися угоди про іменування, чинного в межах бази даних, а написання запиту для перевірки того, що всі процедури, що зберігаються задовольняють нормативам даної організації, – справа непроста. Функція RegexMatch значно спрощує це завдання. Наприклад, наступний тест запиту виконує це завдання:






select ROUTINE_NAME
from INFORMATION_SCHEMA.ROUTINES
where ROUTINE_ENGINE= N”PROCEDURE”
and dbo.RegexMatch( ROUTINE_NAME,
N”^usp_(Insert/Update/Delete/Select)([A-Z][a-z]+)+$” ) = 0

Цей запит перевіряє, чи кожна збережена процедура має префікс «usp_», за яким слід «Insert», «Update», «Delete» або «Select». Він також перевіряє, чи починається кожне слово в назві об'єкта з великої літери. Порівняйте ті рядки з цієї вкрай спрощеною версією, що використовує тільки вбудовані функції:






select ROUTINE_NAME
from INFORMATION_SCHEMA.ROUTINES
where ROUTINE_ENGINE= N”PROCEDURE”
and ( LEN( ROUTINE_NAME ) < 11
or LEFT( ROUTINE_NAME, 4 ) <> N”usp_”
or SUBSTRING( ROUTINE_NAME, 5, 6 ) not in
( N”Insert”, N”Update”, N”Delete”, N”Select” ) )

Хоча цей запит в більшій мірі є кодом, у ньому фактично не вистачає декількох функцій, присутніх у версії з регулярними виразами. По-перше, він нечутливий до регістру букв, і пошук по шаблоном може дати невірні результати. По-друге, він не перевіряє фактичне ім'я суті, міститься в імені процедури. По-третє, всі чотири рядки, тестовані у запиті, мають довжину в шість символів, що дозволило спростити код витяганням окремої підрядка довжиною у шість символів і використовувати її в усіх допустимих операціях порівняння. Це не проблема в даному прикладі, оскільки всі імена команд складаються з шести символів, але уявіть собі стандарт з більш складними командами на зразок Get, List або Find. Цими командами легко маніпулює функція RegexMatch, оскільки вони є лише додатковими варіантами в списку.


Перевірка значень на допустимість – дуже поширене застосування регулярних виразів у відношенні, наприклад, телефонних номерів, поштових індексів і т. д. Як показує наступне визначення таблиці, обмеження CHECK ідеально підходить для цієї мети:






CREATE TABLE [Account]
(
[AccountNumber] nvarchar(20) CHECK (dbo.RegexMatch(
[AccountNumber], “^[A-Z]{3,5}d{5}-d{3}$” ) = 1),
[PhoneNumber] nchar(13) CHECK (dbo.RegexMatch(
[PhoneNumber], “^(d{3})d{3}-d{4}$” ) = 1),
[ZipCode] nvarchar(10) CHECK (dbo.RegexMatch(
[ZipCode], “^d{5}(-d{4})?$” ) = 1)
)

Вміст стовпця AccountNumber перевіряється на відповідність безпідставного угоди про те, що воно повинно починатися з трьох-п'яти букв, за якими йдуть п'ять цифр, потім тире і ще три цифри. Як телефонні номери, так і поштові індекси перевіряються на відповідність стандартам США для форматів телефонних номерів та поштових індексів. Функція RegexMatch надає масу можливостей для SQL Server, але реалізація регулярних виразів в. NET, як буде показано, дає набагато більше.


Отримання даних


Для вилучення даних з рядка можуть використовуватися кошти групування регулярних виразів. Розроблена мною функція RegexGroup забезпечує цю можливість для T-SQL:






[SqlFunction]
public static SqlChars RegexGroup(
SqlChars input, SqlString pattern, SqlString name )
{
Regex regex = new Regex( pattern.Value, Options );
Match match = regex.Match( new string( input.Value ) );
return match.Success ?
new SqlChars( match.Groups[name.Value].Value ) :
SqlChars.Null;
}

Вона, як і функція RegexMatch, створює об'єкт Regex. Однак, замість того щоб проводити перевірку на відповідність, для першого ж знайденого у вхідний рядку збіги створюється об'єкт Match. Цей об'єкт використовується для вилучення зазначеної групи. Якщо у вхідний рядку відповідності не виявлено, функція повертає null. Функція буде працювати і в тому випадку, якщо замість іменованих груп ви віддасте перевагу використовувати нумеровані. Просто передавайте ціле значення функції в SQL-коді, і воно буде неявно наводитися до типу nvarchar, в результаті чого буде повертатися відповідна група.


Для вилучення одних фрагментів даних з інших можна використовувати функцію RegexGroup в списку SELECT. Наприклад, для поля, в якому міститься URL, можна легко провести його синтаксичний розбір для отримання окремих частин. Даний запит використовує групування для знаходження імен всіх серверів в полі Url таблиці UrlTable:






select distinct dbo.RegexGroup( [Url],
N”https?://(?<server>([w-]+.)*[w-]+)”, N”server” )
from [UrlTable]

Ця функція застосовна і в обчислюваних полях. Наступне визначення таблиці забезпечує поділ електронних адрес на поштові скриньки та домени:






CREATE TABLE [Email]
(
[Address] nvarchar(max),
[Mailbox] as dbo.RegexGroup( [Address],
N”(?<mailbox>[^@]*)@”, N”mailbox” ),
[Domain] as dbo.RegexGroup( [Address],
N”@(?<domain>.*)”, N”domain” )

Поле поштової скриньки буде повертати поштову скриньку або ім'я користувача цієї адреси, а поле домену – домен.


Сховище шаблонів


Всі шаблони (patterns), що використовуються цими функціями, – просто рядки, і кожна з них може зберігатися в таблиці бази даних. Більшість баз даних з інформацією міжнародного характеру має таблицю, представляє країни. Додавши до такої таблиці кілька додаткових полів, можна зберігати в ній шаблони перевірки на конкретну країну. Це дозволяє ввести обмеження для адресного рядка, автоматично змінюване в залежності від країни.


У базах даних, що зберігають інформацію про клієнтів, як правило, є таблиця, яка представляє клієнта. Таку таблицю можна використовувати для зберігання групуються шаблонів, що описують спосіб, за допомогою якого необроблені дані про клієнта зберігаються в базі даних; це дозволяє створити обчислювані поля для вибірки лише дійсно необхідних даних. Наприклад, якщо номери банківських рахунків клієнтів мають унікальну структуру і потрібні тільки окремі фрагменти номерів, можна легко створити вираз, що дозволяє отримати коректний фрагмент інформації з номера рахунку кожного клієнта.


Збіги


Замість того, щоб визначати, чи збігається рядок з шаблоном, іноді краще витягти кожне збіг. Раніше для цього було б курсори з переміщенням в циклі. Цей процес досить повільний, а сам код важкий для розуміння і підтримки. Регулярні вирази – набагато більш відповідне засіб для виконання такої операції. Головна проблема тут в тому, як повернути всі необхідні дані в рамках SQL-конструкції. Рішення – табличні функції.


Табличні функції почасти схожі на функції, описані вище, але мають дві істотні відмінності. По-перше, атрибути, застосовувані до методу, повинні повністю визначати структуру повертається таблиці. По-друге, в процесі беруть участь два методи. Перший – повертає перераховується об'єкт (enumerable object) замість фактичного результату функції. Другий передає перераховуються об'єкти для заповнення полів кожного рядка. Кожне значення, що отримується від перечіслітеля (enumerator), повинно відповідати одному рядку набору результатів. Інтерфейс ICollection ст. NET Framework реалізує IEnumerable, що означає можливість повернення будь-якого набору першим методом. Клас Regex містить метод Matches, який повертає об'єкт MatchCollection, який ви могли б використовувати. Проблема з цим об'єктом у тому, що вся рядок повинен бути оброблена до повернення управління з методу Matches. Через оптимізацій в SQL Server я віддаю перевагу писати власний перечіслітель, який повертає кожне збіг на вимогу замість того, щоб заздалегідь повертати весь набір. Насправді остаточне рішення слід приймати з урахуванням того, як використовується функція, і має бути ретельно перевірено до оптимізації перечіслітеля.


У лістингу 2 показаний код перечіслітеля. Клас MatchNode служить оболонкою для індивідуального збіги, знайденого в рядку, і паралельно відстежує його позицію в наборі повертаються збігів. Клас MatchIterator є перераховуються і відповідає за обробку регулярного виразу. Він використовує нове ключове слово yield, яке значно спрощує створення перечіслітеля. Кожне збіг, виявлене всередині рядки, він буде повертати на вимогу.


Лістинг 2. Власний перераховується об'єкт для збігів






internal class MatchNode
{
private int _index;
public int Index { get{ return _index; } }

private string _value;
public string Value { get { return _value; } }

public MatchNode( int index, string value )
{
_index = index;
_value = value;
}
}

internal class MatchIterator : IEnumerable
{
private Regex _regex;
private string _input;

public MatchIterator( string input, string pattern )
{
_regex = new Regex( pattern,
UserDefinedFunctions.Options );
_input = input;
}

public IEnumerator GetEnumerator()
{
int index = 0;
Match current = null;
do
{
current = (current == null) ?
_regex.Match( _input ) : current.NextMatch( );
if (current.Success)
{
yield return new MatchNode( ++index,
current.Value );
}
}
while (current.Success);
}
}


У коді лістингу 3 визначена таблична функція CLR UDF. Метод RegexMatches повертає новий MatchIterator. Арібут SqlFunctionAttribute методу RegexMatches включає кілька додаткових властивостей. У властивість TableDefinition поміщається визначення таблиці функції, у властивість FillRowMethodName – ім'я методу, який повинен викликатися на кожній ітерації повертається перечисляемого об'єкта. У даному випадку цей метод називається FillMatchRow.


Лістинг 3. Таблична функція CLR UDF для збігів






[SqlFunction( FillRowMethodName = “FillMatchRow”,
TableDefinition = “[Index] int,[Text] nvarchar(max)” )]
public static IEnumerable RegexMatches(SqlChars input,
SqlString pattern)
{
return new MatchIterator( new string( input.Value ),
pattern.Value );
}

[SuppressMessage( “Microsoft.Design”,
“CA1021:AvoidOutParameters” )]
public static void FillMatchRow( object data,
out SqlInt32 index, out SqlChars text )
{
MatchNode node = (MatchNode)data;
index = new SqlInt32( node.Index );
text = new SqlChars( node.Value.ToCharArray( ) );
}


На кожній ітерації MatchIterator об'єкт MatchNode передається методу FillMatchRow як перший аргумент. Інші параметри методу FillMatchRow повинні бути оголошені як вихідні і повинні відповідати опису таблиці, визначеної у першій функції. Функція FillMatchRow просто використовує властивості MatchNode для заповнення полів даними.


Нарешті, за допомогою цієї функції легко витягати з рядка різноманітні фрагменти даних. Щоб проілюструвати застосування функції RegexMatches, опрацюємо рядок за допомогою наведеного нижче запиту і визначимо, скільки різних слів в ній міститься:






declare @text nvarchar(max), @pattern nvarchar(max)
select
@text = N”Here are four words.”,
@pattern = “w+”
select count(distinct [Text])
from dbo.RegexMatches( @text, @pattern )

Наведений приклад досить прямолінійний. Він демонструє деякі можливості застосування даної функції, а якщо ви видалите ключове слово distinct, функція буде повертати кількість слів у рядку. На багатьох веб-сайтах введення тексту обмежують вельми довільній довжиною. За допомогою показаної мною перевірки в поєднанні з новою нотацією nvarchar (max) стає можливим обмеження введення певним числом слів. Цей тип запиту нагоді при виконанні різних завдань аналітичної обробки, але функцію RegexMatches можна використовувати і для більш загальних задач. На жаль, цей тип запиту також відображає зайво старанне використання регулярних виразів. Операцію розбиття, виконувану в даному випадку виразом "w +", можна було б легко виконати методом String.Split, який працює набагато швидше. Регулярні вирази – потужний засіб, але, перш ніж їх застосовувати, слід розібратися, наскільки це виправдано.


На форумах MSDN я часто бачу питання про те, як передавати список значень в збережену процедуру. Крім того, мені доводилося зустрічати різні заплутані способи розбору такого списку для ідентифікації відповідних записів. Повинен сказати, що функція RegexMatches дозволяє робити це набагато акуратніше і зрозуміліше:






declare @pattern nvarchar(max), @list nvarchar(max)
select @pattern = N”[^,]+”, @list = N”2,4,6″

select d.* from [Data] d
inner join dbo.RegexMatches( @list, @pattern ) re
on d.[ID] = re.[Text]


Цей шаблон співпадає з будь-якою групою символів, що не містить коми. Для таблиці з ім'ям Data, яка містить цілочисельне поле ID, цей запит буде повертати кожну знайдену в списку запис. Його користь стає ще очевидніше, якщо врахувати неявне приведення типів в SQL Server. Один і той самий запит можна використовувати для цілочислових даних, для «дати / час», GUID або типів даних з плаваючою точкою. Інші способи обробки списку значень зажадали б застосування безлічі функцій або ускладнення збережених процедур. Ця функція застосовна і для списків, де в якості роздільника кома не використовується. Вона дозволяє обробляти списки, колективні пробілами, крапками з комою, табуляторами, символами "повернення каретки» або будь-якими іншими ідентифікованими символами.


Вилучення даних з збігів


За аналогією з поверненням збігів ви можете витягувати дані з кожного збіги. Використати для цього SQL досить важко. Зазвичай цей вид задач виконується додатками, а не СУБД, що викликає певні проблеми, тому що кожен додаток, що використовує базу даних, має реалізувати відповідну обробку. У таких випадках розумним є підхід, що дозволяє домогтися необхідної функціональності за допомогою збережених процедур.


Як і з реалізацією RegexMatches, я вважаю за краще отримувати інформацію про групи через власний перераховується об'єкт. Групування лише трохи складніше, оскільки в межах кожного збігу потрібно виконувати ітерації по групах. У лістингу 4 клас GroupNode виглядає майже так само, як клас MatchNode, за винятком того, що він включає і ім'я групи, яку представляє. Клас GroupIterator аналогічний класу MatchIterator, але включає додатковий цикл для повернення кожної групи. Тепер, коли у мене є що перераховується об'єкт, я можу точно так же визначити табличну функцію, як і для функції RegexMatches.


Лістинг 4. Власний перераховується об'єкт для груп






internal class GroupNode
{
private int _index;
public int Index { get { return _index; } }

private string _name;
public string Name { get { return _name; } }

private string _value;
public string Value { get { return _value; } }

public GroupNode( int index, string group, string value )
{
_index = index;
_name = group;
_value = value;
}
}

internal class GroupIterator : IEnumerable
{
private Regex _regex;
private string _input;

public GroupIterator( string input, string pattern )
{
_regex = new Regex( pattern,
UserDefinedFunctions.Options );
_input = input;
}

public IEnumerator GetEnumerator()
{
int index = 0;
Match current = null;
string[] names = _regex.GetGroupNames();
do
{
index++;
current = (current == null) ?
_regex.Match( _input ) : current.NextMatch( );
if (current.Success)
{
foreach(string name in names)
{
Group group = current.Groups[name];
if (group.Success)
{
yield return new GroupNode(
index, name, group.Value );
}
}
}
}
while(current.Success);
}
}


У лістингу 5 функція RegexGroups визначається аналогічно RegexMatches за винятком того, що вона повертає додаткове поле даних, що містить ім'я групи всередині збіги. Тепер за допомогою цієї функції можна знаходити в межах рядка різні збіги і витягувати з кожного збігу потрібні фрагменти інформації.


Лістинг 5. Таблична UDF-функція для груп






[SqlFunction( FillRowMethodName = “FillGroupRow”,
TableDefinition = “[Index] int, [Group] nvarchar(max),
[Text] nvarchar(max)” )]
public static IEnumerable
RegexGroups( SqlChars input, SqlString pattern )
{
return new GroupIterator( new string( input.Value ),
pattern.Value );
}

[SuppressMessage( “Microsoft.Design”,
“CA1021:AvoidOutParameters” )]
public static void FillGroupRow( object data,
out SqlInt32 index, out SqlChars group, out SqlChars text )
{
GroupNode node = (GroupNode)data;
index = new SqlInt32( node.Index );
group = new SqlChars( node.Name.ToCharArray( ) );
text = new SqlChars( node.Value.ToCharArray( ) );
}


Імпорт даних в різних форматах при роботі з базами даних – задача досить поширена. Імпорт файлів з форматом, де роздільниками є коми, є набагато більш обтяжлива справу, ніж варто було б. Більшість розробників в таких випадках створює програму, яка обробляє кожен рядок, витягує з неї дані і для кожного рядка запускає збережену процедуру. І хоча цей процес працює, я б запропонував інше рішення. Що якщо б ми могли передати весь файл збереженій процедурі і дозволити їй керувати всім процесом? Ця ідея зазвичай вважається занадто складною в реалізації, але завдяки функції RegexGroups все можна зробити фактично в рамках одного запиту. Розглянемо, наприклад, такі дані про клієнтів:






2309478,Janet Leverling,J
2039748,Nancy Davolio,N
0798124,Andrew Fuller,M
4027392,Robert King,L

З кожного рядка треба отримати три фрагменти інформації: семизначний номер клієнта, прізвище і тип клієнта, що вказується однією буквою. Всі три фрагменти можна витягти за допомогою виразу:






(?<CustomerNumber>d{7}),(?<CustomerName>[^,]*),
(?<CustomerType>[A-Z])
?

Тепер виникає проблема, яка полягає в тому, що результати, повернуті функцією RegexGroups, не можна використовувати безпосередньо. Замість курсора для проходу за результатами можна задіяти функціональність SQL Server 2005. Уклавши все це в збережену процедуру, ми отримаємо те, що треба. Процедура, що зберігається в лістингу 6 приймає текстовий файл з комами як роздільники, що містить до 2 Гб даних в кодуванні Unicode. Вона обробляє файл цілком і кожен рядок цього файлу вставляє в таблицю Customer. Таким же чином може бути оброблений будь-який текстовий файл із розділювачами. Внісши в шаблон деякі несуттєві зміни, ви могли б додати керуючі послідовності для підтримки ком в рядках.


Лістинг 6. Обробка файлу з комами як роздільники






create proc ImportCustomers
(
@file nvarchar(max)
)
as

declare @pattern nvarchar(max)

set @pattern = N”(?<CustomerNumber>d{7}),
(? <CustomerName> >[^,]*),(?< CustomerType> [AZ])
?

insert [Customer]
(
[CustomerNumber],
[CustomerName],
[CustomerType]
)
select
f.[CustomerNumber],
f.[CustomerName],
f.[CustomerType]
from dbo.RegExGroups( @file, @pattern ) regex
pivot
(
max([Text])
for [Group]
in ( [CustomerNumber], [CustomerName], [CustomerType] )
) as f


І знову ця процедура демонструє можливість вирішувати одну й ту ж задачу різними способами, причому не завжди регулярні вирази є кращим вибором. У наведеному прикладі використання зведеної таблиці фактично скасовує всю роботу, яку виконувала функція RegexGroups для повернення даних в спеціальному групують форматі. Можна було б вставляти дані безпосередньо в таблицю, застосовуючи набагато простішу і швидку табличну функцію, просто прочитує кожен рядок, метод String.Split (орієнтуючись на коми) і повертати кожний рядок.


Висновок


Хоча функції пошуку збігів дуже потужні, вони недостатньо повні. Є маса варіантів пошуку збігів. Якщо база даних нечутлива до регістру літер, можуть знадобитися функції, які забезпечують знаходження збігів і без урахування регістра. Для зменшення набору результатів може знадобитися критерій відбору. Багаторядковий режим дозволяє для деяких завдань створювати більш точні шаблони пошуку. Можна навіть створити власний тип для передачі відповідних «настроювальних» значень кожної з функцій, щоб при викликах вони використовували потрібний вам набір параметрів.


Пам'ятайте і про те, що при обробці тексту можуть виникати деякі проблеми, пов'язані з локалізацією. Наприклад, клас Regex з. NET Framework розпізнає набагато більше букв, ніж входить в латиницю, тому слід бути уважнішими при розробці шаблонів для глобальних баз даних.


І, звичайно ж, як неодноразово згадувалося в даній статті, оскільки регулярні вирази – надзвичайно потужний інструмент, слід чітко уявляти, наскільки необхідна його міць для вирішення поставлених перед вами завдань. Деякі завдання можна вирішити швидше, використовуючи більш прості засоби.


У наведених прикладах для спрощення картини опущені блоки коду, необхідні для верифікації даних і обробки помилок. Всі вхідні параметри функції повинні перевірятися на допустимість, а для реакції на нульові або порожні значення вводяться рядків повинні бути розроблені відповідні правила. Клас Regex може генерувати виключення, якщо не вдається розібрати шаблон або якщо передані недопустимі параметри. Такі винятки повинні коректно оброблятися.


Комбінування регулярних виразів з можливостями SQL обумовлює безліч альтернативних варіантів обробки даних. Застосування цих функцій може скоротити час, необхідний для розширення функціональності бази даних, а також зробити її більш зручною у супроводі. Регулярні вирази застосовні в будь-якій базі даних, і я раджу поекспериментувати з ними.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*