首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >跨两列的MySQL唯一约束

跨两列的MySQL唯一约束
EN

Database Administration用户
提问于 2012-11-27 06:52:13
回答 3查看 10.7K关注 0票数 6

我有一个定义关系的表格

代码语言:javascript
复制
Src smallint(5) unsigned NOT NULL,
Dst smallint(5) unsigned NOT NULL,
other fields

然后,我需要添加一个约束,表示给定值是否存在于其中一列中。

1)不能在同一栏中复制。

2)也不能在另一栏中复制。

这是无效的

代码语言:javascript
复制
src    dst
1      354
666    1

因为值1存在于第一行中,所以它不能出现在第二行中。

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

我在应用杠杆上做一个轻量级的检查。但我希望数据库能确保。

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

更新2:最初,这只是一个包含所有关系的表,现在我正在开发它

代码语言:javascript
复制
# 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;

其他表基本上与productsRelationships3相同

EN

回答 3

Database Administration用户

发布于 2012-11-27 21:04:16

我对Y立方体溶液做了一点修改,使源和目的地都不是空的,这是原始设计所保证的。我的CHECK约束被注释掉了,因为它们显然不能在MySQL中工作。我保留了它们作为注释,因为它们记录了我的意图,并且它们将在其他RDBMS上工作。

代码语言:javascript
复制
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)
) ;

测试:

代码语言:javascript
复制
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);
票数 5
EN

Database Administration用户

发布于 2012-11-27 07:26:04

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

代码语言:javascript
复制
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)
) ;

您可以拆分成两个表,并将这两个列移动到新表中,并将它们组合成一个列(SrcDst)。一个只有2行的小型引用表(PointType)将有助于强制执行这样的要求,即您有一个Src和一个Dst,而且没有更多:

代码语言:javascript
复制
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)
) ;

这意味着旧Route表上的任何行现在都将是新Route中的1行和RoutePoint表中的2行。

这意味着现在不能简单地将INSERT放到Route表中。您必须使用一个事务,以确保如果在Route表中插入一行,也会在RoutePoint表中插入2行。否则,您可能在Route中有没有SrcDst的行。

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

票数 4
EN

Database Administration用户

发布于 2012-11-27 18:03:45

这可以通过触发器来实现。

参见此SQLFiddle用于工作模型。将第二个INSERT更改为一个重复的值,您将看到插入失败并有错误消息。使用致命信号从“插入前”触发器中跳转可以防止插入的实际发生。

代码语言:javascript
复制
CREATE TRIGGER Route_bi BEFORE INSERT ON Route FOR EACH ROW
  BEGIN
      DECLARE err_msg VARCHAR(128) DEFAULT NULL; 

      IF EXISTS(SELECT x.Dst FROM Route x WHERE x.Dst = NEW.Src) THEN
          SET err_msg = CONCAT_WS('','cannot insert Src value ',NEW.Src,'; already exists as a Dst');
          SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = err_msg;
      END IF;

      IF EXISTS(SELECT x.Src FROM Route x WHERE x.Src = NEW.Dst) THEN
          SET err_msg = CONCAT_WS('','cannot insert Dst value ',NEW.Dst,'; already exists as a Src');
          SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = err_msg;
      END IF;
  END;

Src和Dst列都需要一个唯一的键,这将防止同一列中的重复值。

您还必须在更新触发器之前处理类似的条件,以避免以后将值更改为重复值。

票数 1
EN
页面原文内容由Database Administration提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://dba.stackexchange.com/questions/29336

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档