Шаблон регулярного виразу всередині функції SQL Замінити?


82
SELECT REPLACE('<strong>100</strong><b>.00 GB', '%^(^-?\d*\.{0,1}\d+$)%', '');

Я хочу замінити будь-яку розмітку між двома частинами числа вищевказаним регулярним виразом, але, схоже, це не працює. Я не впевнений, що неправильний синтаксис регулярних виразів, тому що я спробував більш простий, такий як '%[^0-9]%'просто перевірити, але він теж не спрацював. Хтось знає, як я можу цього досягти?


3
Ви можете переглянути відповідь.
Мукус,

1
Яким ви хочете, щоб був кінцевий результат? Ви очікуєте 100.00чи 100.00 GB? І чи існують інші приклади відформатованих чисел, які не відповідають шаблону розмітки, лише перебуваючи навколо частини ліворуч від десяткової коми? Чи може розмітка розміщуватися навколо всього числа, наприклад 100<i>.00</i> GB? Чи завжди праворуч є двозначний код валюти?
Соломон Руцкі

@srutzky Я хочу число з десятковими крапками, якщо такі є, їх мають не всі значення, також для них практично немає шаблону, оскільки він генерується, але сторонній генератор html. Іноді валюта стоїть попереду, іноді після номера, іноді це символ - $, іноді код - USD, з -без пробілів .. і т.д. тощо. просто дуже
смітні

Відповіді:


62

Ви можете використовувати PATINDEX, щоб знайти перший індекс появи шаблону (рядка). Потім за допомогою STUFF введіть ще один рядок у відповідний шаблон (рядок).

Повторіть кожен ряд. Замініть кожного незаконного символу тим, що ви хочете. У вашому випадку нечислові замініть на порожні. Внутрішній цикл - це якщо у поточній комірці цього циклу є більше одного незаконного символу.

DECLARE @counter int

SET @counter = 0

WHILE(@counter < (SELECT MAX(ID_COLUMN) FROM Table))
BEGIN  

    WHILE 1 = 1
    BEGIN
        DECLARE @RetVal varchar(50)

        SET @RetVal =  (SELECT Column = STUFF(Column, PATINDEX('%[^0-9.]%', Column),1, '')
        FROM Table
        WHERE ID_COLUMN = @counter)

        IF(@RetVal IS NOT NULL)       
          UPDATE Table SET
          Column = @RetVal
          WHERE ID_COLUMN = @counter
        ELSE
            break
    END

    SET @counter = @counter + 1
END

Увага: Це повільно! Наявність стовпця varchar може вплинути. Тож використання LTRIM RTRIM може трохи допомогти. Незалежно, це повільно.

Заслуга належить цій відповіді StackOverFlow.

EDIT Credit також надходить на @srutzky

Редагувати (від @Tmdean) Замість того, щоб робити по одному рядку за раз, цю відповідь можна адаптувати до рішення, яке базується на наборах. Він все ще повторює максимальну кількість нечислових символів в одному рядку, тому це не ідеально, але я вважаю, що це повинно бути прийнятним у більшості ситуацій.

WHILE 1 = 1 BEGIN
    WITH q AS
        (SELECT ID_Column, PATINDEX('%[^0-9.]%', Column) AS n
        FROM Table)
    UPDATE Table
    SET Column = STUFF(Column, q.n, 1, '')
    FROM q
    WHERE Table.ID_Column = q.ID_Column AND q.n != 0;

    IF @@ROWCOUNT = 0 BREAK;
END;

Ви також можете значно підвищити ефективність, якщо у таблиці зберігати бітовий стовпець, який вказує, чи поле ще не було очищено. (NULL представляє "Невідомо" у моєму прикладі і має бути типовим стовпцем.)

DECLARE @done bit = 0;
WHILE @done = 0 BEGIN
    WITH q AS
        (SELECT ID_Column, PATINDEX('%[^0-9.]%', Column) AS n
        FROM Table
        WHERE COALESCE(Scrubbed_Column, 0) = 0)
    UPDATE Table
    SET Column = STUFF(Column, q.n, 1, ''),
        Scrubbed_Column = 0
    FROM q
    WHERE Table.ID_Column = q.ID_Column AND q.n != 0;

    IF @@ROWCOUNT = 0 SET @done = 1;

    -- if Scrubbed_Column is still NULL, then the PATINDEX
    -- must have given 0
    UPDATE table
    SET Scrubbed_Column = CASE
        WHEN Scrubbed_Column IS NULL THEN 1
        ELSE NULLIF(Scrubbed_Column, 0)
    END;
