【问题标题】:SQL Server - Compare large number of MD5-Hashed ValuesSQL Server - 比较大量 MD5 哈希值
【发布时间】:2018-09-18 05:20:21
【问题描述】:

假设我在 SQL Server 中有一个存储客户电子邮件的表(几百万条记录) - 为简单起见,如下所示:

CREATE TABLE [Emails]
(
    [Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
    [email] [nvarchar](1000) NOT NULL
)

我有客户向我发送列表,每百万条记录长,他们的客户的电子邮件,但所有 MD5-Hash 加密,所以列表看起来如下:

0x3B46E0E53842A74172BA678974E93BBB
0xACAC5843E184C85AA6FF641AAB0AA644
0xD3C7BA16E02BE75142761894E8E4A125
...

我必须想出一个快速的方法来查看他们列表中的哪些电子邮件存在于我的表格中。

根据我在网上/这里看到的一些答案,我想出了以下逻辑来做到这一点:

  1. 我创建了Emails 表的索引视图,其中MD5-Hash 列作为索引:

    CREATE VIEW dbo.vw_Emails
    WITH SCHEMABINDING
    AS
        SELECT 
            Id
             , email
             , CONVERT(VARBINARY(16), HASHBYTES('MD5', LOWER(email))) AS MD5
        FROM 
            dbo.Emails
    GO
    
    CREATE UNIQUE CLUSTERED INDEX Idx_vw_Emails ON vw_Emails (MD5)
    GO
    
  2. 我创建了一个存储过程,它将BulkImport 给定的列表,将其转换为临时表,将其加入我的视图并返回任何匹配的行,如下所示:

    CREATE PROCEDURE Import_ReturnMatches
    (
        @PathToCSVFile VARCHAR(8000)
    )
    
    AS
    
    DECLARE @fieldsep CHAR(1) = ',';
    DECLARE @recordsep CHAR(1) = CHAR(10);
    
    DECLARE @Emails TABLE 
    (
        MD5 VARCHAR(MAX) NOT NULL
    );
    
    DECLARE @sql VARCHAR(8000) = 
        'CREATE TABLE #tmp 
        (
              MD5 varchar(max) NOT NULL
        );
    
        BULK INSERT #tmp
        FROM ''' + @PathToCSVFile + '''
        WITH (FIRSTROW = 1, FIELDTERMINATOR = ''' + @fieldsep + ''', ROWTERMINATOR = ''' + @recordsep + ''');
    
        SELECT *
        FROM #tmp';
    
    INSERT INTO @Emails
    EXEC (@sql);
    
    SELECT 
        r.*
    FROM 
        @Emails l
        JOIN vw_Email_Dim r 
        ON l.MD5 = r.MD5
    

如您所见,我将导入的列类型设置为VARCHAR(MAX),但这只是因为没有其他方法真正起作用......这就是我卡住的地方。即使我在文件中放置了应该匹配的记录,它似乎总是返回一个空集。

我的问题是:

  1. 我做错了什么/我该如何解决这个问题?
  2. 我是否为我的存储/索引/导入使用了正确的数据类型?
  3. 这只是一个整体的坏主意吗?有没有更好的方法来完成我想做的事情?

【问题讨论】:

  • 如果可能的话,我会在您的表中添加另一列来存储您电子邮件的 MD5 哈希值。它肯定会使加入更快。电子邮件中字母的大小写也可能引起悲伤。
  • 谢谢,@thomas,我很可能最终会这样做,但这仍然是对我公司的考验,所以在我证明这可行之前,他们不会让我更改生产表。不过,这确实是有道理的。
  • 比较 binary 和 varchar 不会做你想要的。
  • @MartinSmith,我看到了这一点,这就是我的问题的症结所在 - 我怎样才能做到这一点?
  • 您需要使用带有样式参数的CONVERT 将包含十六进制字符串的varchar 转换为varbinary,反之亦然

标签: sql-server join hash filter md5


【解决方案1】:

检查这个答案。您需要将 varchar 与 varchar 进行比较-我认为不是 varbinary。

Generate MD5 hash string with T-SQL

【讨论】:

  • 我为我的加入尝试了这个:@Emails l JOIN vw_Emails r ON l.MD5 = CONVERT(VARCHAR(MAX), r.MD5, 2),但仍然没有运气......我做错了什么吗?
  • 你能改变视图以转换为 varchar(32) 吗?
  • 您需要向我们展示一些失败的示例数据,并且您希望它成功(双方)。 IE。提供一个 MVCE
  • @john 您需要样式参数 1 来保留 0x
  • 我刚试过这个 - 它工作了 - 在存储列问题和我在ROWTERMINATOR 的疯狂愚蠢之间 - 我需要做到\n
【解决方案2】:

你的问题很可能是这个值:

LOWER(email)

如果您不确定使用什么大小写或编码(Windows 1252、UTF8、UTF16、UTF16LE?)从源电子邮件中生成 MD5 哈希,那么您正在考虑需要测试所有组合匹配的哈希值。考虑我们将LOWER 更改为UPPER 的位置 - 生成完全不同的 MD5 哈希值:

您将需要控制如何在源中生成 MD5 哈希,或者将元数据(另一个字段)添加到导入以描述输入的大小写和编码方式。

【讨论】:

  • 我也是这么认为的,但是即使我将视图生成的哈希直接放入文件中,我也会返回空记录集...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多