跨两列的 MySQL 唯一约束

The*_*tor 5 mysql database-design constraint

我有一个定义关系的表

Src smallint(5) unsigned NOT NULL,
Dst smallint(5) unsigned NOT NULL,
other fields
Run Code Online (Sandbox Code Playgroud)

我需要添加一个约束,说明其中一列中是否存在给定值。
1) 不能在同一列中重复。
2) 也不能在其他列中重复。

这是无效的

src    dst
1      354
666    1
Run Code Online (Sandbox Code Playgroud)

由于值 1 出现在第一行中,因此它不能出现在第二行中。

如何定义这种类型的约束?

我正在对应用程序杠杆进行轻量级检查。但我希望数据库确保它。

更新:目前我有 7 种不同类型的关系,每种关系类型一张表。

更新 2:原来这只是一张包含所有关系的表,现在我要爆炸了

# variante
Create TABLE `productsRelationships3` (
  `relSrc` smallint(5) unsigned NOT NULL,
  `relDst` smallint(5) unsigned NOT NULL,
  PRIMARY KEY `src-dst-3` (relSrc, relDst),
  UNIQUE  `src-3` (relSrc)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

# this is the import
INSERT INTO productsRelationships3 SELECT relSrc, relDst FROM  productsRelationships WHERE relType=3;
DELETE FROM productsRelationships WHERE relType=3;

#this is the retrieval. The dummy rows are there because I do a UNION
#SELECT relSrc, relDst, 3 as relType, relTypeDesc, 0 as fracQty, 28281 as source FROM productsRelationships3 LEFT JOIN productsRelationshipsDesc on 3=relTypeID WHERE relDst=28281 OR relSrc=28281;



# fraccion
#relType is from the old 1-table schema. It's going to be deleted
Create TABLE `productsRelationships6` (
  `relSrc` smallint(5) unsigned NOT NULL,
  `relType` tinyint(2) unsigned NOT NULL DEFAULT 6,
  `fracQty` int(2) unsigned NOT NULL,
  `relDst` smallint(5) unsigned NOT NULL,
  PRIMARY KEY `src-dst-6` (relSrc, relDst),
  UNIQUE  `src-6` (relSrc),
  UNIQUE  `dst-6` (relDst),
  CONSTRAINT `fk_type_desc_6` FOREIGN KEY (`relType`) REFERENCES `productsrelationshipsdesc` (`relTypeID`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

#import
INSERT INTO productsRelationships6 SELECT relSrc, relType, fracQty, relDst FROM  productsRelationships WHERE relType=6;
Run Code Online (Sandbox Code Playgroud)

其他表与 productsRelationships3 基本相同

A-K*_*A-K 5

我稍微修改了ypercube 的解决方案,以便源和目标都不为空,正如原始设计所保证的那样。我的CHECK约束被注释掉了,因为它们显然在 MySQL 中不起作用。我将它们保留为注释,因为它们记录了我的意图,并且它们将适用于其他 RDBMS。

CREATE TABLE PointType
( PointTypeID tinyint unsigned NOT NULL,
  TypeDescription CHAR(20) NOT NULL,
  PRIMARY KEY (PointTypeID),
  UNIQUE (TypeDescription)
) ;

INSERT INTO PointType 
  (PointTypeID, TypeDescription)
VALUES
  (1, 'Source'), 
  (2, 'Destination') ;

CREATE TABLE PointUsageQuota
( RouteID int unsigned NOT NULL,
  PointTypeID tinyint unsigned NOT NULL,
  PointID smallint(5) unsigned NOT NULL,
  PRIMARY KEY (PointID),     -- and this is what all the fuss is about
  UNIQUE (RouteID , PointID, PointTypeID), -- target for the foreign keys
  FOREIGN KEY (PointTypeID)
    REFERENCES PointType (PointTypeID)
) ;

CREATE TABLE Route
( RouteID int unsigned NOT NULL,
  SourcePointID smallint(5) unsigned NOT NULL,
  SourceTypeID tinyint unsigned NOT NULL, 
      -- CHECK(SourceTypeID = 1),
  FOREIGN KEY (RouteID , SourcePointID, SourceTypeID) 
    REFERENCES PointUsageQuota (RouteID , PointID, PointTypeID) ,
  DestinationPointID smallint(5) unsigned NOT NULL,
  DestinationTypeID tinyint unsigned NOT NULL, 
      -- CHECK(DestinationTypeID = 2),
  FOREIGN KEY (RouteID , DestinationPointID, DestinationTypeID) 
    REFERENCES PointUsageQuota (RouteID , PointID, PointTypeID) ,
  --  other fields
  PRIMARY KEY (RouteID)
) ;
Run Code Online (Sandbox Code Playgroud)

测试:

insert into PointUsageQuota
values(1,1,666),(1,2,354);

INSERT INTO Route VALUES (1, 666, 1, 354, 2);
-- this fails:
INSERT INTO Route VALUES (2, 666, 1, 354, 2);
-- this fails too:
INSERT INTO Route VALUES (2, 354, 1, 666, 2);
Run Code Online (Sandbox Code Playgroud)


ype*_*eᵀᴹ 4

我认为当前的设计不可能有这种限制。如果您可以更改它并假设表格现在是:

CREATE TABLE Route
( RouteID int unsigned NOT NULL,
  Src smallint(5) unsigned NOT NULL,
  Dst smallint(5) unsigned NOT NULL,
  -- other fields
  PRIMARY KEY (RouteID)
) ;
Run Code Online (Sandbox Code Playgroud)

您可以拆分为 2 个表,并将两列移动到新表中,将它们合并为一列 ( SrcDst)。一个只有 2 行的小参考表 ( ) 将有助于强制执行只有 a和 a 的PointType要求:SrcDst

CREATE TABLE Route
( RouteID int unsigned NOT NULL,
  --- other fields
  PRIMARY KEY (RouteID)
) ;

CREATE TABLE PointType
( PointTypeID tinyint unsigned NOT NULL,
  TypeDescription CHAR(20) NOT NULL,
  PRIMARY KEY (PointTypeID),
  UNIQUE (TypeDescription)
) ;

INSERT INTO PointType 
  (PointTypeID, TypeDescription)
VALUES
  (1, 'Source'), 
  (2, 'Destination') ;

CREATE TABLE RoutePoint
( RouteID int unsigned NOT NULL,
  PointTypeID tinyint unsigned NOT NULL,
  SrcDst smallint(5) unsigned NOT NULL,
  PRIMARY KEY (RouteID, PointTypeID),
  UNIQUE (SrcDst),                       -- and this is what all the fuss is about
  FOREIGN KEY (RouteID)
    REFERENCES Route (RouteID)
      ON DELETE CASCADE ON UPDATE CASCADE,
  FOREIGN KEY (PointTypeID)
    REFERENCES PointType (PointTypeID)
) ;
Run Code Online (Sandbox Code Playgroud)

这意味着旧Route表中的任何行现在将成为新表中的 1 行Route和表中的 2 行RoutePoint

这意味着现在你不能简单地INSERT进入Route表中。您必须使用一个事务来确保如果在Route表中插入一行,则也会在RoutePoint表中插入 2 行。Route否则,您可能会有没有Src或 的行Dst

必须对两个表的UPDATEDELETE语句进行类似的更改,因此不会意外更改或从RoutePoint表中删除行,例如留下Route没有相关SrcDst数据的行。