2015-03-05 4 views
3

У меня есть таблица MyTextstable (myTextsTable_id INT, myTextsTable_text VARCHAR(MAX)). В этой таблице содержится около 4 миллионов записей, и я пытаюсь удалить любой экземпляр символов ASCII в следующих диапазонах (44): VARCHAR(MAX), столбец myTextsTable_text.SQL-запрос работает нормально в SQL Server 2012, но не удалось выполнить в SQL Server 2008 R2

  • 00 - 08
  • 11 - 12
  • 14 - 31

Я написал следующий SQL запрос, который берет за 10 минут на SQL Server 2012, но не удалось выполнить на SQL Server 2008 R2 даже через два часа (поэтому я остановил выполнение). Обратите внимание: я восстановил резервную копию базы данных SQL Server 2008 R2 на SQL Server 2012 (т. Е. Данные точно такие же).

BEGIN TRANSACTION [Tran1] 

BEGIN TRY 
    UPDATE myTextsTable 
    SET myTextsTable_text = 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(myTextsTable_text, CHAR(0), ''), CHAR(1), ''), CHAR(2), ''), CHAR(3), ''), CHAR(4), ''), CHAR(5), ''), CHAR(6), ''), CHAR(7), ''), CHAR(8), ''), CHAR(11), ''), CHAR(12), ''), CHAR(14), ''), CHAR(15), ''), CHAR(16), ''), CHAR(17), ''), CHAR(18), ''), CHAR(19), ''), CHAR(20), ''), CHAR(21), ''), CHAR(22), ''), CHAR(23), ''), CHAR(24), ''), CHAR(25), ''), CHAR(26), ''), CHAR(27), ''), CHAR(28), ''), CHAR(29), ''), CHAR(30), ''), CHAR(31), ''), CHAR(127), '') 
    WHERE myTextsTable_text LIKE '%[' + CHAR(0) + CHAR(1) + CHAR(2) + CHAR(3) + CHAR(4) + CHAR(5) + CHAR(6) + CHAR(7) + CHAR(8) + CHAR(11) + CHAR(12) + CHAR(14) + CHAR(15) + CHAR(16) + CHAR(17) + CHAR(18) + CHAR(19) + CHAR(20) + CHAR(21) + CHAR(22) + CHAR(23) + CHAR(24) + CHAR(25) + CHAR(26) + CHAR(27) + CHAR(28) + CHAR(29) + CHAR(30) + CHAR(31) + CHAR(127) + ']%'; 
    COMMIT TRANSACTION [Tran1]; 
END TRY 

BEGIN CATCH 
    ROLLBACK TRANSACTION [Tran1]; 
    --PRINT ERROR_MESSAGE(); 
END CATCH; 

Включено только 135 записей. Поскольку единственный запрос UPDATE не работал в SQL Server 2008, я попробовал следующий подход с временной таблицей.

BEGIN TRANSACTION [Tran1] 

BEGIN TRY 
    IF OBJECT_ID('tempdb..#myTextsTable') IS NOT NULL DROP TABLE #myTextsTable; 
    SELECT myTextsTable_id, myTextsTable_text 
    INTO #myTextsTable 
    FROM myTextsTable 
    WHERE myTextsTable_text LIKE '%[' + CHAR(0) + CHAR(1) + CHAR(2) + CHAR(3) + CHAR(4) + CHAR(5) + CHAR(6) + CHAR(7) + CHAR(8) + CHAR(11) + CHAR(12) + CHAR(14) + CHAR(15) + CHAR(16) + CHAR(17) + CHAR(18) + CHAR(19) + CHAR(20) + CHAR(21) + CHAR(22) + CHAR(23) + CHAR(24) + CHAR(25) + CHAR(26) + CHAR(27) + CHAR(28) + CHAR(29) + CHAR(30) + CHAR(31) + CHAR(127) + ']%'; 

    UPDATE #myTextsTable 
    SET myTextsTable_text = 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(myTextsTable_text, CHAR(0), ''), CHAR(1), ''), CHAR(2), ''), CHAR(3), ''), CHAR(4), ''), CHAR(5), ''), CHAR(6), ''), CHAR(7), ''), CHAR(8), ''), CHAR(11), ''), CHAR(12), ''), CHAR(14), ''), CHAR(15), ''), CHAR(16), ''), CHAR(17), ''), CHAR(18), ''), CHAR(19), ''), CHAR(20), ''), CHAR(21), ''), CHAR(22), ''), CHAR(23), ''), CHAR(24), ''), CHAR(25), ''), CHAR(26), ''), CHAR(27), ''), CHAR(28), ''), CHAR(29), ''), CHAR(30), ''), CHAR(31), ''), CHAR(127), '') 

    UPDATE myTextsTable 
    SET myTextsTable_text = new.myTextsTable_text 
    FROM myTextsTable 
    INNER JOIN #myTextsTable new ON new.myTextsTable_id=myTextsTable.myTextsTable_id 

    DROP TABLE #myTextsTable; 

    COMMIT TRANSACTION [Tran1]; 
