Самый быстрый способ удаления нечисловых символов из VARCHAR в SQL Server

Я пишу утилиту импорта, которая использует номера телефонов в качестве уникального ключа в импорте.

Мне нужно проверить, что номер телефона еще не существует в моей БД. Проблема в том, что номера телефонов в БД могут иметь такие вещи, как тире и скобки и, возможно, другие вещи. Я написал функцию для удаления этих вещей, проблема в том, что она медленная и с тысячами записей в моей БД и тысячами записей для импорта сразу, этот процесс может быть неприемлемо медленным. Я уже сделал номер столбца номера телефона индексом.

Я попытался использовать script из этой записи:
T-SQL trim & nbsp (и другие не-буквенно-цифровые символы)

Но это не ускорило его.

Есть ли более быстрый способ удаления нечисловых символов? Что-то, что может хорошо работать, когда нужно сравнивать 10 000 - 100 000 записей.

Что бы ни было сделано, необходимо выполнить быстро.

Обновление
Учитывая, что люди отреагировали, я думаю, что мне придется очищать поля до запуска утилиты импорта.

Чтобы ответить на вопрос о том, что я пишу в утилите импорта, это приложение С#. Сейчас я сравниваю BIGINT с BIGINT, и вам не нужно изменять данные DB, и я все еще делаю удар по производительности с очень небольшим набором данных (около 2000 записей).

Может ли сравнение BIGINT с BIGINT замедлять работу?

Я оптимизировал кодовую часть моего приложения настолько, насколько могу (удалял регулярные выражения, удалял ненужные вызовы БД). Хотя я больше не могу изолировать SQL как источник проблемы, я все еще чувствую, что это так.

Ответ 1

Я могу неправильно понять, но у вас есть два набора данных для удаления строк из одного для текущих данных в базе данных, а затем новый набор при импорте.

Для обновления существующих записей я бы просто использовал SQL, который должен произойти только один раз.

Однако SQL не оптимизирован для такого рода операций, так как вы сказали, что пишете утилиту импорта, я бы сделал эти обновления в контексте самой утилиты импорта, а не в SQL. Это будет намного лучше. Что вы пишете утилиту?

Кроме того, я могу полностью недопонимать этот процесс, поэтому извиняюсь, если вне базы.

Изменить:
Для первоначального обновления, если вы используете SQL Server 2005, вы можете попробовать CLR-функцию. Здесь быстрый, используя регулярное выражение. Не уверен, как производительность сравнится, я никогда не использовал это сам, кроме быстрого теста прямо сейчас.

using System;  
using System.Data;  
using System.Text.RegularExpressions;  
using System.Data.SqlClient;  
using System.Data.SqlTypes;  
using Microsoft.SqlServer.Server;  

public partial class UserDefinedFunctions  
{  
    [Microsoft.SqlServer.Server.SqlFunction]  
    public static SqlString StripNonNumeric(SqlString input)  
    {  
        Regex regEx = new Regex(@"\D");  
        return regEx.Replace(input.Value, "");  
    }  
};  

После развертывания для обновления вы можете просто использовать:

UPDATE table SET phoneNumber = dbo.StripNonNumeric(phoneNumber)

Ответ 2

Я видел это решение с кодом T-SQL и PATINDEX. Мне это нравится: -)

CREATE Function [fnRemoveNonNumericCharacters](@strText VARCHAR(1000))
RETURNS VARCHAR(1000)
AS
BEGIN
    WHILE PATINDEX('%[^0-9]%', @strText) > 0
    BEGIN
        SET @strText = STUFF(@strText, PATINDEX('%[^0-9]%', @strText), 1, '')
    END
    RETURN @strText
END

Ответ 3

replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(string,'a',''),'b',''),'c',''),'d',''),'e',''),'f',''),'g',''),'h',''),'i',''),'j',''),'k',''),'l',''),'m',''),'n',''),'o',''),'p',''),'q',''),'r',''),'s',''),'t',''),'u',''),'v',''),'w',''),'x',''),'y',''),'z',''),'A',''),'B',''),'C',''),'D',''),'E',''),'F',''),'G',''),'H',''),'I',''),'J',''),'K',''),'L',''),'M',''),'N',''),'O',''),'P',''),'Q',''),'R',''),'S',''),'T',''),'U',''),'V',''),'W',''),'X',''),'Y',''),'Z','')*1 AS string,