END;

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


2
Для того, щоб це рішення працювало, принаймні вам потрібно додати крапку до шаблону PATINDEX; вона повинна бути: [^0-9.]. Якщо немає , то ви роздягнути десяткову і перетворити те , що повинно бути 100.00в 10000.
Соломон Руцький

@srutzky ok додав '.' Я насправді працював над алфавітом і думав, що робити ^ 0-9 буде працювати.
Мукус

+1 за зусилля, але (як ви також вказали) це призведе до того, що звіти працюватимуть занадто довго, вони повільні, як і є ... але для менших даних це відмінне рішення!
січень,

1
Я просто працював над чимось подібним до цього, тому збираюся оновити відповідь швидшим рішенням. Це все ще не ідеально, але продуктивність повинна бути прийнятною в більшості ситуацій.
Тмдін,

@Tmdean: Дякую, що сприяли цьому, я спробую наступного разу, коли зіткнуся з подібною проблемою.
січня 2015 р.,

23

Замість того, щоб зачистити знайденого персонажа за його єдиною позицією, використання Replace(Column, BadFoundCharacter, '')може бути значно швидшим. Крім того, замість того, щоб просто замінити по одному невдалому символу, знайденому в кожному стовпці, це замінює всіх знайдених.

WHILE 1 = 1 BEGIN
    UPDATE dbo.YourTable
    SET Column = Replace(Column, Substring(Column, PatIndex('%[^0-9.-]%', Column), 1), '')
    WHERE Column LIKE '%[^0-9.-]%'
    If @@RowCount = 0 BREAK;
END;

Я переконаний, що це буде працювати краще, ніж прийнята відповідь, хоча б тому, що воно робить менше операцій. Є й інші способи, які також можуть бути швидшими, але я зараз не маю часу на їх вивчення.


Виглядає цікаво, я не маю часу спробувати це зараз, але буду робити, коли маю. Привітання
JANT

4
Це допомогло мені вирішити дещо непов’язану проблему. Я використав ваш Replace(Column, Substring(Column, PatIndex('%[^0-9.-]%', Column), 1), '')біт для вибраного запиту. Отже, спасибі!
jyoseph 02

1
@jyoseph Чудово! Тільки майте на увазі, що це призведе до видалення лише всіх випадків певного поганого символу, і його потрібно запускати неодноразово, якщо набір поганих символів перевищує одиницю ...
ErikE

@ErikE Дякуємо за увагу! Я використовував його для запиту стовпця з телефонними номерами (дещо змінив шаблон до% [^ 0-9]%), щоб видалити все, що не є числовим. Таким чином, користувач міг здійснити запит 333-1234, і він збігався б з телефонними номерами, введеними як 3331234. Якщо я правильно розумію, ви говорите, що у випадку, коли номер телефону (333) -333-1234, він буде лише першим "("? Мені доведеться ще трохи перевірити це.
jyoseph

Правильно. Ви можете встановити модуль CLR. Або в ідеалі просто зробити це в програмному коді.
ErikE

23

У загальному розумінні SQL Server не підтримує регулярні вирази, і ви не можете використовувати їх у власному коді T-SQL.

Для цього ви можете написати функцію CLR. Дивіться тут , наприклад.


1
Добре, що , здається, єдиний шлях , то ... Спасибі
JANT

4

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

Приклад часу виконання деяких відповідей на даний момент на основі запущених рекурсивних запитів на основі набору або скалярної функції, на основі тестового набору 1m рядків, що видаляє символи з випадкового newid, коливається від 34s до 2m05s для прикладів циклу WHILE та від 1m3s до { forever} для прикладів функцій.

Використання функції таблиці з перехресним застосуванням досягає тієї ж мети за 10 секунд . Можливо, вам доведеться налаштувати його відповідно до ваших потреб, таких як максимальна довжина, яку він обробляє.

