将列从 NVARCHAR 转换为 VARCHAR

equ*_*pe9 5 sql-server datatypes varchar type-conversion sql-server-2016

我有几个有很多NVARCHAR(MAX)列的大表。该数据库仅供美国使用,我认为任何列中都没有任何外来字符或字母,所以我想调查一下:

  1. 将其中许多列从 更改为NVARCHARVARCHAR并且
  2. 根据我所做的一些数据分析,将这些列的大小调整为更合适的大小。例如,我知道其中某些列中数据的最大长度仅为 30 个字符左右,因此NVARCHAR(MAX)看起来有些过大。

我还试图限制隐式转换。

在执行此操作之前,有什么简单的方法可以确定每列是否确实只包含VARCHAR数据并且不会出现任何转换问题NVARCHAR

Sol*_*zky 6

\n

该数据库仅供美国使用,我认为任何列中都没有任何外来字符或字母,因此我想调查一下...将其中许多列从 NVARCHAR 更改为 VARCHAR

\n
\n\n

在继续之前,您需要研究/确定这些列的实际业务需求。如果这些列中的一个或多个当前确实没有包含任何不能轻松放入 的字符VARCHAR,这并不意味着明天有人不会尝试输入纯 Unicode 字符,并且这样做将超出系统的预期设计。如果最终用户/销售人员/支持人员/文档认为可以在系统中输入纯 Unicode 字符,那么系统需要允许这一点,即使尚未有人使用该功能。如果是这种情况,并且您认为应该更改它,因为您认为不需要输入此类字符,那么您需要将其提交给经理/产品负责人/等。

\n\n

此外,仅仅因为目标用户群在美国并不意味着不会使用纯 Unicode 字符。至少在“名称”列、“描述”列等中很容易存在各种重音字符,或者谁知道是什么。您应该与产品负责人和/或团队进行的讨论应围绕每个列/数据点的需求展开。

\n\n
\n

例如,我知道其中一些列中数据的最大长度仅为 30 个字符左右,因此 NVARCHAR(MAX) 似乎有点矫枉过正。

\n
\n\n

与上面关于这些列中应允许使用哪些类型的字符的观点类似,您首先需要确定系统允许人们发送多少个字符。如果 UI 中的描述字段上限为 500 - 1000 个字符,并且代码的其他部分,甚至文档(人们总是可以希望,对吧?),同意,那么绝对是,NVARCHAR(MAX)绝对是矫枉过正。但是,如果该列需要存储超过 4000 个不是由 8 位代码页表示的各种字符,那么NVARCHAR(MAX)也不算过分(尽管您可能需要考虑更好的设计,而不是在单个列中存储太多字符)桌子)。不管怎样,特定的内容最多只有 30 个字符NVARCHAR(MAX)是向产品负责人/团队提供的良好信息,以便就该列的命运做出更明智的决定。也许当前允许 1000 个字符但最大记录长度为 30 - 50 的字段应该更新为仅允许 75 - 100。但这需要协调一致的努力。

\n\n

综上所述,如果您想确定NVARCHAR//列中是否有任何字符无法转换为,则需要转换为使用NCHAR该特定列所使用的排序规则的变体。例如,如果特定列正在使用,那么您将指定进行测试。使用排序规则的原因是,非二进制排序规则只会查找至少有一个字符在代码页中根本没有任何映射并因此转换为. 但是,非二进制排序规则不会捕获某些字符没有直接映射到代码页的实例,而是具有“最适合”映射。例如,上标 2 字符 ,在代码页 1252 中具有直接映射,因此绝对没有问题。另一方面,它在代码页 1250 中没有直接映射(由阿尔巴尼亚排序规则使用),但它确实有一个“最佳拟合”映射,可以将其转换为常规. 非二进制排序规则的问题在于,它将等于,因此它不会注册为无法转换为 的行。例如:NTEXTVARCHARVARCHAR_BIN2Albanian_100_CI_ASAlbanian_100_BIN2_BIN2?\xc2\xb222\xc2\xb2VARCHAR

\n\n
SELECT CONVERT(VARCHAR(MAX), N\'\xc2\xb2\' COLLATE French_100_CI_AS); -- Code Page 1252\n-- \xc2\xb2\nSELECT CONVERT(VARCHAR(MAX), N\'\xc2\xb2\' COLLATE Albanian_100_CI_AS); -- Code Page 1250\n-- 2\n\nSELECT CONVERT(VARCHAR(MAX), N\'\xc2\xb2\' COLLATE Albanian_100_CI_AS)\nWHERE  N\'\xc2\xb2\' <> CONVERT(NVARCHAR(MAX),\n                       CONVERT(VARCHAR(MAX), N\'\xc2\xb2\' COLLATE Albanian_100_CI_AS));\n-- (no rows returned)\n\nSELECT CONVERT(VARCHAR(MAX), N\'\xc2\xb2\' COLLATE Albanian_100_BIN2)\nWHERE  N\'\xc2\xb2\' <> CONVERT(NVARCHAR(MAX),\n                       CONVERT(VARCHAR(MAX), N\'\xc2\xb2\' COLLATE Albanian_100_BIN2));\n-- 2\n
Run Code Online (Sandbox Code Playgroud)\n\n

理想情况下,您应该显式转换回 ,NVARCHAR以便代码清楚地了解它在做什么,但不这样做仍会隐式转换回NVARCHAR,因此无论哪种方式,行为都是相同的。

\n


Sup*_*pez 2

我相信您正在寻找一个 select 语句来找到 ASCII (VARCHAR) 不支持的字符串,对吗?

下面引用的答案提供了这样的声明(带有一些示例表结构)。此语句的作用是将存储为 NVARCHAR (Unicode) 的值与转换为 VARCHAR (ASCII) 的相同值进行比较。如果查询没有返回任何内容,则可以进行转换。如果行中包含任何列的值与存储为 VARCHAR 的值不同,则将返回该列。
我从下面的其他答案中复制了示例:

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))
Run Code Online (Sandbox Code Playgroud)

/sf/answers/2087373851/