Joh*_*ten 15 sql-server datatypes unicode
我继承了一些 SQL Server 数据库。有一个表(我称之为“G”),大约有 8670 万行,41 列宽,来自 SQL Server 2014 Standard 上的源数据库(我称之为“Q”),它得到了 ETL在 SQL Server 2008 R2 Standard 上具有相同表名的目标数据库(我将称之为“P”)。
即 [Q].[G] ---> [P].[G]
编辑:2017 年 3 月 20 日:有人问过源表是否是目标表的唯一源。是的,它是唯一的来源。就 ETL 而言,并没有发生任何真正的转变;它实际上旨在成为源数据的 1:1 副本。因此,没有计划向此目标表添加其他源。
[Q].[G] 中略多于一半的列是 VARCHAR(源表):
同样,[P].[G] 中的相同列是 NVARCHAR(目标表),具有相同宽度的相同列数。(换句话说,长度相同,但 NVARCHAR)。
这不是我的设计。
我想将 [P].[G](目标)列数据类型从 NVARCHAR 更改为 VARCHAR。我想安全地做到这一点(没有转换造成的数据丢失)。
如何查看目标表中每个 NVARCHAR 列中的数据值以确认该列是否实际包含任何 Unicode 数据?
可以检查每个 NVARCHAR 列的每个值(在循环中?)并告诉我是否有任何值是真正的 Unicode 的查询(DMV?)是理想的解决方案,但欢迎使用其他方法。
Joe*_*ish 10
假设您的一列不包含任何 unicode 数据。要验证您是否需要读取每一行的列值。除非在列上有索引,否则对于行存储表,您将需要从表中读取每个数据页。考虑到这一点,我认为将所有列检查组合到针对表的单个查询中是很有意义的。这样你就不会多次读取表的数据,也不必编写游标或其他类型的循环。
要检查单个列,相信您可以这样做:
SELECT COLUMN_1
FROM [P].[Q]
WHERE CAST(COLUMN_1 AS VARCHAR(80)) <> CAST(COLUMN_1 AS NVARCHAR(80));
Run Code Online (Sandbox Code Playgroud)
除非有 unicode 字符,否则从NVARCHAR
to 转换VARCHAR
应该给你相同的结果。Unicode 字符将被转换为?
. 所以上面的代码应该NULL
正确处理案例。您有 24 列要检查,因此您可以使用标量聚合检查单个查询中的每一列。一种实现如下:
SELECT
MAX(CASE WHEN CAST(COLUMN_1 AS VARCHAR(80)) <> CAST(COLUMN_1 AS NVARCHAR(80)) THEN 1 ELSE 0 END) COLUMN_1_RESULT
...
, MAX(CASE WHEN CAST(COLUMN_14 AS VARCHAR(30)) <> CAST(COLUMN_14 AS NVARCHAR(30)) THEN 1 ELSE 0 END) COLUMN_14_RESULT
...
, MAX(CASE WHEN CAST(COLUMN_23 AS VARCHAR(8)) <> CAST(COLUMN_23 AS NVARCHAR(8)) THEN 1 ELSE 0 END) COLUMN_23_RESULT
FROM [P].[Q];
Run Code Online (Sandbox Code Playgroud)
对于每一列,您将获得其1
任何值是否包含 unicode 的结果。结果0
意味着可以安全地转换所有数据。
我强烈建议使用新的列定义制作表的副本并将数据复制到那里。如果您就地进行转换,您将进行昂贵的转换,因此制作副本可能不会那么慢。拥有副本意味着您可以轻松验证所有数据是否仍然存在(一种方法是使用EXCEPT关键字),并且您可以非常轻松地撤消操作。
另外,请注意您目前可能没有任何 unicode 数据,未来的 ETL 可能会将 unicode 加载到以前干净的列中。如果在您的 ETL 过程中没有对此进行检查,您应该考虑在进行此转换之前添加它。
在做任何事情之前,请考虑@RDFozz 在对该问题的评论中提出的问题,即:
是否有任何除了其他来源
[Q].[G]
填充这个表?
如果响应是“我 100% 确定这是此目标表的唯一数据源”之外的任何响应,则不要进行任何更改,无论当前表中的数据是否可以在不使用的情况下进行转换数据丢失。
是否有任何计划/讨论与在不久的将来添加其他来源以填充此数据有关?
我想补充一个相关的问题:以前有过周围的电流源表(即支持多国语言的任何讨论[Q].[G]
通过转换),它给NVARCHAR
?
您需要四处询问以了解这些可能性。我假设你目前没有被告知任何指向这个方向的事情,否则你不会问这个问题,但如果这些问题被认为是“不”,那么他们需要被问到,并被问到足够广泛的受众来获得最准确/完整的答案。
这里的主要问题不是具有无法转换(永远)的Unicode 代码点,而是具有无法全部放入单个代码页的代码点。这就是 Unicode 的优点:它可以保存所有代码页中的字符。如果您从NVARCHAR
(无需担心代码页)转换为VARCHAR
,则需要确保目标列的排序规则使用与源列相同的代码页。这假设有一个源或多个源使用相同的代码页(但不一定是相同的排序规则)。但是如果有多个代码页的多个源,那么您可能会遇到以下问题:
DECLARE @Reporting TABLE
(
ID INT IDENTITY(1, 1) PRIMARY KEY,
SourceSlovak VARCHAR(50) COLLATE Slovak_CI_AS,
SourceHebrew VARCHAR(50) COLLATE Hebrew_CI_AS,
Destination NVARCHAR(50) COLLATE Latin1_General_CI_AS,
DestinationS VARCHAR(50) COLLATE Slovak_CI_AS,
DestinationH VARCHAR(50) COLLATE Hebrew_CI_AS
);
INSERT INTO @Reporting ([SourceSlovak]) VALUES (0xDE20FA);
INSERT INTO @Reporting ([SourceHebrew]) VALUES (0xE820FA);
UPDATE @Reporting
SET [Destination] = [SourceSlovak]
WHERE [SourceSlovak] IS NOT NULL;
UPDATE @Reporting
SET [Destination] = [SourceHebrew]
WHERE [SourceHebrew] IS NOT NULL;
SELECT * FROM @Reporting;
UPDATE @Reporting
SET [DestinationS] = [Destination],
[DestinationH] = [Destination]
SELECT * FROM @Reporting;
Run Code Online (Sandbox Code Playgroud)
返回(第二个结果集):
DECLARE @Reporting TABLE
(
ID INT IDENTITY(1, 1) PRIMARY KEY,
SourceSlovak VARCHAR(50) COLLATE Slovak_CI_AS,
SourceHebrew VARCHAR(50) COLLATE Hebrew_CI_AS,
Destination NVARCHAR(50) COLLATE Latin1_General_CI_AS,
DestinationS VARCHAR(50) COLLATE Slovak_CI_AS,
DestinationH VARCHAR(50) COLLATE Hebrew_CI_AS
);
INSERT INTO @Reporting ([SourceSlovak]) VALUES (0xDE20FA);
INSERT INTO @Reporting ([SourceHebrew]) VALUES (0xE820FA);
UPDATE @Reporting
SET [Destination] = [SourceSlovak]
WHERE [SourceSlovak] IS NOT NULL;
UPDATE @Reporting
SET [Destination] = [SourceHebrew]
WHERE [SourceHebrew] IS NOT NULL;
SELECT * FROM @Reporting;
UPDATE @Reporting
SET [DestinationS] = [Destination],
[DestinationH] = [Destination]
SELECT * FROM @Reporting;
Run Code Online (Sandbox Code Playgroud)
如您所见,所有这些字符都可以转换为VARCHAR
,只是不在同一VARCHAR
列中。
使用以下查询来确定源表的每一列的代码页:
ID SourceSlovak SourceHebrew Destination DestinationS DestinationH
1 ? ú NULL ? ú ? ú ? ?
2 NULL ? ? ? ? ? ? ? ?
Run Code Online (Sandbox Code Playgroud)
话虽如此....
你提到在 SQL Server 2008 R2 上,但是,你没有说是什么版本。如果您碰巧使用的是企业版,那么请忘记所有这些转换内容(因为您这样做可能只是为了节省空间),并启用数据压缩:
如果使用标准版(现在看来您是),那么还有另一种可能性:升级到 SQL Server 2016,因为 SP1 包括所有版本都可以使用数据压缩的能力(请记住,我确实说过“长期” )。
当然,既然刚刚澄清了数据只有一个来源,那么您就不用担心了,因为该来源不能包含任何仅 Unicode 的字符或其特定代码之外的字符页。在这种情况下,您唯一需要注意的是使用与源列相同的排序规则,或者至少使用相同的代码页。意思是,如果源列正在使用SQL_Latin1_General_CP1_CI_AS
,那么您可以Latin1_General_100_CI_AS
在目的地使用。
一旦您知道要使用的排序规则,您可以:
ALTER TABLE ... ALTER COLUMN ...
是VARCHAR
(一定要指定当前NULL
/NOT NULL
设置),这需要一点时间和大量事务日志空间,8700 万行,或
为每个列创建新的“ColumnName_tmp”列,然后通过UPDATE
执行TOP (1000) ... WHERE new_column IS NULL
. 一旦填充了所有行(并验证它们都正确复制了!您可能需要一个触发器来处理更新,如果有的话),在显式事务中,用于sp_rename
将“当前”列的列名交换为“ _Old”然后是新的“_tmp”列以简单地从名称中删除“_tmp”。然后调用sp_reconfigure
该表以使任何引用该表的缓存计划无效,如果有任何引用该表的视图,您将需要调用sp_refreshview
(或类似的方法)。一旦您验证了应用程序并且 ETL 可以正常使用它,您就可以删除这些列。
归档时间: |
|
查看次数: |
16563 次 |
最近记录: |