:)

Ответ 4

Если вы не хотите создавать функцию или вам нужен только один встроенный вызов в T-SQL, вы можете попробовать:

set @Phone = REPLACE(REPLACE(REPLACE(REPLACE(@Phone,'(',''),' ',''),'-',''),')','')

Конечно, это специфично для удаления форматирования номера телефона, а не для общего удаления всех специальных символов из строковой функции.

Ответ 5

Простая функция:

CREATE FUNCTION [dbo].[RemoveAlphaCharacters](@InputString VARCHAR(1000))
RETURNS VARCHAR(1000)
AS
BEGIN
  WHILE PATINDEX('%[^0-9]%',@InputString)>0
        SET @InputString = STUFF(@InputString,PATINDEX('%[^0-9]%',@InputString),1,'')     
  RETURN @InputString
END

GO

Ответ 6

create function dbo.RemoveNonNumericChar(@str varchar(500))  
returns varchar(500)  
begin  
declare @startingIndex int  
set @startingIndex=0  
while 1=1  
begin  
    set @startingIndex= patindex('%[^0-9]%',@str)  
    if @startingIndex <> 0  
    begin  
        set @str = replace(@str,substring(@str,@startingIndex,1),'')  
    end  
    else    break;   
end  
return @str  
end

go  

select dbo.RemoveNonNumericChar('[email protected]#$%^[email protected]%^@#$^')  

Ответ 7

Вы можете удалить их в ночном режиме, сохраняя их в отдельном поле, а затем выполнить обновление для измененных записей непосредственно перед запуском процесса?

Или при вставке/обновлении сохраните "числовой" формат, чтобы ссылаться позже. Триггер был бы простым способом сделать это.

Ответ 8

Работа с varchars существенно медленная и неэффективная по сравнению с работой с числами по очевидным причинам. Функции, на которые вы ссылаетесь в исходном сообщении, действительно будут довольно медленными, поскольку они прокручивают каждый символ в строке, чтобы определить, является ли это числом. Сделайте это для тысяч записей, и процесс будет медленным. Это идеальная работа для регулярных выражений, но они не поддерживаются в SQL Server. Вы можете добавить поддержку с помощью функции CLR, но трудно сказать, насколько медленно это произойдет, но я не ожидал, что это будет значительно быстрее, чем цикл каждого символа каждого номера телефона, однако!

Как только вы получите номера телефонов, отформатированные в вашей базе данных, чтобы они были только номерами, вы можете переключиться на числовой тип в SQL, который обеспечил бы молниеносное сравнение с другими числовыми типами. Вы можете обнаружить, что в зависимости от того, насколько быстрыми будут ваши новые данные, выполнение обрезки и преобразование в числовое значение на стороне базы данных достаточно быстро, как только вы сравниваете, правильно отформатировано, но, если возможно, вам будет лучше отключить запись утилиты импорта на языке .NET, которая позаботится об этих проблемах с форматированием перед удалением базы данных.

В любом случае, у вас будет большая проблема с дополнительным форматированием. Даже если ваши номера гарантированно будут только североамериканскими по происхождению, некоторые люди поставят 1 перед полным телефонным номером с полным кодом, а другие - нет, что вызовет возможность для нескольких записей одного и того же номера телефона. Кроме того, в зависимости от того, что представляют ваши данные, некоторые люди будут использовать свой домашний номер телефона, в котором может быть несколько человек, живущих там, поэтому единственным ограничением на него будет только один член базы данных в домохозяйстве. Некоторые из них будут использовать свой рабочий номер и иметь одну и ту же проблему, а некоторые будут или не будут включать расширение, которое снова вызовет искусственный потенциал уникальности.

Все это может повлиять на вас или не повлиять на вас, в зависимости от ваших конкретных данных и обычаев, но важно помнить!

Ответ 9

Сначала я попробую функцию Скотта CLR, но добавлю предложение WHERE, чтобы уменьшить количество обновленных записей.

UPDATE table SET phoneNumber = dbo.StripNonNumeric(phoneNumber) 
WHERE phonenumber like '%[^0-9]%'

Если вы знаете, что подавляющее большинство ваших записей имеют нечисловые символы, это может не помочь.

Ответ 10

Я знаю, что поздно в игре, но вот функция, которую я создал для T-SQL, которая быстро удаляет нечисловые символы. Отметим, что у меня есть схема "String", в которую я помещаю функции утилиты для строк в...

