在执行任何操作之前,请在对问题的评论中考虑@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;
返回(第二个结果集):
ID SourceSlovak SourceHebrew Destination DestinationS DestinationH
1 Ţ ú NULL Ţ ú Ţ ú ? ?
2 NULL ט ת ? ? ט ת ט ת
如您所见,所有这些字符都可以转换为VARCHAR
,只是不能在同一个字符中VARCHAR
列中。
使用以下查询来确定源表的每一列的代码页是什么:
SELECT OBJECT_NAME(sc.[object_id]) AS [TableName],
COLLATIONPROPERTY(sc.[collation_name], 'CodePage') AS [CodePage],
sc.*
FROM sys.columns sc
WHERE OBJECT_NAME(sc.[object_id]) = N'source_table_name';
话虽如此....
您提到在SQL Server 2008 R2上,但没有说什么版本。如果您碰巧使用的是Enterprise Edition,则请忽略所有这些转换内容(因为您可能只是为了节省空间而这样做),然后启用数据压缩:
Unicode压缩实现
如果使用标准版(现在看来您是),则还有另一种可能:升级到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
。填充所有行(并确认所有行均已正确复制后!您可能需要触发器来处理UPDATE,如果有的话),在显式事务中,用于sp_rename
将“当前”列的列名交换为“ _Old”,然后新建“ _tmp”列,以从名称中删除“ _tmp”。然后,调用sp_reconfigure
该表以使引用该表的所有缓存计划都无效,并且如果有任何引用该表的View,则需要调用sp_refreshview
(或类似方法)。验证应用程序并且ETL可以正常使用后,您可以删除列。
[G]
被ETL传送到[P]
。如果[G]
为varchar
,则ETL进程是数据进入的唯一方式[P]
,那么除非该进程添加了真正的Unicode字符,否则不应该有任何字符。如果其他进程在中添加或修改了数据[P]
,则您需要格外小心-仅仅因为当前所有数据都可以varchar
,并不意味着nvarchar
明天就不能添加数据。同样,有可能正在消耗[P]
需求nvarchar
数据中的任何数据。