代码之家  ›  专栏  ›  技术社区  ›  OscarRyz

从数据库列创建唯一的主键(哈希)

  •  2
  • OscarRyz  · 技术社区  · 16 年前

    我有一张没有主键的桌子。

    我将在一个新表中插入一些记录来分析它们,并考虑使用所有可用列的值创建一个新的主键。

    如果这是一种像Java那样的编程语言,我会:

     int hash = column1 * 31 + column2 * 31 + column3*31 
    

    或者类似的事情。但这是SQL。

    如何从可用列的值创建主键?简单地将所有列标记为pk是行不通的,因为我需要做的是将它们与其他db表中的数据进行比较。

    我的桌子有三个号码和一个日期。

    编辑 我的问题是

    我想需要更多的背景知识。很抱歉之前没有提供。

    我有一个数据库(dm),每天都从另一个数据库(原始源)更新。它有过去两年的记录。

    上个月(七月)更新过程被打破,一个月没有数据被更新到DM中。

    我在oracle xe中手动创建了一个具有相同结构的表,并将原始源中的记录复制到db(myxe)中,我只复制了7月份的记录,以便在月末创建所需的报告。

    最后,在8月8日,更新过程得到了修复,等待通过这个自动过程迁移的记录被复制到数据库中(从originalsource复制到dm)。

    一旦数据被复制(到dm中),此过程将从原始源清除数据。

    一切看起来都很好,但我们刚刚意识到有一些记录丢失了(大约是7月份的25%)。

    所以,我想做的是使用备份(myxe)并将丢失的所有记录插入数据库(dm)。

    这里的问题是:

    • 他们没有一个定义明确的pk。
    • 它们在不同的数据库中。

    所以我想,如果我能从两个表中创建一个唯一的pk,给出相同的数字,我就能分辨出哪个丢失了,然后插入它们。

    编辑2

    因此,我在当地环境中做了以下工作:

    select a.* from the_table@PRODUCTION a , the_table b where
    a.idle = b.idle and 
    a.activity = b.activity and 
    a.finishdate = b.finishdate
    

    返回两个数据库中存在的所有行。联合?)我有2000张唱片。

    我接下来要做的是从目标数据库中删除它们,然后将它们从我的数据库中全部插入到目标表中

    我希望我不会陷入最糟糕的境地-s:-s

    4 回复  |  直到 16 年前
        1
  •  3
  •   Adamski    16 年前

    通过组合3个数字和日期来创建散列值的危险在于它可能不唯一,因此不能安全地用作主键。

    相反,我建议对主键使用自动递增的i d。

        2
  •  3
  •   Quassnoi    16 年前

    只需创建代理项密钥:

    ALTER TABLE mytable ADD pk_col INT
    
    UPDATE  mytable
    SET     pk_col = rownum
    
    ALTER TABLE mytable MODIFY pk_col INT NOT NULL
    
    ALTER TABLE mytable ADD CONSTRAINT pk_mytable_pk_col PRIMARY KEY (pk_col)
    

    或者:

    ALTER TABLE mytable ADD pk_col RAW(16)
    
    UPDATE  mytable
    SET     pk_col = SYS_GUID()
    
    ALTER TABLE mytable MODIFY pk_col RAW(16) NOT NULL
    
    ALTER TABLE mytable ADD CONSTRAINT pk_mytable_pk_col PRIMARY KEY (pk_col)
    

    后者使用 GUID 在数据库中是唯一的,但占用更多空间,生成速度慢得多 INSERT 会很慢)

    更新:

    如果您需要创建相同的 PRIMARY KEY 在两张桌子上 完全相同的 数据,请使用:

    MERGE
    INTO    mytable v
    USING   (
            SELECT  rowid AS rid, rownum AS rn
            FROM    mytable
            ORDER BY
                    co1l, col2, col3
            )
    ON      (v.rowid = rid)
    WHEN MATCHED THEN
    UPDATE
    SET     pk_col = rn
    

    请注意,表最多只能有一行是相同的(也就是说,表中有相同数量的行和相同的数据)。

    Update 2 :

    对于你的问题,你不需要 PK 完全。

    如果您只想选择 dm ,使用此选项(打开 糖尿病 侧)

    SELECT  *
    FROM    mytable@myxe
    MINUS
    SELECT  *
    FROM    mytable
    

    这将返回中存在的所有记录 mytable@myxe 但不在 mytable@dm

    注意,如果有的话,它会缩小所有的副本。

        3
  •  1
  •   Cynthia    16 年前

    假设您已经确保了唯一性……您可以在SQL中做几乎相同的事情。唯一的问题是将日期转换为数值,以便可以对其进行哈希。

    Select Table2.SomeFields 
        FROM Table1 LEFT OUTER JOIN Table2 ON
            (Table1.col1 * 31) + (Table1.col2 * 31) + (Table1.col3 * 31) + 
                ((DatePart(year,Table1.date) + DatePart(month,Table1.date) + DatePart(day,Table1.date) )* 31) = Table2.hashedPk
    

    上面的查询适用于sql server,而oracle的唯一区别在于如何处理日期转换。此外,在sql server中还有其他转换日期的函数,因此这绝不是唯一的解决方案。

    而且,您可以将它与Quassnoi的SET语句组合起来填充新字段。只需对值使用连接条件逻辑的左侧。

        4
  •  1
  •   Philip Kelley    16 年前

    如果使用旧表中的值加载新表,然后需要加入两个表,则只能“适当”地执行此操作,如果您可以唯一标识原始表中的每一行。Quassnoi的解决方案将允许您这样做,如果您可以首先通过添加一个新列来更改旧表。

    如果无法更改原始表,则基于旧表的列生成某种形式的哈希代码将起作用——但是,同样,仅当哈希代码唯一标识每一行时。(甲骨文有校验和函数,对吧?如果是,请使用它们。)

    如果无法保证散列代码的唯一性,则可能需要使用由多个列组成的主键来确保唯一性(例如,自然键)。如果没有自然键,我听说oracle为每一行数据提供了rownum,你能用它吗?