查询以查找给定范围内包含 ASCII 字符的行

Fre*_*red 6 sql-server collation t-sql character-set encoding

我正在使用另一个主题的一些脚本,但接受的答案不适用于我的所有数据场景。我会问我关于如何检查非 Ascii 字符的原始帖子的问题,但我还没有足够的声誉来评论或投票。

问题:

我的测试

我使用示例数据、其中一个答案中的存储过程以及用于演示该问题的查询创建了SQL Fiddle

查询 1:sample_table

-- Note: The "bad dash" row has char(150)

SELECT * FROM sample_table;

+-------------------+
|    DataColumn     |
+-------------------+
| test - good dash  |
| test – bad dash   |
+-------------------+
Run Code Online (Sandbox Code Playgroud)

查询2:其它答案约翰表示“坏破折号”行含有炭(150):

SELECT dbo.Find_Invalid_Chars(DataColumn) [Invalid Characters]
FROM sample_table
WHERE dbo.Find_Invalid_Chars(DataColumn) IS NOT NULL;

+----------------------+
|  Invalid Characters  |
+----------------------+
| test [150] bad dash  |
+----------------------+
Run Code Online (Sandbox Code Playgroud)

查询3:接受的答案马丁·史密斯返回任何结果

SELECT DataColumn AS [Bad Data]
FROM sample_table
WHERE DataColumn LIKE '%[' + CHAR(127)+ '-' +CHAR(255)+']%' COLLATE Latin1_General_100_BIN2;

+------------+
| [Bad Data] |
+------------+

-- No rows returned.
Run Code Online (Sandbox Code Playgroud)

结论

不幸的是,我经常需要在无法在其中创建存储过程的数据库范围内(或之外)查找字符。我真的很想找到已接受答案的修复程序或不需要创建的简单脚本任何对象(包括临时表)。

有什么建议?提前致谢。

编辑 1:该解决方案无法修改或添加数据库中的任何对象或设置。我正在寻找一个独立的查询CHAR(),无论提供的是 ASCII 还是扩展 ASCII 数字,它都会选择在两个数字之间的范围内包含一个或多个字符的行。

编辑 2: DataColumn 可以在VARCHAR或 中NVARCHAR。我无法控制这一点,所以我希望找出一个适用于两者的独立查询。查询的目的是在源表/列中查找某些软件应用程序未正确处理的字符。应用程序正在正确解释源,但有时会出现“标准”范围之外的字符问题,尽管范围因应用程序而异。

Sol*_*zky 5

\n

为什么接受的答案不适用于 char(150)?

\n
\n

事实上,确实如此。问题是你的测试不好/无效。您测试列 ,DataColumn正在使用NVARCHAR而不是VARCHAR。字符本身适用于两种数据类型,但由于每种情况下的使用方式不同,其行为也有所不同:

\n
    \n
  • Find_Invalid_Chars()函数(即“其他”答案)中,字符串被转换回,VARCHAR因为这是该函数的输入参数的数据类型。在这种情况下,它按预期工作(尽管我相信它可以比该循环更有效地完成,但那是另一个时间了;-)
  • \n
  • LIKE查询(即“接受”答案)中,扩展和连接的结果\'%[\' + CHAR(127)+ \'-\' +CHAR(255)+\']%\'实际上被转换为NVARCHAR,因为这是与之比较的列的数据类型(并且NVARCHAR具有更高的数据类型优先级),因此该LIKE函数没有表现正如预期的那样:CHAR(255)字符映射到不同的代码点,和/或CHAR(150)列本身中的字符映射到不同的代码点(CHAR(127)字符不会更改,因为它位于标准 ASCII 范围内)。无论哪种情况,转换都会NVARCHAR导致“En Dash”字符(“\xe2\x80\x93”)的数值不再处于该范围内。意思是,该LIKE函数正在查找和y之间的值(其中>= 128),并且“En Dash”字符现在为 > 。而在 中,= 255 且= 150。127xxyxVARCHARxy
  • \n
\n

看到它确实有效的快速修复方法是将列NVARCHAR的数据类型 更改DataColumnVARCHAR(是的,只需删除开头的“N”),然后重新构建架构,然后执行,查询LIKE将按预期运行。

\n

以下内容可能有助于解释为什么创建测试列NVARCHAR导致LIKE查询与行不匹配:

\n
SELECT UNICODE(CHAR(127)) AS [CHAR(127)],\n       UNICODE(CHAR(150)) AS [CHAR(150)],\n       UNICODE(CHAR(255)) AS [CHAR(255)];\n\n/*\nCHAR(127)     CHAR(150)     CHAR(255)\n127           8211          255\n*/\n
Run Code Online (Sandbox Code Playgroud)\n

正如您在查询下面的结果中看到的,“坏破折号”是存储在列中时CHAR(150)变成的。而且,由于该谓词使用二进制排序规则(在这种情况下通常是正确的做法),因此它查看的是代码点/值,而不是字符。因此,该子句正在查找值在 127 到 255 之间的字符,而 8211 通常不在该范围内;-)。NCHAR(8211)NVARCHARLIKE

\n

