2009-03-26 9 views

Respuesta

15

Primero construya una cadena con todos los caracteres que no le interesen (el ejemplo usa el rango 0x20 - 0x7F, o 7 bits sin los caracteres de control.) Cada carácter tiene el prefijo |, para usar en la cláusula de escape luego.

-- Start with tab, line feed, carriage return 
declare @str varchar(1024) 
set @str = '|' + char(9) + '|' + char(10) + '|' + char(13) 

-- Add all normal ASCII characters (32 -> 127) 
declare @i int 
set @i = 32 
while @i <= 127 
    begin 
    -- Uses | to escape, could be any character 
    set @str = @str + '|' + char(@i) 
    set @i = @i + 1 
    end 

El siguiente fragmento busca cualquier carácter que no esté en la lista. El% coincide con 0 o más caracteres. [] Coincide con uno de los caracteres dentro de [], por ejemplo, [abc] podría coincidir con a, bo c. El^niega la lista, por ejemplo [^ abc] coincidiría con cualquier cosa que no sea a, b, o c.

select * 
from yourtable 
where yourfield like '%[^' + @str + ']%' escape '|' 

Se requiere que el carácter de escape porque de lo contrario la búsqueda de personajes como],% o _ ensuciaría la expresión similares.

Espero que esto sea útil, y gracias al comentario de JohnFX en la otra respuesta.

+0

Es posible que desee agregar algunos (o todos) de los personajes por debajo de 32 y, sobre todo importante sería el retorno de carro (13), la línea de alimentación (10) y Tab (9). –

+0

Buen punto agregado – Andomar

+1

Encontré que su técnica es la forma más confiable de hacerlo en SQL-Server. – cusman

2

Probablemente no sea la mejor solución, pero tal vez una consulta como:

SELECT * 
FROM yourTable 
WHERE yourTable.yourColumn LIKE '%[^0-9a-zA-Z]%' 

sustituir la expresión "0-9a-zA-Z" con algo que captura el conjunto ASCII completo (o un subconjunto de que su datos contiene).

+0

¿No coincidiría esto solo con las filas que contienen un carácter ASCII, a diferencia de solo los caracteres ASCII? – Andomar

+0

El^marcador en el frente de la expresión significa NO, entonces no. Obtendría cualquier fila que tuviera al menos un carácter que no estuviera en los rangos especificados. – JohnFx

+0

¿Cómo puedo poner el conjunto ascii completo en esa expresión? son los datos HTML los que estoy viendo así que "/><'etc ... está ahí. – TheSoftwareJedi

0

Mi respuesta anterior confundía datos UNICODE/no UNICODE. Aquí hay una solución que debería funcionar para todas las situaciones, aunque todavía me encuentro con algunas anomalías. Parece que ciertos caracteres Unicode que no son ASCII para caracteres superíndices se confunden con el carácter numérico real. Es posible que pueda jugar con colaciones para evitar eso.

Espero que ya tengas una tabla de números en tu base de datos (pueden ser muy útiles), pero solo en caso de que haya incluido el código para llenarlo parcialmente también.

También podría ser necesario para jugar un poco con el rango numérico, ya que los caracteres Unicode pueden ir más allá de 255.

CREATE TABLE dbo.Numbers 
(
    number INT NOT NULL, 
    CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (number) 
) 
GO 
DECLARE @i INT 

SET @i = 0 

WHILE @i < 1000 
BEGIN 
    INSERT INTO dbo.Numbers (number) VALUES (@i) 

    SET @i = @i + 1 
END 
GO 

SELECT *, 
    T.ID, N.number, N'%' + NCHAR(N.number) + N'%' 
FROM 
    dbo.Numbers N 
INNER JOIN dbo.My_Table T ON 
    T.description LIKE N'%' + NCHAR(N.number) + N'%' OR 
    T.summary LIKE N'%' + NCHAR(N.number) + N'%' 
and t.id = 1 
WHERE 
    N.number BETWEEN 127 AND 255 
ORDER BY 
    T.id, N.number 
GO 
+0

De la manera que lo entiendo, ASCII es de 7 bits y varchar de 8 bits. Entonces, varchar aún puede almacenar una gran cantidad de caracteres que no son ascii, como ä o é. – Andomar

+0

Estoy viendo los mismos resultados. Esto no funciona – TheSoftwareJedi

+1

ASCII extendido es de 8 bits, que es a lo que algunas personas se refieren cuando dicen "ASCII". También editaré la publicación para limitarla a ASCII normal. –

-1