END TRY 

BEGIN CATCH 
    ROLLBACK TRANSACTION [Tran1]; 
    --PRINT ERROR_MESSAGE(); 
END CATCH; 

Однако результат такой же. Работает отлично в SQL Server 2012, но не в SQL Server 2008 R2. Я обнаружил, что запрос UPDATE все еще выполнялся даже через два часа (записи были сохранены в таблице темпа (#myTextsTable) через несколько минут, я проверил это позже, чтобы убедиться, что какая часть занимает больше времени).

Поскольку вышеупомянутые два способа не работали, я попытался использовать это с использованием переменных TABLE, чтобы проверить, не имеет значения, но результат был таким же (т.е. отлично работает в SQL Server 2012, но не в SQL Server 2008 R2)

BEGIN TRANSACTION [Tran1] 

BEGIN TRY 
    DECLARE @myTextsTable TABLE (myTextsTable_id INT, myTextsTable_text VARCHAR(MAX)) 
    INSERT INTO @myTextsTable(myTextsTable_id, myTextsTable_text) 
    SELECT myTextsTable_id, myTextsTable_text 
    FROM myTextsTable 
    WHERE myTextsTable_text LIKE '%[' + CHAR(0) + CHAR(1) + CHAR(2) + CHAR(3) + CHAR(4) + CHAR(5) + CHAR(6) + CHAR(7) + CHAR(8) + CHAR(11) + CHAR(12) + CHAR(14) + CHAR(15) + CHAR(16) + CHAR(17) + CHAR(18) + CHAR(19) + CHAR(20) + CHAR(21) + CHAR(22) + CHAR(23) + CHAR(24) + CHAR(25) + CHAR(26) + CHAR(27) + CHAR(28) + CHAR(29) + CHAR(30) + CHAR(31) + CHAR(127) + ']%'; 

    UPDATE @myTextsTable 
    SET myTextsTable_text = 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(myTextsTable_text, CHAR(0), ''), CHAR(1), ''), CHAR(2), ''), CHAR(3), ''), CHAR(4), ''), CHAR(5), ''), CHAR(6), ''), CHAR(7), ''), CHAR(8), ''), CHAR(11), ''), CHAR(12), ''), CHAR(14), ''), CHAR(15), ''), CHAR(16), ''), CHAR(17), ''), CHAR(18), ''), CHAR(19), ''), CHAR(20), ''), CHAR(21), ''), CHAR(22), ''), CHAR(23), ''), CHAR(24), ''), CHAR(25), ''), CHAR(26), ''), CHAR(27), ''), CHAR(28), ''), CHAR(29), ''), CHAR(30), ''), CHAR(31), ''), CHAR(127), '') 

    UPDATE myTextsTable 
    SET myTextsTable_updated = GETDATE() 
     ,myTextsTable_updatedby = 'As per V87058' 
     ,myTextsTable_text = new.myTextsTable_text 
    FROM myTextsTable 
    INNER JOIN @myTextsTable new ON new.myTextsTable_id=myTextsTable.myTextsTable_id 

    COMMIT TRANSACTION [Tran1]; 
END TRY 