Функція:

CREATE FUNCTION [dbo].[RemoveChars](@InputUnit VARCHAR(40))
RETURNS TABLE
AS
RETURN
    (
        WITH Numbers_prep(Number) 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
            )
        ,Numbers(Number) AS
            (
                SELECT TOP (ISNULL(LEN(@InputUnit),0))
                    row_number() OVER (ORDER BY (SELECT NULL))
                FROM Numbers_prep a
                    CROSS JOIN Numbers_prep b
            )
        SELECT
            OutputUnit
        FROM
            (
                SELECT
                    substring(@InputUnit,Number,1)
                FROM  Numbers
                WHERE substring(@InputUnit,Number,1) like '%[0-9]%'
                ORDER BY Number
                FOR XML PATH('')
            ) Sub(OutputUnit)
    )

Використання:

UPDATE t
SET column = o.OutputUnit
FROM ##t t
CROSS APPLY [dbo].[RemoveChars](t.column) o

4

Ось функція, яку я написав, щоб виконати це на основі попередніх відповідей.

CREATE FUNCTION dbo.RepetitiveReplace
(
    @P_String VARCHAR(MAX),
    @P_Pattern VARCHAR(MAX),
    @P_ReplaceString VARCHAR(MAX),
    @P_ReplaceLength INT = 1
)
RETURNS VARCHAR(MAX)
BEGIN
    DECLARE @Index INT;

    -- Get starting point of pattern
    SET @Index = PATINDEX(@P_Pattern, @P_String);

    while @Index > 0
    begin
        --replace matching charactger at index
        SET @P_String = STUFF(@P_String, PATINDEX(@P_Pattern, @P_String), @P_ReplaceLength, @P_ReplaceString);
        SET @Index = PATINDEX(@P_Pattern, @P_String);
    end

    RETURN @P_String;
END;

Суть

Редагувати:

Спочатку у мене тут була рекурсивна функція, яка погано працює з сервером sql, оскільки вона має обмеження рівня вкладеності 32, що призведе до помилки, як показано нижче, коли б ви намагалися зробити 32+ заміни функцією. Замість того, щоб намагатися змінити рівень сервера, щоб дозволити більше вкладеності (що може бути небезпечним, наприклад, дозволяти ніколи не закінчуються цикли), перехід на цикл while має набагато більше сенсу.

Перевищено максимально збережений рівень вкладеності процедури, функції, тригера або подання (обмеження 32).


2

Обернення рішення всередині функції SQL може бути корисним, якщо ви хочете використовувати його повторно. Я навіть роблю це на клітинному рівні, тому я ставлю це як іншу відповідь:

CREATE FUNCTION [dbo].[fnReplaceInvalidChars] (@string VARCHAR(300))
RETURNS VARCHAR(300)
BEGIN
    DECLARE @str VARCHAR(300) = @string;
    DECLARE @Pattern VARCHAR (20) = '%[^a-zA-Z0-9]%';
    DECLARE @Len INT;
    SELECT @Len = LEN(@String); 
    WHILE @Len > 0 
    BEGIN
        SET @Len = @Len - 1;
        IF (PATINDEX(@Pattern,@str) > 0)
            BEGIN
                SELECT @str = STUFF(@str, PATINDEX(@Pattern,@str),1,'');    
            END
        ELSE
        BEGIN
            BREAK;
        END
    END     
    RETURN @str
END

2

Я створив цю функцію для очищення рядка, який містив нечислові символи в часовому полі. Час містив знаки запитання, коли вони не додавали протокол, приблизно так 20: ??. Функція прокручує кожен символ і замінює символ? з 0:

 CREATE FUNCTION [dbo].[CleanTime]
(
    -- Add the parameters for the function here
    @intime nvarchar(10) 
)
RETURNS nvarchar(5)
AS
BEGIN
    -- Declare the return variable here
    DECLARE @ResultVar nvarchar(5)
    DECLARE @char char(1)
    -- Add the T-SQL statements to compute the return value here
    DECLARE @i int = 1
    WHILE @i <= LEN(@intime)
    BEGIN
    SELECT @char =  CASE WHEN substring(@intime,@i,1) like '%[0-9:]%' THEN substring(@intime,@i,1) ELSE '0' END
    SELECT @ResultVar = concat(@ResultVar,@char)   
    set @i  = @i + 1       
    END;
    -- Return the result of the function
    RETURN @ResultVar

