自动递增与复合键

时间:2012-09-13 13:00:22

标签: database database-design foreign-keys primary-key composite-primary-key

我还有一个与帖子相关的问题: Composite primary keys in databases

请查看那边的帖子(否则我只需重复一遍)。

现在我的问题是: 如果我将ID自动增量作为主键(因为我接受并且允许我再次通过此键引用当前表),我如何确保User_ID和Admin_ID之间的组合(两者都是FK的)插入时只能存在一次(唯一)? 这是一种多对多的关系 它可以在前端的编程中完成(通过选择检查现有记录),但是我的感觉告诉我这不是最好的方式,我想知道我是否可以直接将限制放在后端。

我会在逻辑上将FK添加到主键,但后来我又回到了复合键,这是我通常建议的,而不是使用。
这样做的正确方法是什么?

感谢您的帮助。

4 个答案:

答案 0 :(得分:3)

  

我怎样才能确保User_ID和Admin_ID(两个FK)之间的组合只能在插入时存在一次(是唯一的)?

创建复合键。

  

可以在前端编程中完成(通过选择检查现有记录)

除非您是访问该表的唯一客户,否则它不可能。 1

在真实的并发环境中,您永远无法知道另一个事务是否插入了相同的值(正如您尝试插入的那样)之后之前您的INSERT

即使您是唯一访问数据库的人,也需要一个有效执行SELECT的索引。那么为什么不让DBMS将这个索引用于密钥呢?

  

我会在逻辑上将FK添加到主键,但后来我又回到了复合键,这是我通常建议不要使用的。

错误的建议。如果列或列的组合必须是唯一的,则来创建密钥。 2 您不能跳过创建强制数据正确性的密钥,因为你有另一个(代理)钥匙。

代理人一般不能替换自然键,只能添加。 3 所以问题就变成了:代理人的额外开销值得吗? Sometimes它是,有时它不是,但这里没有严格的规则。


1 或者愿意锁定整个表格,破坏流程中的可扩展性。

2 虽然它不一定需要主要

3 他们通常“替换”的是自然键作为主键的角色,但自然键仍然作为备用键继续存在。

答案 1 :(得分:1)

我会选择一个复合键

如果我真的需要一个自动增量主键,那么我将在两个外键列上创建一个唯一索引

附注:使用复合键的优点是,当使用像Entity Framework这样的ORM工具时,它会自动将其识别为多对多关系,并将附加交集表抽象为关系。

答案 2 :(得分:1)

考虑以下(假设的)架构。你会在“棋盘”表中添加一个代理键吗? {xxx,yyy,pc}的值受“受限”域或PK + FK约束的约束。

在哪些情况下会添加代理键(对于{xxx,yyy}帮助?

(额外的约束(例如:每个颜色不超过一个King ......)对于真正的国际象棋游戏是必要的,(但部分)业务规则(例如有效的移动......)将被处理无论如何,通过“应用逻辑”)

-- this is Postgres-specific:
-- create a schema to play in
DROP SCHEMA chess CASCADE ;
CREATE SCHEMA chess ;
SET search_path='chess' ;

        -- Domain with only values A-H to three allowed.
CREATE DOMAIN chess_column
        AS CHAR(1) NOT NULL
        check (value >= 'A' AND value <= 'H')
        ;
        -- Domain with only values 1-8 allowed.
CREATE DOMAIN chess_row
        AS INTEGER NOT NULL
        check (value >= 1 AND value <= 8)
        ;
        -- Table with only valid pieces
CREATE TABLE chess_piece
        ( id INTEGER NOT NULL PRIMARY KEY
        , pname varchar
        ) ;
INSERT INTO chess_piece(id,pname) VALUES
 ( -6, 'Black King' ) , ( -5, 'Black Queen' ) , ( -4, 'Black Rook' )
, ( -3, 'Black Bishop' ) , ( -2, 'Black Knight' ) , ( -1, 'Black Pawn' )
, ( 6, 'White King' ) , ( 5, 'White Queen' ) , ( 4, 'White Rook' )
, ( 3, 'White Bishop' ) , ( 2, 'White Knight' ) , ( 1, 'White Pawn' )
        ;

CREATE TABLE chessboard
        ( xxx chess_column
        , yyy chess_row
        , pc INTEGER NOT NULL REFERENCES chess_piece(id)
        , PRIMARY KEY (xxx,yyy)
        );
        -- Too lazy to enter the entire board
        -- ; only put a White Pawn at E2
INSERT INTO chessboard(xxx,yyy,pc)
SELECT 'E', 2, p.id
FROM chess_piece p
WHERE p.pname = 'White Pawn';
        ;
        -- Shift the pawn
UPDATE chessboard b
SET yyy = 4
FROM chess_piece p
WHERE b.pc = p.id
AND p.pname = 'White Pawn';
AND b.xxx = 'E' AND b.yyy = 2
        ;
        -- Try to put a piece outside the board
\echo Try put a piece outside the board
INSERT INTO chessboard(xxx,yyy,pc)
SELECT 'I', 2, p.id
FROM chess_piece p
WHERE p.pname = 'Black Pawn';
        ;
        -- add a non-existing piece
\echo add a non-existing piece
INSERT INTO chessboard(xxx,yyy,pc)
VALUES( 'H', 1, 42)
        ;
        -- Position is already occupied
\echo Position is already occupied
INSERT INTO chessboard(xxx,yyy,pc)
SELECT 'E', 4, p.id
FROM chess_piece p
WHERE p.pname = 'Black Pawn';
        ;

答案 3 :(得分:0)

使用ID。我非常同意另一页的答案。对于快速和脏的应用程序,复合键很好。但是,我通常会将自动递增的id放入我创建的新表中,除非它们是用于报告的静态表。

对于您的具体问题,至少有四个答案我可以想到:

  1. 实施约束,以便永远不会复制这两个字段。
  2. 在两列上创建唯一索引。
  3. 在表格上实施触发器以检查重复项。
  4. 通过检查数据有效性的存储过程进行插入。
  5. 我的偏好是(4),与(1)或(2)组合。我发现通过存储过程控制插入为我提供了很大的灵活性,特别是当我想记录或调试问题时。也就是说,我通常不会使用大容量事务系统,因为减少开销是最重要的。

    另一个答案错过了自动递增ID的一个优点。以下查询:

    select *
    from t
    order by 1 desc
    

    返回最近添加的记录,假设id是第一列(就像在我的所有表中一样)。只是能够看到最近插入的记录就足以让我使用id。