BEGIN CATCH 
    ROLLBACK TRANSACTION [Tran1]; 
    --PRINT ERROR_MESSAGE(); 
END CATCH; 

Может ли кто-нибудь объяснить, почему это произошло? Как заставить этот SQL-запрос работать в SQL Server 2008 R2?

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

SQL Server 2012
Microsoft SQL Server 2012 - 11.0.5058.0 (X64)
Standard Edition (64-разрядная версия) на Windows NT 6.3 (Build 9600:) (гипервизор)

SQL Server 2008 R2
Microsoft SQL Server 2012 - 11.0.5058.0 (X64)
Standard Edition (64-разрядная версия) на Windows NT 6.3 (Build 9600:) (гипервизор)

+0

Вместо переменной таблицы попробуйте TempTable. –

+0

@ SelvaTS: Спасибо за ваш комментарий. Я попробовал временную таблицу перед тем, как попробовать ее с помощью TABLE vatable, как упоминалось в моем вопросе. – Sathish

+0

Это может быть довольно долго, но я попытаюсь разделить на 30 запросов (или с помощью цикла) для замены символов один за другим: 'UPDATE myTextsTable SET myTextsTable_text = REPLACE (myTextsTable_text, CHAR (0), ' ') WHERE myTextsTable_text LIKE'% ['+ CHAR (0) +']% '; ', чтобы избежать слишком большого количества исправленных REPLACE. И в этом случае, возможно, CHARINDEX вместо LIKE. – Rubik

ответ

1

Это known issue на SQL Server 2008 с типами данных LOB и некоторыми сопоставлениями.

Это легко воспроизвести

/*Hangs on 2008*/ 

DECLARE @VcMax varchar(max)= char(0) + 'a' 

SELECT REPLACE(@VcMax COLLATE Latin1_General_CS_AS, char(0), '') 

В то время как он висел ЦП, и, как представляется, в бесконечном цикле через эти функции.

enter image description here

И исправить это легко. Либо использовать не MAX тип данных ...

... или бинарный сверку

/*Doesn't Hang*/ 
DECLARE @VcMax varchar(max)= char(0) + 'a' 

SELECT REPLACE(@VcMax COLLATE Latin1_General_100_BIN2, char(0), '') 
+0

Спасибо за ваш комментарий. Я попробую это сейчас, но не могли бы вы пояснить, почему нам нужно переменная 'CAST @ VBMax' в вашем примере (она объявлена ​​как поле VARCHAR (MAX)?)? – Sathish

+0

@Sathish - Не нужно вообще. Это потому, что я просто скопировал код из элемента подключения, который я отправил, и там он использовал 'varbinary (max)' –

+0

Спасибо за разъяснение, я подумал, что так будет. Не могли бы вы объяснить, каково изображение, прикрепленное к вашему ответу? Можно ли его создать в SSMS или нам нужно использовать любой другой инструмент? – Sathish

-1

Вероятно, проблема заключается в вложенности в замене и сообщается о выполнении, а не функции compilación check @@ nestlevel. https://technet.microsoft.com/en-us/library/ms190607(v=sql.105).aspx

+0

'@@ nestlevel' не применяется к встроенным функциям и не приведет к неопределенному зависанию, если оно будет превышено. И это легко воспроизвести с помощью одного «REPLACE». –

1

Для тех, кто читает это в будущем, следующие способы работали отлично.

Способ 1. Изменение COLLATION на VARCHAR(MAX) колонки в UPDATE SQL запросе к BINARY COLLATION, как Мартин Смит предложил (см принятого ответа).