- Esta es una forma muy, muy ineficiente de hacerlo, pero debe estar bien para - Mesas pequeñas Utiliza una tabla auxiliar de números según Itzik Ben-Gan y simplemente - busca caracteres con el bit 7 establecido.

SELECT * 
FROM yourTable as t 
WHERE EXISTS (SELECT * 
       FROM msdb..Nums as NaturalNumbers 
       WHERE NaturalNumbers.n < LEN(t.string_column) 
         AND ASCII(SUBSTRING(t.string_column, NaturalNumbers.n, 1)) > 127) 
3

Técnicamente, creo que un NCHAR (1) es un carácter ASCII válidos si & sólo si UNICODE (@NChar) < 256 y ASCII (@NChar) = UNICODE (@NChar) aunque eso no puede ser exactamente lo que pretendías Por lo tanto, esta sería una solución correcta:

;With cteNumbers as 
(
    Select ROW_NUMBER() Over(Order By c1.object_id) as N 
    From sys.system_columns c1, sys.system_columns c2 
) 
Select Distinct RowID 
From YourTable t 
    Join cteNumbers n ON n <= Len(CAST(TXT As NVarchar(MAX))) 
Where UNICODE(Substring(TXT, n.N, 1)) > 255 
    OR UNICODE(Substring(TXT, n.N, 1)) <> ASCII(Substring(TXT, n.N, 1)) 

Esto también debería ser muy rápido.

+0

ASCII solo tiene 127. También sus números cte son raros: la solución final debe usar una tabla de números preexistente en lugar de la misma. De lo contrario, así es como lo haría. –

+0

FYI: "No parecer raro" no es un criterio que realmente me importe a mí (oa la mayoría de los súper geeks de este sitio). Y la ventaja de no usar una tabla de números preexistente, es que funciona incluso si dicha tabla no existe previamente (lo que generalmente no sucede). Finalmente, si prueba mi solución, encontrará que es comparable y, a veces, incluso mejor que una tabla de números preexistente. – RBarryYoung

+0

Realmente no quiero discutir: estoy de acuerdo con su solución. Pero "raro" es generalmente difícil de leer y no se puede mantener. Así es como lo dije en serio. Tampoco es DRY si alguna vez tienes dos consultas diferentes que necesitan números: tienes que escribir el cte no mantenible dos veces. Leer números de fila de una combinación de system_columns en sí mismo no es muy sencillo, pero leer números de una tabla de números sería muy sencillo de depurar. –

40

a veces he estado usando esta declaración "fundido" para encontrar caracteres "extraños"

select 
    * 
from 
    <Table> 
where 
    <Field> != cast(<Field> as varchar(1000)) 
+0

Esto funcionó para mí y fue mucho más rápido que la solución de RBarryYoung – Trisped

+1

No funciona si la intercalación está configurada en una intercalación" no ASCII ". – deerchao

+0

Brillante respuesta –

1

Empecé con una solución de @ CC1960, pero encontró un caso de uso interesante que causó el error.Parece que SQL Server igualará ciertos caracteres Unicode a sus aproximaciones que no sean Unicode. Por ejemplo, SQL Server considera el carácter Unicode "coma de ancho completo" (http://www.fileformat.info/info/unicode/char/ff0c/index.htm) lo mismo que una coma ASCII estándar cuando se compara en una cláusula WHERE.

Para evitar esto, haga que SQL Server compare las cadenas como binarias. Pero recuerde, nvarchar y varchar binarios no coinciden (16 bits vs 8 bits), por lo que necesita para convertir su copia de seguridad varchar a nvarchar de nuevo antes de hacer la comparación binaria:

select * 
from my_table 
where CONVERT(binary(5000),my_table.my_column) != CONVERT(binary(5000),CONVERT(nvarchar(1000),CONVERT(varchar(1000),my_table.my_column))) 
1

Si está buscando un caracter Unicode específico, puede usar algo como a continuación.

select Fieldname from 
    (
     select Fieldname, 
      REPLACE(Fieldname COLLATE Latin1_General_BIN, 
      NCHAR(65533) COLLATE Latin1_General_BIN, 
      'CustomText123') replacedcol 
     from table 
    ) results where results.replacedcol like '%CustomText123%' 
2

Aquí ya go:

SELECT * 
FROM Objects 
WHERE 
    ObjectKey LIKE '%[^0-9a-zA-Z !"#$%&''()*+,\-./:;<=>[email protected]\[\^_`{|}~\]\\]%' ESCAPE '\' 
Cuestiones relacionadas