0

我还有一个与帖子相关的问题: 数据库中的复合主键

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

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

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

谢谢你的帮助。

4

4 回答 4

3

如何确保 User_ID 和 Admin_ID(两个 FK)之间的组合在插入时只能存在一次(唯一)?

创建一个复合键。

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

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

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

即使你是唯一一个访问数据库的人,你也需要一个索引来有效地执行 SELECT。那么为什么不让 DBMS 使用这个索引作为键呢?

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

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

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


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

2虽然它不一定是主要的。

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

于 2012-09-13T18:33:59.813 回答
1

I would go for a composite key

If I really need an auto-increment primary key, then I'll create a unique index on the two foreign key columns

Side note: The advantage of using a composite key is that when using an ORM tool like Entity Framework, it automatically recognizes it as a many to many relationship, and abstracting away the additional intersection table into just a relationship.

于 2012-09-13T13:17:43.133 回答
1

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

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

(真正的国际象棋游戏需要额外的约束(例如:每种颜色不超过一个国王......),(但部分)业务规则(如有效移动......)将由“应用程序逻辑”无论如何)

-- 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';
        ;
于 2012-09-14T00:28:54.467 回答
0

带着身份证去。我非常同意另一页上的答案。对于快速而肮脏的应用程序,复合键很好。但是,我通常将自动递增的 id 放入我创建的新表中,除非它们是用于报告的静态表。

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

  1. 实施约束以使这两个字段永远不会重复。
  2. 在两列上创建唯一索引。
  3. 在表上实施触发器以检查重复项。
  4. 通过检查数据有效性的存储过程进行插入。

我的偏好是(4),结合(1)或(2)。我发现通过存储过程控制插入给了我很大的灵活性,特别是当我想记录或调试问题时。也就是说,我通常不使用大容量事务系统,在这些系统中,减少开销至关重要。

在另一个答案中遗漏了自动递增的 id 有一个优势。以下查询:

select *
from t
order by 1 desc

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

于 2012-09-13T14:59:16.013 回答