REPLACE (myTextsTable_text КОПИЯМ Latin1_General_100_BIN2, CHAR (0), ...

Решение будет как показано ниже:

GO 
BEGIN TRANSACTION [Tran1] 

BEGIN TRY 
    IF OBJECT_ID('tempdb..#myTextsTable') IS NOT NULL DROP TABLE #myTextsTable; 
    SELECT myTextsTable_id, myTextsTable_text 
    INTO #myTextsTable 
    FROM myTextsTable 
    WHERE myTextsTable_text LIKE '%[' + CHAR(0) + CHAR(1) + CHAR(2) + CHAR(3) + CHAR(4) + CHAR(5) + CHAR(6) + CHAR(7) + CHAR(8) + CHAR(11) + CHAR(12) + CHAR(14) + CHAR(15) + CHAR(16) + CHAR(17) + CHAR(18) + CHAR(19) + CHAR(20) + CHAR(21) + CHAR(22) + CHAR(23) + CHAR(24) + CHAR(25) + CHAR(26) + CHAR(27) + CHAR(28) + CHAR(29) + CHAR(30) + CHAR(31) + CHAR(127) + ']%'; 

    UPDATE #myTextsTable 
    SET myTextsTable_text = 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(myTextsTable_text COLLATE Latin1_General_100_BIN2, CHAR(0), ''), CHAR(1), ''), CHAR(2), ''), CHAR(3), ''), CHAR(4), ''), CHAR(5), ''), CHAR(6), ''), CHAR(7), ''), CHAR(8), ''), CHAR(11), ''), CHAR(12), ''), CHAR(14), ''), CHAR(15), ''), CHAR(16), ''), CHAR(17), ''), CHAR(18), ''), CHAR(19), ''), CHAR(20), ''), CHAR(21), ''), CHAR(22), ''), CHAR(23), ''), CHAR(24), ''), CHAR(25), ''), CHAR(26), ''), CHAR(27), ''), CHAR(28), ''), CHAR(29), ''), CHAR(30), ''), CHAR(31), ''), CHAR(127), '') 

    UPDATE myTextsTable 
    SET myTextsTable_updated = GETDATE() 
     ,myTextsTable_updatedby = 'As per V87058' 
     ,myTextsTable_text = new.myTextsTable_text 
    FROM myTextsTable 
    INNER JOIN #myTextsTable new ON new.myTextsTable_id=myTextsTable.myTextsTable_id 

    DROP TABLE #myTextsTable; 

    COMMIT TRANSACTION [Tran1]; 
END TRY 

Way 2: Я создал SQL function к замените эти символы на STUFF вместо функции REPLACE.

Примечание. Обратите внимание, что функция SQL записывается в мое конкретное требование . Таким образом, он заменяет только символы в следующем диапазоне .

  • 00 - 08
  • 11 - 12
  • 14 - 31

-

Go 
CREATE FUNCTION [dbo].RemoveASCIICharactersInRange(@InputString VARCHAR(MAX)) 
    RETURNS VARCHAR(MAX) 
    AS 
    BEGIN 
     IF @InputString IS NOT NULL 
     BEGIN 
      DECLARE @Counter INT, @TestString NVARCHAR(40) 

      SET @TestString = '%[' + NCHAR(0) + NCHAR(1) + NCHAR(2) + NCHAR(3) + NCHAR(4) + NCHAR(5) + NCHAR(6) + NCHAR(7) + NCHAR(8) + NCHAR(11) + NCHAR(12) + NCHAR(14) + NCHAR(15) + NCHAR(16) + NCHAR(17) + NCHAR(18) + NCHAR(19) + NCHAR(20) + NCHAR(21) + NCHAR(22) + NCHAR(23) + NCHAR(24) + NCHAR(25) + NCHAR(26) + NCHAR(27) + NCHAR(28) + NCHAR(29) + NCHAR(30) + NCHAR(31) + NCHAR(127)+ ']%' 

      SELECT @Counter = PATINDEX (@TestString, @InputString COLLATE Latin1_General_BIN) 

      WHILE @Counter <> 0 
      BEGIN 
      SELECT @InputString = STUFF(@InputString, @Counter, 1, '') 
      SELECT @Counter = PATINDEX (@TestString, @InputString COLLATE Latin1_General_BIN) 
      END 
     END 
     RETURN(@InputString) 
    END 

    GO 

Затем UPDATE SQL запрос (в моем temp table) будет что-то вроде:

UPDATE #myTextsTable 
SET myTextsTable_text = [dbo].RemoveASCIICharactersInRange(#myTextsTable_text) 
Go 

Мой личный предпочтительный способ был бы первым.

Смежные вопросы