这
CONVERT(CHAR(32), HASHBYTES('SHA2_256', BusinessKey))
会引起问题。它采用 32 字节哈希并将其作为 32 个代码点存储在一些 varchar 排序规则中。许多代码点是非打印字符,因此检查值将一直是个问题。
而且我不会相信 SSIS 或任何其他外部程序来回甚至显示这些值。许多编程环境使用以空值结尾的字符串,其中字节 0x00 表示字符串的结尾。并且您的哈希将在大约 12% 的时间中具有 0x00 代码点。P=1-(255/256)^32
将哈希存储为 BINARY(32) 或 Base64 编码字符串,您的问题应该会消失。
这是从我正在处理的示例中提取的一个小实用程序,它可能对您有用。我在存储在具有唯一约束的 char(32) 中的 200k 行表上使用哈希对其进行了测试,并且效果很好。
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;
namespace Microsoft.Samples.SqlServerDataMover
{
public class Program
{
public static void Main(string[] args)
{
var srcConnectionString = args[0];
var destConnectionString = args[1];
using (var src = new SqlConnection(srcConnectionString))
using (var dest = new SqlConnection(destConnectionString))
{
src.Open();
dest.Open();
var cmdTables = src.CreateCommand();
cmdTables.CommandText = "select schema_name(schema_id) schema_name, name table_name from sys.tables where type_desc = 'USER_TABLE'";
var tables = new List<(string, string)>();
using (var rdr = cmdTables.ExecuteReader())
{
while (rdr.Read())
{
var schema = rdr.GetString(0);
var table = rdr.GetString(1);
tables.Add((schema, table));
}
}
foreach (var t in tables)
{
CopyTable(src, dest, t.Item2, t.Item1);
}
}
}
static string QuoteName(string identifier)
{
var sb = new StringBuilder(identifier.Length + 3, 1024);
sb.Append('[');
foreach (var c in identifier)
{
if (c == ']')
sb.Append(']');
sb.Append(c);
}
sb.Append(']');
return sb.ToString();
}
public static void CopyTable(SqlConnection src, SqlConnection dest, string tableName, string schemaName, int batchSize = 10000)
{
var opts = SqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.KeepNulls | SqlBulkCopyOptions.TableLock;
var sql = $"select * from {QuoteName(schemaName)}.{QuoteName(tableName)}";
var cmd = src.CreateCommand();
cmd.CommandTimeout = 0;
cmd.CommandText = sql;
using (var rdr = cmd.ExecuteReader())
using (var bc = new SqlBulkCopy(dest, opts, null))
{
var schemaDt = rdr.GetSchemaTable();
//schemaDt.WriteXml(Console.Out);
var schema = new Dictionary<string, DataRow>();
foreach (DataRow r in schemaDt.Rows)
{
schema.Add(r[0].ToString(), r);
}
bc.BatchSize = batchSize;
for (int i = 0; i < rdr.FieldCount; i++)
{
var cn = rdr.GetName(i);
bool isreadonly = schema[cn].Field<bool>("IsReadOnly");
if (!isreadonly)
{
bc.ColumnMappings.Add(i, cn);
}
}
bc.NotifyAfter = 10000;
bc.SqlRowsCopied += (s, a) =>
{
Console.WriteLine($"[{schemaName}].[{tableName}] {a.RowsCopied} Rows Copied.");
};
bc.DestinationTableName = $"[{schemaName}].[{tableName}]";
bc.WriteToServer(rdr);
Console.WriteLine($"[{schemaName}].[{tableName}] Complete. {1} Rows Copied.");
}
}
}
}
只需传入两个连接字符串即可运行它,首先是源,然后是目标。
例如
PS C:\temp\datamover> .\bin\debug\DataMover.exe "Server=xxxxxx.database.windows.net;database=adventureworks;User ID=xxxxxx;Password=xxxxxx" "Server=localhost;database=awcopy;integrated security=true"