基于父表中的参数的唯一约束检查

时间:2009-12-15 06:57:59

标签: hibernate database-design jpa

表用户有(userId, scoreType, ...)

@Table(name = "User", uniqueConstraints =
   @UniqueConstraint(columnNames = userId))

- scoreType可以是积分或百分比

UserScore (id, userId, points, percentage)

我想提供基于user.scoreType存储点数或百分比的灵活性。因此,如果用户的scoreType被标记为点,我们可以假设UserScore表只填充了点,反之亦然。

一个。我假设由于上述要求,我将无法在UserScore.points或UserScore.percentage上添加nullable = false检查。

湾我应该如何定义@UniqueConstraint表的UserScore检查。应该是

@Table(name = "UserScore", uniqueConstraints = { 
   @UniqueConstraint(columnNames = userId, points), 
   @UniqueConstraint(columnNames = userId, percentage))

对此问题上的任何其他观点表示赞赏

5 个答案:

答案 0 :(得分:1)

  

一个。我假设由于上述要求,我将无法在UserScore.points或UserScore.percentage上添加nullable = false检查。

正确

  

湾我应该如何定义UserScore表的@UniqueConstraint检查。应该是@Table(name =“UserScore”,uniqueConstraints = {@UniqueConstraint(columnNames = userId,points),@ UniqueConstraint(columnNames = userId,percentage))

两者都不适用于您所谈论的业务规则。 @UniqueConstraint(columnNames = userId, points)只允许用户ID和点的唯一组合; @UniqueConstraint(columnNames = userId, percentage)只允许用户ID和百分比的唯一组合。

答案 1 :(得分:0)

您可以对点和百分比使用相同的列,例如score int。然后用“假设小数点”存储百分比,比如25.7%score = 257。有了这个,就不需要约束了。

你也可以将scoreType移到UserScore表中,以便为每个用户提供混合评分 - 毕竟,它确实描述了得分。

CREATE TABLE UserScore (
     id int
    ,[userID] int
    ,scoreType varchar(3)
    ,score int
    )


查询表(SQL服务器)时:

SELECT
   id
  ,[userId]
  ,CASE scoreType WHEN 'pts' THEN score ELSE 0 END AS points
  ,CASE scoreType WHEN 'pts' THEN 0.0 ELSE (cast(score AS decimal(4,1)) /10.0) END AS percentage
FROM UserScore

答案 2 :(得分:0)

我从您的描述中读到的是,用户在userScore中只能有一个条目,并且该条目只能包含点数或百分比,但不能同时包含两者。如果是这样,用户和userScore表应该只是一个表,其中包含userId,points,percentage列。 scoreType已经隐式,可以省略。然后使用检查约束来确保只填充一个点和百分比。所以像

CREATE TABLE User (
    userId int PRIMARY KEY,
    points int,
    percentage numeric,
    CHECK (points IS NULL OR percentage IS NULL),
    CHECK (points IS NOT NULL OR percentage IS NOT NULL)  -- if desired
);

如果您真的需要scoreType列,请创建一个视图:

CREATE VIEW UserView AS
    SELECT userId,
           CASE WHEN points IS NOT NULL THEN 'points'
                ELSE 'percentage' END AS scoreType
    ...
    FROM User;

答案 3 :(得分:0)

此处有两种情况,基于您能够/允许在现有架构中引入多少更改。

如果您可以更改架构,这里最简单的解决方案是只有一个字段并使用用户记录中的scoreType字段来确定如何解释数据。如果您的百分比总是小数点后两位,那么您可以像这样创建表格:

CREATE TABLE userScore (
    id INT,
    userID INT,
    scoreValue INT
);

并像这样查询:

SELECT id,
       CASE WHEN scoreType = 'points' THEN scoreValue ELSE NULL END as points,
       CASE WHEN scoreType = 'percentage' THEN scoreValue / 100.0 ELSE NULL END as percentage
WHERE userID = ?

现在不需要任何类型的约束。

如果用户的scoreType字段可以更改且使用的字段(点数或百分比)将取决于存储分数时scoreType的值 ,则必须将scoreType添加到我的userScore版本中为了保持用每条记录解释数据的规则,因为你不能依赖父记录的一致性(但它确实不需要连接)。


如果您无法更改架构,则可以使用ON INSERT / UPDATE触发器来获取scoreType字段以验证结果记录在正确的字段中是否只有值。在PostgreSQL(我最熟悉的DB)中,触发器函数的语法就是这样的(为清楚起见,v_ *字段是变量/参数):

-- for trigger ON INSERT/UPDATE to userScore

SELECT INTO v_scoreType scoreType FROM user WHERE userID = v_userID;
IF   (v_scoreType = 'points' AND NEW.points IS NOT NULL AND NEW.percentage IS NULL)
  OR (v_scoreType = 'percentage' AND NEW.points IS NULL AND NEW.percentage IS NOT NULL) THEN
    RETURN NEW; -- this record is OK
ELSE
    RAISE EXCEPTION 'User Score type is %, tried to set points = % and percentage = %', v_scoreType, NEW.points, NEW.percentage;
END IF;

希望其中一个想法有所帮助!

答案 4 :(得分:0)

当分数类型为分数时,也许您可​​以尝试将百分比默认为某个荒谬值 当scoreType为百分比时,对点进行相同的操作。 场景1:用户= Bob,ScoreType = Points

       User= Bob, Points=2.5,Percentage -100000 (Default value for percentage) 

然后您可以定义复合主键

@Table(name =“UserScore”,uniqueConstraints = {    @UniqueConstraint(columnNames = userId,points,percentage)}