PS请记住,该函数CHAR(150) 可以返回不同的字符,甚至返回不同的字符NULL,具体取决于执行该函数的数据库的默认排序规则。这是因为VARCHAR数据是基于代码页的,而代码页是由排序规则决定的,而执行函数时使用的排序规则CHAR()是活动/当前数据库的默认排序规则。这会影响值 128 - 255。无论排序规则如何,值 0 - 127 将始终返回相同的字符,因为这些字符是标准 ASCII 字符集,并且在 SQL Server 支持的所有代码页中都是相同的(尽管并非在所有代码中)一般页面)。

\n

PPS另外,我刚刚注意到函数和查询之间的逻辑略有不同(即链接问题的两个答案):CHAR(127)在函数中被认为是好的/有效的Find_Invalid_Chars(),但在查询中被认为是坏的/无效的LIKE。如果是我,我会认为它CHAR(127)是有效的,因为它是标准 ASCII 字符集的一部分。但是,您需要决定您的想法。请注意这种差异,以防您确实需要LIKE稍微调整语法。

\n
\n

鉴于:

\n
    \n
  1. \n
    \n

    查询的目的是查找源表/列中某些软件应用程序未正确处理的字符。

    \n
    \n

    和:

    \n
  2. \n
  3. \n
    \n

    数据可以是 VARCHAR 或 NVARCHAR。

    \n
    \n
  4. \n
\n

我会这样说:

\n
    \n
  1. 不想NVARCHAR将源数据转换为VARCHAR,因为可能存在将无效源字符转换为有效字符的“最适合”映射,但您的一个或多个软件应用程序可能不使用“最适合”映射。

    \n
    SELECT NCHAR(178) AS [Unicode], -- Superscript 2 (U+00B2)\n       CONVERT(VARCHAR(5), NCHAR(178)\n                   COLLATE SQL_Latin1_General_CP1_CI_AS) AS [CodePage-1252],\n       CONVERT(VARCHAR(5), NCHAR(178)\n                   COLLATE Turkmen_100_CI_AS) AS [CodePage-1250]\n\n/*\nUnicode    CodePage-1252    CodePage-1250\n\xc2\xb2          \xc2\xb2                2\n*/\n
    Run Code Online (Sandbox Code Playgroud)\n
  2. \n
  3. 查找不在特定“有效”范围内的字符可能比查找在特定无效范围内的字符更可靠,尤其是在处理包含NVARCHAR远远超过256 个字符的字符时。

    \n
  4. \n
  5. 如果“有效”范围始终在值 0 和 127 之间(因为这些值在两种情况下都是相同的),那么您可以通过单个查询逃脱。但如果您需要指定高于 127 的值,那么您将需要一个查询 forVARCHAR和一个查询 for NVARCHAR

    \n
  6. \n
\n

综上所述:

\n
    \n
  • 对于和 ,以下查询返回至少包含一个不在0 - 127 范围内的字符的行。但是,它仅适用于值高于 127 的列。VARCHARNVARCHARNVARCHAR

    \n
    SELECT *\nFROM   (VALUES (NCHAR(178)), (NCHAR(8211)), (N\'\'), (NULL), (N\'xy\' + NCHAR(165)),\n           (N\'AA\'), (N\'mM\' + NCHAR(999) + N\'Nn\'), (N\'#!~()\')) tmp(TestValue)\nWHERE  tmp.[TestValue] LIKE N\'%[^\' + NCHAR(0) + N\'-\' + NCHAR(127)\n          + N\']%\' COLLATE Latin1_General_100_BIN2;\n\n/*\nTestValue\n\xc2\xb2\n\xe2\x80\x93\nxy\xc2\xa5\nmM\xcf\xa7Nn\n*/\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
  • 以下查询还返回至少包含一个不在0 - 127 范围内的字符的行,但仅适用于VARCHAR列。但是,它允许使用 128 到 255 之间的值。

    \n
    SELECT *\nFROM   (VALUES (CHAR(178)), (CHAR(150)), (\'\'), (NULL), (\'AA\'), (\'#!~()\'),\n        (\'xy\' + CONVERT(VARCHAR(5), NCHAR(165) COLLATE Latin1_General_100_BIN2)),\n        (\'mM\' + CONVERT(VARCHAR(5), NCHAR(199) COLLATE Latin1_General_100_BIN2) + \'Nn\')\n       ) tmp(TestValue)\nWHERE  tmp.[TestValue] LIKE \'%[^\' + CHAR(0) + \'-\' + CHAR(127)\n          + \']%\' COLLATE Latin1_General_100_BIN2;\n\n/*\nTestValue\n\xc2\xb2\n\xe2\x80\x93\nxy\xc2\xa5\nmM\xc3\x87Nn\n*/\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
\n
\n

关于:

\n
\n

应用程序可以正确解释源,但有时会出现“标准”范围之外的字符问题,尽管范围因应用程序而异。

\n
\n
    \n
  1. 不确定我是否理解如果应用程序正确解释源数据,某些字符如何会出现“问题”,除非您的意思是它们“大部分”正确解释数据。
  2. \n
  3. 范围因应用程序而异,听起来可能需要比这样的简单问答格式进行更详细的调查。此行为可能是由于它们使用不同的驱动程序进行连接(ODBC / OLEDB / 等)、它们使用什么语言编写、它们对所获取的数据做出什么假设等等。有些问题可能可以通过应用程序的配置(不更改代码)来修复,有些问题可能只能通过更改代码来修复,等等。
  4. \n
\n