2009-10-16 208 views
26

Esto se basa en una pregunta similar How to Replace Multiple Characters in Access SQL?Cómo reemplazar varios caracteres en SQL?

escribí esto, ya que el servidor SQL 2005 parece tener un límite en sustituir() para 19 sustituciones dentro de una cláusula where.

tengo la siguiente tarea: Necesidad de realizar un partido en una columna, y para mejorar las posibilidades de un partido de pelar múltiples caracteres y las Naciones Unidas es necesario el uso de reemplazar la función

DECLARE @es NVarChar(1) SET @es = '' 
DECLARE @p0 NVarChar(1) SET @p0 = '!' 
DECLARE @p1 NVarChar(1) SET @p1 = '@' 
---etc... 

SELECT * 
FROM t1,t2 
WHERE REPLACE(REPLACE(t1.stringkey,@p0, @es), @p1, @es) 
    = REPLACE(REPLACE(t2.stringkey,@p0, @es), @p1, @es)  
---etc 

() Si hay> 19 REPLACE() en esa cláusula where, no funciona. Así que la solución que se me ocurrió es crear una función de SQL llamada trimChars en este ejemplo (con perdón de ellos a partir de @ 22

CREATE FUNCTION [trimChars] (
    @string varchar(max) 
) 

RETURNS varchar(max) 
AS 
BEGIN 

DECLARE @es NVarChar(1) SET @es = '' 
DECLARE @p22 NVarChar(1) SET @p22 = '^' 
DECLARE @p23 NVarChar(1) SET @p23 = '&' 
DECLARE @p24 NVarChar(1) SET @p24 = '*' 
DECLARE @p25 NVarChar(1) SET @p25 = '(' 
DECLARE @p26 NVarChar(1) SET @p26 = '_' 
DECLARE @p27 NVarChar(1) SET @p27 = ')' 
DECLARE @p28 NVarChar(1) SET @p28 = '`' 
DECLARE @p29 NVarChar(1) SET @p29 = '~' 
DECLARE @p30 NVarChar(1) SET @p30 = '{' 

DECLARE @p31 NVarChar(1) SET @p31 = '}' 
DECLARE @p32 NVarChar(1) SET @p32 = ' ' 
DECLARE @p33 NVarChar(1) SET @p33 = '[' 
DECLARE @p34 NVarChar(1) SET @p34 = '?' 
DECLARE @p35 NVarChar(1) SET @p35 = ']' 
DECLARE @p36 NVarChar(1) SET @p36 = '\' 
DECLARE @p37 NVarChar(1) SET @p37 = '|' 
DECLARE @p38 NVarChar(1) SET @p38 = '<' 
DECLARE @p39 NVarChar(1) SET @p39 = '>' 
DECLARE @p40 NVarChar(1) SET @p40 = '@' 
DECLARE @p41 NVarChar(1) SET @p41 = '-' 

return REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
     @string, @p22, @es), @p23, @es), @p24, @es), @p25, @es), @p26, @es), @p27, @es), @p28, @es), @p29, @es), @p30, @es), @p31, @es), @p32, @es), @p33, @es), @p34, @es), @p35, @es), @p36, @es), @p37, @es), @p38, @es), @p39, @es), @p40, @es), @p41, @es) 
END 

Esto entonces se puede utilizar, además de la otra reemplazar cadenas

SELECT * 
FROM t1,t2 
WHERE trimChars(REPLACE(REPLACE(t1.stringkey,@p0, @es), @p1, @es) 
     = REPLACE(REPLACE(t2.stringkey,@p0, @es), @p1, @es)) 

