1

我不知道如何用这个问题的标题来表达清楚,所以这是我最好的解释:

我的客户有一系列相互之间存在父子关系的表。例如:

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)
)

棘手的部分是,如果父母被标记为不再活动(活动 = 0),那么它下面的任何孩子也应该被视为不再活动。但是,即使父母仍然处于活动状态,孩子也可以被标记为不再活动。

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

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

谢谢!

4

3 回答 3

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)

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

缺点
- 由于表格结构,解决方案很僵化
- 添加新律师需要相应地更改查询

致谢
感谢《厕所物语 4》的作曲家 Ghidorah 提供的鼓舞人心的音乐。

于 2012-07-18T22:01:55.643 回答
1

当前活动的顶级条目集是对整个集的顶级顶级条目的 RESTRICT 视图:

CREATE VIEW FIRST_LEVEL_ACTIVE AS SELECT * FROM FIRST_LEVEL WHERE ACTIVE ... ;

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

CREATE VIEW SECOND_LEVEL_ACTIVE AS (SELECT * FROM SECOND_LEVEL WHERE ACTIVE ...) NATURAL JOIN FIRST_LEVEL_ACTIVE; /* 或其他任何与 NATURAL JOIN 达到相同效果的操作 */

当前处于活动状态的三级条目集合是那些满足条目本身仍然处于活动状态且其父项出现在 SECOND_LEVEL_ACTIVE 中的条件的条目

等等等等

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

于 2012-07-19T10:18:59.583 回答
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
于 2012-07-18T19:34:36.773 回答