END

2

Для тих, хто шукає ефективне та просте рішення та готовий увімкнути CLR:

create database TestSQLFunctions
go
use TestSQLFunctions
go
alter database TestSQLFunctions set trustworthy on

EXEC sp_configure 'clr enabled', 1
RECONFIGURE WITH OVERRIDE
go

CREATE ASSEMBLY [SQLFunctions]
AUTHORIZATION [dbo]
FROM 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C0103004BE8B85F0000000000000000E00022200B013000000800000006000000000000C2270000002000000040000000000010002000000002000004000000000000000600000000000000008000000002000000000000030060850000100000100000000010000010000000000000100000000000000000000000702700004F000000004000009803000000000000000000000000000000000000006000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E74657874000000C8070000002000000008000000020000000000000000000000000000200000602E72737263000000980300000040000000040000000A0000000000000000000000000000400000402E72656C6F6300000C0000000060000000020000000E00000000000000000000000000004000004200000000000000000000000000000000A4270000000000004800000002000500682000000807000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003A022D02022A020304281000000A2A1E02281100000A2A0042534A4201000100000000000C00000076342E302E33303331390000000005006C00000018020000237E000084020000CC02000023537472696E6773000000005005000004000000235553005405000010000000234755494400000064050000A401000023426C6F620000000000000002000001471500000900000000FA0133001600000100000013000000020000000200000003000000110000000F00000001000000030000000000CA01010000000000060025014F02060092014F02060044001D020F006F02000006006C00E20106000801E2010600D400E20106007901E20106004501E20106005E01E20106008300E2010600580030020600360030020600B700E20106009E00B0010600AA02DB010A00F300FC010A001F00FC010E00C3027E02000000000100000000000100010001001000C3029D0241000100010050200000000096002E001A0001005F2000000000861817020600040000000100BD0200000200F40100000300B102090017020100110017020600190017020A0029001702100031001702100039001702100041001702100049001702100051001702100059001702100061001702150069001702100071001702100079001702100089001702060099002E001A0081001702060020007B0010012E000B002A002E00130033002E001B0052002E0023005B002E002B006D002E0033006D002E003B006D002E0043005B002E004B0073002E0053006D002E005B006D002E0063008B002E006B00B5002E007300C2000480000001000000000000000000000000009D020000040000000000000000000000210016000000000004000000000000000000000021000A00000000000400000000000000000000002100DB010000000000000000003C4D6F64756C653E0053797374656D2E44617461006D73636F726C696200446174614163636573734B696E64005265706C61636500477569644174747269627574650044656275676761626C6541747472696275746500436F6D56697369626C6541747472696275746500417373656D626C795469746C6541747472696275746500417373656D626C7954726164656D61726B417474726962757465005461726765744672616D65776F726B41747472696275746500417373656D626C7946696C6556657273696F6E41747472696275746500417373656D626C79436F6E66696775726174696F6E4174747269627574650053716C46756E6374696F6E41747472696275746500417373656D626C794465736372697074696F6E41747472696275746500436F6D70696C6174696F6E52656C61786174696F6E7341747472696275746500417373656D626C7950726F6475637441747472696275746500417373656D626C79436F7079726967687441747472696275746500417373656D626C79436F6D70616E794174747269627574650052756E74696D65436F6D7061746962696C6974794174747269627574650053797374656D2E52756E74696D652E56657273696F6E696E670053514C46756E6374696F6E732E646C6C0053797374656D0053797374656D2E5265666C656374696F6E007061747465726E004D6963726F736F66742E53716C5365727665722E536572766572002E63746F720053797374656D2E446961676E6F73746963730053797374656D2E52756E74696D652E496E7465726F7053657276696365730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F6465730053797374656D2E546578742E526567756C617245787072657373696F6E730053514C46756E6374696F6E73004F626A656374007265706C6163656D656E7400696E70757400526567657800000000000000003A1617E607071B47B964858BCD87458B00042001010803200001052001011111042001010E04200101020600030E0E0E0E08B77A5C561934E0890801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F7773010801000200000000001101000C53514C46756E6374696F6E73000005010000000017010012436F7079726967687420C2A920203230323000002901002434346436386231632D393735312D343938612D396665352D32316666333934303738303900000C010007312E302E302E3000004D01001C2E4E45544672616D65776F726B2C56657273696F6E3D76342E352E320100540E144672616D65776F726B446973706C61794E616D65142E4E4554204672616D65776F726B20342E352E32808F010001005455794D6963726F736F66742E53716C5365727665722E5365727665722E446174614163636573734B696E642C2053797374656D2E446174612C2056657273696F6E3D342E302E302E302C2043756C747572653D6E65757472616C2C205075626C69634B6579546F6B656E3D623737613563353631393334653038390A4461746141636365737301000000000000982700000000000000000000B2270000002000000000000000000000000000000000000000000000A4270000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF25002000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001001000000018000080000000000000000000000000000001000100000030000080000000000000000000000000000001000000000048000000584000003C03000000000000000000003C0334000000560053005F00560045005200530049004F004E005F0049004E0046004F0000000000BD04EFFE00000100000001000000000000000100000000003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073006C006100740069006F006E00000000000000B0049C020000010053007400720069006E006700460069006C00650049006E0066006F0000007802000001003000300030003000300034006200300000001A000100010043006F006D006D0065006E007400730000000000000022000100010043006F006D00700061006E0079004E0061006D006500000000000000000042000D000100460069006C0065004400650073006300720069007000740069006F006E0000000000530051004C00460075006E006300740069006F006E00730000000000300008000100460069006C006500560065007200730069006F006E000000000031002E0030002E0030002E003000000042001100010049006E007400650072006E0061006C004E0061006D0065000000530051004C00460075006E006300740069006F006E0073002E0064006C006C00000000004800120001004C006500670061006C0043006F007000790072006900670068007400000043006F0070007900720069006700680074002000A90020002000320030003200300000002A00010001004C006500670061006C00540072006100640065006D00610072006B00730000000000000000004A00110001004F0072006900670069006E0061006C00460069006C0065006E0061006D0065000000530051004C00460075006E006300740069006F006E0073002E0064006C006C00000000003A000D000100500072006F0064007500630074004E0061006D00650000000000530051004C00460075006E006300740069006F006E00730000000000340008000100500072006F006400750063007400560065007200730069006F006E00000031002E0030002E0030002E003000000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000031002E0030002E0030002E0030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000C000000C43700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
WITH PERMISSION_SET = SAFE