CREATE FUNCTION String.ComparablePhone( @string nvarchar(32) ) RETURNS bigint AS
BEGIN
    DECLARE @out bigint;

-- 1. table of unique characters to be kept
    DECLARE @keepers table ( chr nchar(1) not null primary key );
    INSERT INTO @keepers ( chr ) VALUES (N'0'),(N'1'),(N'2'),(N'3'),(N'4'),(N'5'),(N'6'),(N'7'),(N'8'),(N'9');

-- 2. Identify the characters in the string to remove
    WITH found ( id, position ) AS
    (
        SELECT 
            ROW_NUMBER() OVER (ORDER BY (n1+n10) DESC), -- since we are using stuff, for the position to continue to be accurate, start from the greatest position and work towards the smallest
            (n1+n10)
        FROM 
            (SELECT 0 AS n1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS d1,
            (SELECT 0 AS n10 UNION SELECT 10 UNION SELECT 20 UNION SELECT 30) AS d10
        WHERE
            (n1+n10) BETWEEN 1 AND len(@string)
            AND substring(@string, (n1+n10), 1) NOT IN (SELECT chr FROM @keepers)
    )
-- 3. Use stuff to snuff out the identified characters
    SELECT 
        @string = stuff( @string, position, 1, '' )
    FROM 
        found
    ORDER BY
        id ASC; -- important to process the removals in order, see ROW_NUMBER() above

-- 4. Try and convert the results to a bigint   
    IF len(@string) = 0
        RETURN NULL; -- an empty string converts to 0

    RETURN convert(bigint,@string); 
END

Затем использовать его для сравнения для вставки, что-то вроде этого;

INSERT INTO Contacts ( phone, first_name, last_name )
SELECT i.phone, i.first_name, i.last_name
FROM Imported AS i
LEFT JOIN Contacts AS c ON String.ComparablePhone(c.phone) = String.ComparablePhone(i.phone)
WHERE c.phone IS NULL -- Exclude those that already exist

Ответ 11

Я бы рекомендовал применять строгий формат для телефонных номеров в базе данных. Я использую следующий формат. (Предполагая номера телефонов в США)

База данных: 5555555555x555

Дисплей: (555) 555-5555 доб. 555

Вход: 10 цифр или более цифр, встроенных в любую строку. (Замена Regex удаляет все нечисловые символы)

Ответ 12

"Хотя я больше не могу изолировать SQL как источник проблемы, я все еще чувствую, что это так."

Запустите SQL Profiler и посмотрите. Возьмите полученные запросы и проверьте их планы выполнения, чтобы убедиться, что этот индекс используется.

Ответ 13

Тысячи записей против тысяч записей обычно не являются проблемой. Я использовал SSIS для импорта миллионов записей с дедублированием, как это.

Я бы очистил базу данных, чтобы сначала удалить нечисловые символы и сохранить их.

Ответ 14

Ищете супер простое решение:

SUBSTRING([Phone], CHARINDEX('(', [Phone], 1)+1, 3)
       + SUBSTRING([Phone], CHARINDEX(')', [Phone], 1)+1, 3)
       + SUBSTRING([Phone], CHARINDEX('-', [Phone], 1)+1, 4) AS Phone

Ответ 15

Я бы использовал функцию Inline с точки зрения производительности, см. ниже: Обратите внимание, что символы типа "+", "-" и т.д. не будут удалены

CREATE FUNCTION [dbo].[UDF_RemoveNumericStringsFromString]
 (
 @str varchar(100)
 )
 RETURNS TABLE AS RETURN
 WITH Tally (n) as 
  (
  -- 100 rows
   SELECT TOP (Len(@Str)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
   FROM (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
   CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
  )

  SELECT OutStr =  STUFF(
       (SELECT SUBSTRING(@Str, n,1) st
        FROM Tally
        WHERE ISNUMERIC(SUBSTRING(@Str, n,1)) = 1
        FOR XML PATH(''),type).value('.', 'varchar(100)'),1,0,'')
  GO

  /*Use it*/
  SELECT OutStr
  FROM dbo.UDF_RemoveNumericStringsFromString('fjkfhk759734977fwe9794t23')
  /*Result set
   759734977979423 */

Вы можете определить его с более чем 100 символами...