数据库设计 - 记录链式条目

时间:2011-09-02 03:09:49

标签: sql-server database oracle database-design relational-database

有3例

1) Basic
Sender ---> Receiver

2) Parallel
Sender ----> Receiver1
       ----> ReceiverN

3) Chained
Sender ----> Primary Receiver  -----> Secondary Receiver1
                               -----> Secondary ReceiverN

对于1)Basic和2)Parallels,你可能会像这样设计你的表

Account
-Id (PK)
-UserId (FK)
-Name
-Description
-etc

Entry
-Id (PK)
-SenderAccountId (FK)
-ReceiverAccountId (FK)

现在您如何设计数据库以记录“链式”条目?

2 个答案:

答案 0 :(得分:1)

您可以在帐户(Id-PK,UserId-FK,名称,描述,...)和条目(Id-PK)表之间添加多对多关系: EntryAccount(EntryId& AccountId-PK,EntryAccountType) 其中EntryAccountType字段可以具有以下值之一{S = Sender,R = Receiver,P = Primary receiver,N = secoNdary receiver}。

EntryAccount表的INSERT语句将为:

--Basic
INSERT EntryAccount (EntryId,AccountId,EntryAccountType)
VALUES (...,...,'S')
INSERT EntryAccount (EntryId,AccountId,EntryAccountType)
VALUES (...,...,'R')

--Parallel
INSERT EntryAccount (EntryId,AccountId,EntryAccountType)
VALUES (...,...,'S')
INSERT EntryAccount (EntryId,AccountId,EntryAccountType)
VALUES (...,...,'R') 
INSERT EntryAccount (EntryId,AccountId,EntryAccountType)
VALUES (...,...,'R')
INSERT EntryAccount (EntryId,AccountId,EntryAccountType)
VALUES (...,...,'R')

--Chained
INSERT EntryAccount (EntryId,AccountId,EntryAccountType)
VALUES (...,...,'S')
INSERT EntryAccount (EntryId,AccountId,EntryAccountType)
VALUES (...,...,'P')
INSERT EntryAccount (EntryId,AccountId,EntryAccountType)
VALUES (...,...,'N') 
INSERT EntryAccount (EntryId,AccountId,EntryAccountType)
VALUES (...,...,'N')
INSERT EntryAccount (EntryId,AccountId,EntryAccountType)
VALUES (...,...,'N')

然后,要强制执行某些业务规则(一个发送方-S,一个主接收方-P和许多[二级]接收方-R / N),您可以在EntryAccount表上创建唯一的过滤索引(SQL Server 2008):IUF_EntryAccount_EntryId_EntryAccountType (键> EntryId& EntryAccountType,过滤器> EntryAccountType IN('S','P'))。此外,此索引适用于查询优化。 但是,这个索引还不够,因为你可以拥有像这样的“不一致”的Entry业务对象:

Entry(1001)
EntryAccoount(1001,...,'S') without EntryAccoount(1001,...,'R') 
or
EntryAccoount(1001,...,'R') without EntryAccoount(1001,...,'S')
, etc.

要解决此问题,您需要在EntryAccount表上执行INSERT,UPDATE,DELETE后触发:

    CREATE TRIGGER ...
    AFTER INSERT, UPDATE, DELETE
    ...
    DECLARE @Results TABLE
    (
    EntryId INT PRIMARY KEY
    ,SendersCount INT NOT NULL DEFAULT O
    ,ReceiversCount INT NOT NULL DEFAULT O
    ,PrimaryReceiversCount INT NOT NULL DEFAULT O
    ,SecondaryReceiversCount INT NOT NULL DEFAULT O
    );
    INSERT @Results(EntryId)
    SELECT EntryId
    FROM inserted
    UNION --no duplicates
    SELECT EntryId
    FROM deleted;

    --Count senders
    UPDATE @Results
    SET SendersCount = q.Num
    FROM @Results r
    JOIN
    (
    SELECT ea.EntryId, COUNT(*) Num
    FROM EntryAccount ea
    JOIN @Results i ON ea.EntryId = i.EntryId
    WHERE ea.EntryAccountType = 'S'
    GROUP BY ea.EntryId
    ) q ON r.EntryId = q.EntryId;

    -Count [standard-R] receivers
    UPDATE @Results
    SET ReceiversCount = q.Num
    FROM @Results r
    JOIN
    (
    SELECT ea.EntryId, COUNT(*) Num
    FROM EntryAccount ea
    JOIN @Results i ON ea.EntryId = i.EntryId
    WHERE ea.EntryAccountType = 'R'
    GROUP BY ea.EntryId
    ) q ON r.EntryId = q.EntryId;

    --Count primary-P receivers
    UPDATE @Results
    SET PrimaryReceiversCount = q.Num
    FROM @Results r
    JOIN
    (
    SELECT ea.EntryId, COUNT(*) Num
    FROM EntryAccount ea
    JOIN @Results i ON ea.EntryId = i.EntryId
    WHERE ea.EntryAccountType = 'P'
    GROUP BY ea.EntryId
    ) q ON r.EntryId = q.EntryId;


    --Count secondary-N receivers
    UPDATE @Results
    SET SecondaryReceiversCount = q.Num
    FROM @Results r
    JOIN
    (
    SELECT ea.EntryId, COUNT(*) Num
    FROM EntryAccount ea
    JOIN @Results i ON ea.EntryId = i.EntryId
    WHERE ea.EntryAccountType = 'N'
    GROUP BY ea.EntryId
    ) q ON r.EntryId = q.EntryId;

    --Final validation
    IF EXISTS
    (
    SELECT *
    FROM @Results r
    WHERE NOT(r.SendersCount=1 AND r.ReceiversCount>=1 AND r.PrimaryReceiver=0 AND r.SecondaryReceiversCount=0 
    OR r.SenderCount=1 AND r.ReceiversCount=0 AND r.PrimaryReceiver=1 AND r.SecondaryReceiversCount >=1
    OR r.SenderCount=0 AND r.ReceiversCount=0 AND r.PrimaryReceiver=0 AND r.SecondaryReceiversCount=0
)
    )
    ROLLBACK;

如果您没有SQL Server 2008(R1 / R2),则无法创建过滤索引,但只能依赖于触发器。

PS:我还没有测试过这个解决方案。

答案 1 :(得分:0)

将一个parent_record_id添加到Entry表中。对于链中的第一步,它将为null。对于第二个,它将包含原始记录的ID。第三步,它将包含相关第二步的ID,依此类推。

我在评论中看到你说它最多可以有两个链接深度,所以你可以用另一种方法提出一种不同的解决方法,但这种方式是通用的,适用于很多场景,如果您的要求变为3级或更高级别的链接,则继续工作。

然后,您可以查找CONNECT BY SQL语法,以对此类数据结构执行有效的层次结构查询。