如何最好地保持父子关系中的状态

时间:2012-07-18 19:14:50

标签: sql tsql sql-server-2008-r2 relational-database

我无法弄清楚如何用这个问题的标题来说清楚,所以这是我最好的解释:

我的客户有一系列彼此处于父子关系的表。例如:

CREATE TABLE dbo.First_Level (
    id INT IDENTITY(1, 1) NOT NULL,
    name VARCHAR(20) NOT NULL,
    active BIT NOT NULL DEFAULT(1),
    CONSTRAINT PK_First_Level PRIMARY KEY CLUSTERED (id)
)

CREATE TABLE dbo.Second_Level (
    id INT IDENTITY(1, 1) NOT NULL,
    first_level_id INT NOT NULL,
    name VARCHAR(20) NOT NULL,
    active BIT NOT NULL DEFAULT(1),
    CONSTRAINT PK_Second_Level PRIMARY KEY CLUSTERED (id),
    CONSTRAINT FK_SecondLevel_FirstLevel FOREIGN KEY (first_level_id) REFERENCES dbo.First_Level (id)
)


CREATE TABLE dbo.Third_Level (
    id INT IDENTITY(1, 1) NOT NULL,
    second_level_id INT NOT NULL,
    name VARCHAR(20) NOT NULL,
    active BIT NOT NULL DEFAULT(1),
    CONSTRAINT PK_Third_Level PRIMARY KEY CLUSTERED (id),
    CONSTRAINT FK_ThirdLevel_SecondLevel FOREIGN KEY (second_level_id) REFERENCES dbo.Second_Level (id)
)

棘手的部分是,如果父项被标记为不再处于活动状态(active = 0),则其下的任何子项也应被视为不再处于活动状态。但是,即使父级仍然有效,也可以将 标记为不再有效。

我正在寻找有关如何最好地执行这些业务规则的建议。我可以覆盖视图,然后查看所有父级以确定任何给定行的最终活动标志,但我的客户端已经有很多代码围绕这些表编写。

我可以通过触发器强制执行规则,但我希望有一种更优雅的方式来做到这一点。

谢谢!

3 个答案:

答案 0 :(得分:1)

我个人不喜欢将业务规则放在数据库中,但是开发对此有所分歧。无论如何,这是一个解决方案,它从逻辑层面应用您描述的规则,即不更改基础表。这允许您限制更新并确保所有表中每行的活动/非活动状态与其他表的耦合不太紧密。

SELECT
    FL.id AS FirstLevelId
    ,FL.active AS FirstLevelActive
    ,SL.id AS SecondLevelId
    -- If First Level is Active, ignore it by passing it as NULL to coalesce, and take the value of Second_Level.active
    -- If First Level is not active, take its value as the "active" value for this level. 
    ,COALESCE(NULLIF(FL.active, 1), SL.active) AS SecondLevelActive
    ,TL.id AS ThirdLevelId
    -- Same as above, but with one more level involved
    ,COALESCE(NULLIF(FL.active, 1), NULLIF(SL.active, 1), TL.active) AS ThirdLevelActive
FROM
    First_Level FL
    LEFT JOIN
    Second_Level SL ON
        (SL.first_level_id = FL.id)
    LEFT JOIN
    Third_Level TL ON
        (TL.second_level_id = SL.id)

<强>优点
  - 通过应用业务规则检索活动状态,但保留级别数据   - 无需更新,因此无需跟踪级联更新,也无需使用触发器   - 通过将此查询放在View中,应用程序将始终“即时”获得正确的状态

<强>缺点
  - 由于工作台结构,解决方案很僵硬   - 添加新律师需要相应地更改查询

致谢
感谢马桶故事4的作曲家Ghidorah,感谢鼓舞人心的音乐。

答案 1 :(得分:1)

当前有效的顶级条目集是顶级条目的整个设置顶级的RESTRICT视图:

创建视图FIRST_LEVEL_ACTIVE AS SELECT * FROM FIRST_LEVEL WHERE ACTIVE ...;

当前活动的第二级条目集是满足条目本身仍处于活动状态的条目,并且它们的父级出现在刚刚定义的视图中:

创建视图SECOND_LEVEL_ACTIVE AS(SELECT * FROM SECOND_LEVEL WHERE ACTIVE ...)NATURAL JOIN FIRST_LEVEL_ACTIVE; / *或其他任何与NATURAL JOIN * /

达到相同效果的操作

当前活动的第三级条目集是满足条目本身仍处于活动状态且其父级出现在SECOND_LEVEL_ACTIVE

中的条目。

等。等

为了处理这些视图的UPDATE,SQL标准具有INSTEAD OF触发器。

答案 2 :(得分:0)

您可以通过让更新语句在更新活动状态时更新子项来强制执行业务规则:

-- Delete first level
DECLARE @FirstLevelIdToDelete INT
SET @FirstLevelIdToDelete = x

UPDATE  Third_Level
SET     Third_level.active = 0
FROM    Third_Level tl
JOIN    Second_Level sl ON tl.second_level_id = sl.id
WHERE   sl.first_level_id = @FirstLevelIdToDelete

UPDATE  Second_Level
SET     active = 0
WHERE   Second_Level.first_level_id = @FirstLevelIdToDelete

UPDATE  First_Level
SET     active = 0
WHERE   id = @FirstLevelIdToDelete

-- Delete second level
DECLARE @SecondLevelIdToDelete INT
SET @SecondLevelIdToDelete = x

UPDATE  Third_Level
SET     active = 0
WHERE   Third_Level.second_level_id = @SecondLevelIdToDelete

UPDATE  Second_Level
SET     active = 0
WHERE   id = @SecondLevelIdToDelete

-- Delete third level
DECLARE @ThirdLevelIdToDelete INT
SET @ThirdLevelIdToDelete = x

UPDATE  Third_Level
SET     active = 0
WHERE   id = @ThirdLevelIdToDelete