我有下表:
Select
name,
address,
description
from dbo.users
我想在这个表中搜索任何UNICODE但不是ASCII的字符。这可能吗?
如果要确定NVARCHAR
/ NCHAR
/ NTEXT
列中是否有任何字符无法转换为VARCHAR
,则需要使用用于该特定列的排序规则的VARCHAR
变体转换为_BIN2
。例如,如果某个特定列使用的是Albanian_100_CI_AS
,那么您可以为测试指定Albanian_100_BIN2
。使用_BIN2
排序规则的原因是非二进制排序规则只能找到在代码页中至少有一个字符完全没有任何映射的情况,因此转换为?
。但是,非二进制排序规则不会捕获没有直接映射到代码页的字符的实例,而是具有“最适合”的映射。例如,上标2字符²
在代码页1252中具有直接映射,因此绝对没有问题。另一方面,它在代码页1250中没有直接映射(由阿尔巴尼亚排序规则使用),但它确实具有“最佳拟合”映射,将其转换为常规2
。非二进制排序规则的问题是2
将等同于²
,因此它不会注册为无法转换为VARCHAR
的行。例如:
SELECT CONVERT(VARCHAR(MAX), N'²' COLLATE French_100_CI_AS); -- Code Page 1252
-- ²
SELECT CONVERT(VARCHAR(MAX), N'²' COLLATE Albanian_100_CI_AS); -- Code Page 1250
-- 2
SELECT CONVERT(VARCHAR(MAX), N'²' COLLATE Albanian_100_CI_AS)
WHERE N'²' <> CONVERT(NVARCHAR(MAX),
CONVERT(VARCHAR(MAX), N'²' COLLATE Albanian_100_CI_AS));
-- (no rows returned)
SELECT CONVERT(VARCHAR(MAX), N'²' COLLATE Albanian_100_BIN2)
WHERE N'²' <> CONVERT(NVARCHAR(MAX),
CONVERT(VARCHAR(MAX), N'²' COLLATE Albanian_100_BIN2));
-- 2
理想情况下,你会明确地转换回NVARCHAR
,以便代码清楚地知道它正在做什么,虽然不这样做仍然会隐式转换回NVARCHAR
,所以行为是相同的。
请注意,仅使用MAX
类型。不要使用NVARCHAR(4000)
或VARCHAR(4000)
否则你可能会因NVARCHAR(MAX)
列中的数据截断而得到误报。
因此,就问题中的示例代码而言,查询将是(假设正在使用Latin1_General
排序规则):
SELECT usr.*
FROM dbo.[users] usr
WHERE usr.[name] <> CONVERT(NVARCHAR(MAX),
CONVERT(VARCHAR(MAX), usr.[name] COLLATE Latin1_General_100_BIN2))
OR usr.[address] <> CONVERT(NVARCHAR(MAX),
CONVERT(VARCHAR(MAX), usr.[address] COLLATE Latin1_General_100_BIN2))
OR usr.[description] <> CONVERT(NVARCHAR(MAX),
CONVERT(VARCHAR(MAX), usr.[description] COLLATE Latin1_General_100_BIN2));
您可以非常简单地找到非ASCII字符:
SELECT NAME, ADDRESS, DESCRIPTION
FROM DBO.USERS
WHERE NAME != CAST(NAME AS VARCHAR(4000))
OR ADDRESS != CAST(ADDRESS AS VARCHAR(4000))
OR DESCRIPTION != CAST(DESCRIPTION AS VARCHAR(4000))
据我所知,似乎没有内置功能。蛮力方法是将每个字符传递给ascii
然后将结果传递给char
并检查它是否返回'?',这意味着该字符超出范围。您可以使用以下代码编写UDF作为参考,但我认为这是一个非常低效的解决方案:
declare @i int = 1
declare @x nvarchar(10) = N'vsdǣf'
declare @result nvarchar(100) = N''
while (@i < len(@x))
begin
if char(ascii(substring(@x,@i,1))) = '?'
begin
set @result = @result + substring(@x,@i,1)
end
set @i = @i+1
end
select @result