he creado un par de funciones más que ver similares reemplazando así como trimChars (trimMoreChars (

SELECT * 
FROM t1,t2 
WHERE trimChars(trimMoreChars(REPLACE(REPLACE(t1.stringkey,@p0, @es), @p1, @es) 
     = REPLACE(REPLACE(t2.stringkey,@p0, @es), @p1, @es))) 

¿Puede alguien darme una mejor solución a este problema en términos de rendimiento y quizás una implementación más limpia?

+0

¿Puede desinfectar su entrada antes de pasarla a la base de datos? – Juliet

+1

¿Qué tan grandes son las tablas t1 y t2? ¿Es posible hacer el trabajo fuera de la base de datos? Parece un trabajo para expresiones regulares. –

Respuesta

20

Consideraría seriamente making a CLR UDFinstead y el uso de expresiones regulares (tanto la cadena como el patrón se pueden pasar como parámetros) para realizar una búsqueda completa y reemplazarla por un rango de caracteres. Debe superar fácilmente a este UDF de SQL.

40

Un truco útil en SQL es la capacidad de usar @var = function(...) para asignar un valor. Si tiene varios registros en su conjunto de registros, su var está asignado varias veces con efectos secundarios:

declare @badStrings table (item varchar(50)) 

INSERT INTO @badStrings(item) 
SELECT '>' UNION ALL 
SELECT '<' UNION ALL 
SELECT '(' UNION ALL 
SELECT ')' UNION ALL 
SELECT '!' UNION ALL 
SELECT '?' UNION ALL 
SELECT '@' 

declare @testString varchar(100), @newString varchar(100) 

set @teststring = 'Juliet ro><0zs my s0x()[email protected][email protected][email protected]!' 
set @newString = @testString 

SELECT @newString = Replace(@newString, item, '') FROM @badStrings 

select @newString -- returns 'Juliet ro0zs my s0xrzone' 
+0

Esto es genial - ¿cómo se incorpora esto dentro de la cláusula where en mi pregunta anterior? - Gracias – kiev

+0

@kiev: no se puede poner esto en una cláusula WHERE. –

+2

@kiev: la creación de una función definida por el usuario es el enfoque correcto. Sin embargo, es mejor usar mi enfoque en lugar de anidar un bajillon sustituyéndolo entre sí, ya que mi enfoque admite un número indefinido de reemplazos. Puede hacer que la función sea más dinámica pasando una lista de cadenas separadas por comas para reemplazar, usando una función de división (http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=50648) para convertir la lista en una mesa, luego devuelve la cadena reemplazada. – Juliet

0

Una opción es utilizar una tabla de números/recuento para conducir un proceso iterativo a través de una consulta basada seudo-set .

La idea general de la sustitución de carbón se puede demostrar con un simple mapa de caracteres enfoque de tabla:

create table charMap (srcChar char(1), replaceChar char(1)) 
insert charMap values ('a', 'z') 
insert charMap values ('b', 'y') 


create table testChar(srcChar char(1)) 
insert testChar values ('1') 
insert testChar values ('a') 
insert testChar values ('2') 
insert testChar values ('b') 

select 
coalesce(charMap.replaceChar, testChar.srcChar) as charData 
from testChar left join charMap on testChar.srcChar = charMap.srcChar 

A continuación, se puede llevar en el enfoque de tabla de conteo para hacer la búsqueda en cada posición de carácter en la cadena.

create table tally (i int) 
declare @i int 
set @i = 1 
while @i <= 256 begin 
    insert tally values (@i) 
    set @i = @i + 1 
end 

create table testData (testString char(10)) 
insert testData values ('123a456') 
insert testData values ('123ab456') 
insert testData values ('123b456') 

select 
    i, 
    SUBSTRING(testString, i, 1) as srcChar, 
    coalesce(charMap.replaceChar, SUBSTRING(testString, i, 1)) as charData 
from testData cross join tally 
    left join charMap on SUBSTRING(testString, i, 1) = charMap.srcChar 
where i <= LEN(testString) 
0

No sé por qué Charles Bretana borra su respuesta, así que estoy añadiendo de nuevo en CW como una respuesta, pero una columna calculada PERSISTED es realmente una buena manera de manejar estos casos donde se necesita limpiado o datos transformados casi todo el tiempo, pero necesitan preservar la basura original. Su sugerencia es relevante y apropiada INDEPENDIENTEMENTE de cómo decida limpiar sus datos.

Específicamente, en mi proyecto actual, tengo una columna calculada persistente que recorta todos los ceros iniciales (afortunadamente esto se maneja de manera muy fácil en T-SQL directo) de algunos identificadores numéricos particulares almacenados de manera incoherente con ceros a la izquierda. Esto se almacena en columnas calculadas persistentes en las tablas que lo necesitan y se indexa porque ese identificador conformado se usa a menudo en las uniones.

2

Le sugiero que cree una función escalar definida por el usuario. Este es un ejemplo (lo siento por adelantado, ya que los nombres de las variables están en español):

CREATE FUNCTION [dbo].[Udf_ReplaceChars] (
    @cadena VARCHAR(500), -- String to manipulate 
    @caracteresElim VARCHAR(100), -- String of characters to be replaced 
    @caracteresReem VARCHAR(100) -- String of characters for replacement 
) 
RETURNS VARCHAR(500) 
AS 
BEGIN 
    DECLARE @cadenaFinal VARCHAR(500), @longCad INT, @pos INT, @caracter CHAR(1), @posCarER INT; 
    SELECT 
    @cadenaFinal = '', 
    @longCad = LEN(@cadena), 
    @pos = 1; 

    IF LEN(@caracteresElim)<>LEN(@caracteresReem) 
    BEGIN 
     RETURN NULL; 
    END 

    WHILE @pos <= @longCad 
    BEGIN 
     SELECT 
     @caracter = SUBSTRING(@cadena,@pos,1), 
     @pos = @pos + 1, 
     @posCarER = CHARINDEX(@caracter,@caracteresElim); 

     IF @posCarER <= 0 
     BEGIN 
      SET @cadenaFinal = @cadenaFinal + @caracter; 
     END 
     ELSE 
     BEGIN 
      SET @cadenaFinal = @cadenaFinal + SUBSTRING(@caracteresReem,@posCarER,1) 
     END 
    END 

    RETURN @cadenaFinal; 
END 

Aquí hay un ejemplo usando esta función:

SELECT dbo.Udf_ReplaceChars('This is a test.','sat','Z47'); 

Y el resultado es: 7hiZ iZ 4 7eZ7 .

Como puede ver, cada carácter del parámetro @caracteresElim se reemplaza por el carácter en la misma posición del parámetro @caracteresReem.

+0

parece una buena solución – kiev

-1

Estos son los pasos

  1. crear una función CLR

Ver siguiente código:

public partial class UserDefinedFunctions 
{ 

[Microsoft.SqlServer.Server.SqlFunction] 
public static SqlString Replace2(SqlString inputtext, SqlString filter,SqlString  replacewith) 
{ 

    string str = inputtext.ToString(); 
    try 
    { 
     string pattern = (string)filter; 
     string replacement = (string)replacewith; 
     Regex rgx = new Regex(pattern); 
     string result = rgx.Replace(str, replacement); 
     return (SqlString)result; 

    } 
    catch (Exception s) 
    { 
     return (SqlString)s.Message; 
    } 
} 
} 
  1. implementar la función CLR

  2. Ahora probarlo

Ver siguiente código:

create table dbo.test(dummydata varchar(255)) 
Go 
INSERT INTO dbo.test values('[email protected]'),('This 12is @test') 
Go 
Update dbo.test 
set dummydata=dbo.Replace2(dummydata,'[[email protected]]','') 

select * from dbo.test 
dummydata, Psswrd, This is test booom!!!!!!!!!!!!! 
11

me gusta @ solución de Juliett! Yo sólo tiene que utilizar un CTE para obtener todos los caracteres no válidos:

DECLARE @badStrings VARCHAR(100) 
DECLARE @teststring VARCHAR(100) 

SET @badStrings = '><()[email protected]' 
SET @teststring = 'Juliet ro><0zs my s0x()[email protected][email protected][email protected]!' 

;WITH CTE AS 
(
    SELECT SUBSTRING(@badStrings, 1, 1) AS [String], 1 AS [Start], 1 AS [Counter] 
    UNION ALL 
    SELECT SUBSTRING(@badStrings, [Start] + 1, 1) AS [String], [Start] + 1, [Counter] + 1 
    FROM CTE 
    WHERE [Counter] < LEN(@badStrings) 
) 

SELECT @teststring = REPLACE(@teststring, CTE.[String], '') FROM CTE 

SELECT @teststring 

Julieta ro0zs mi s0xrzone

+0

Esta solución rompió mi awesometer. Utiliza un CTE recursivo, y luego un REEMPLAZO recursivo de @teststring ... – Baodad

+0

Esto es asombroso.Sin embargo, ten cuidado porque esto no captará un espacio final en @badStrings. –

+0

¿Se puede aplicar esta solución a una tabla de selección? IE Seleccione el campo donde el valor en ('1', '2') seleccionará el campo '<>! 1' – Adam

1
declare @testVal varchar(20) 

set @testVal = '?t/es?ti/n*g 1*2?3*' 

select @testVal = REPLACE(@testVal, item, '') from (select '?' item union select '*' union select '/') list 

select @testVal; 
Cuestiones relacionadas