go

CREATE FUNCTION RegexReplace(
    @input nvarchar(max),
    @pattern nvarchar(max),
    @replacement nvarchar(max)
) RETURNS nvarchar  (max)
AS EXTERNAL NAME SQLFunctions.[SQLFunctions.Regex].Replace; 

go

-- outputs This is a test 
select dbo.RegexReplace('This is a test 12345','[0-9]','')

Зміст DLL: введіть тут опис зображення


1

Якщо ви робите це лише для параметра, що входить до збереженої процедури, ви можете використовувати наступне:

declare @badIndex int
set @badIndex = PatIndex('%[^0-9]%', @Param)
while @badIndex > 0
    set @Param = Replace(@Param, Substring(@Param, @badIndex, 1), '')
    set @badIndex = PatIndex('%[^0-9]%', @Param)

0

Я думаю, що простіший і швидший підхід повторюється кожним символом алфавіту:

DECLARE @i int
SET @i = 0

WHILE(@i < 256)
BEGIN  

    IF char(@i) NOT IN ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.')      

      UPDATE Table SET Column = replace(Column, char(@i), '')

    SET @i = @i + 1

END

1
Будь ласка, не використовуйте щось подібне у виробництві. Ви робите 245 оновлень без пропозиції where. Це працює, але це далеко не ефективний підхід. Кращою ідеєю може бути перебір символів, які ми хочемо видалити, замість усіх символів, доступних в алфавіті. Але навіть це можна було б покращити до чогось кращого.
Андерсон Сільва,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.