Самый быстрый способ удалить нечисловые символы из VARCHAR в SQL Server - PullRequest
60 голосов
/ 20 сентября 2008

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

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

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

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

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

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

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

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

Может ли сравнение BIGINT и BIGINT замедлить процесс?

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

Ответы [ 15 ]

103 голосов
/ 30 июня 2011

Я видел это решение с кодом 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
36 голосов
/ 30 июля 2013

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 * * 1002

:)

16 голосов
/ 12 августа 2011

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

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

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

16 голосов
/ 20 сентября 2008

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

Для обновления существующих записей я бы просто использовал 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)
9 голосов
/ 20 марта 2014

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

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 голосов
/ 03 ноября 2010
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('aisdfhoiqwei352345234@#$%^$@345345%^@#$^')  
1 голос
/ 08 апреля 2010

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

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
1 голос
/ 20 сентября 2008

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

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

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

1 голос
/ 20 сентября 2008

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

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

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

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

1 голос
/ 20 сентября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...