Consider the following (hypothetical) schema. Would you add a surrogate key to the "chessboard" table ? The values for {xxx,yyy,pc} are constrained, either by "restricted" domains, or by the PK+FK constraints.
In which cases would adding a surrogate key (for {xxx,yyy} help?
(additional constraints (such as: no more than one King per Color ...) would be necessary for a real chess game, (but part of) the business rules (such as valid moves ...) would be handled by the "application logic" anyway)
-- 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';
;