SQL - 两个地方的数据

时间:2012-12-09 22:34:34

标签: sql

请看下面的DDL:

CREATE TABLE Sport (ID int, Description varchar(50),primary key(ID))

CREATE TABLE Audit (AditID int, PersonID int, SportID int, AuditDate datetime,primary key(id))

CREATE Table Person (ID int not null, Name varchar(50), FavouriteSportID int, PRIMARY KEY (id),
FOREIGN KEY (FavouriteSportID) REFERENCES Sport(Id))

INSERT INTO Sport VALUES (1,'Football')
INSERT INTO Sport VALUES (2,'Basketball')
INSERT INTO Sport VALUES (3,'Squash')

INSERT INTO Person VALUES (1,'Ian',1)
INSERT INTO Audit VALUES (1,1,1,'2012-01-01')
INSERT INTO Audit VALUES (2,1,1,'2012-02-01')
INSERT INTO Audit VALUES (3,1,'2012-03-01')

审核表显示过去人们最喜爱的运动。

我意识到这涉及将最喜欢的运动存储在两个地方,即Person.FavouriteSportID和最近的审核记录,即审核记录ID 3也表明Ian目前最喜欢的运动是Squash(因为它有最多最近的审计记录)。

我想知道这个简单的要求是否有更好的设计。

2 个答案:

答案 0 :(得分:1)

没关系。事实上这很好。

或者,你可以使用AuditId代替SportId in Person,作为对当前SportId的最新审计行的引用,但这使得Audit成为当前活动的一部分表示。我更喜欢你的设计,因为当前的表示仅限于较少的表,审计表是一个支腿。自动化也更容易 - Person上的简单after update, delete触发器将可靠地维护审计表,而不需要任何人的进一步努力。

修改

如果您不需要在审计表中记录当前关联,则不要,并且您没有存储两次相同的信息。如上所述,after update, delete trigger会做得很好

-- syntax assuming SQL Server 2008+, adapt as appropriate
create trigger tr_Person_Audit on Person 
after update, delete 
as begin
  insert Audit (PersonId, SportId, ExpirationDate)
  select PersonId, SportId, getdate() -- as expiration date
  from (
    select PersonId, SportId from deleted except 
    select PersonId, SportId from inserted -- control for updates where nothing changed
    ) this
end

如果你需要在审计表中记录当前的关联,它不是那么干净,但是Audit仍然是一个支腿,你仍然可以使用{{自动化它1}}触发器:

after insert, update

答案 1 :(得分:0)

您也可以删除列Person.FavouriteSportID并在需要时查询最近的审核。保持数据一致更容易。

SELECT TOP 1 SportID
FROM Audit
WHERE PersonID = @1
ORDER BY AuditDate DESC

或者

SELECT
    Person.*,
    (SELECT TOP 1 Audit.SportID
     FROM Audit
     WHERE Audit.PersonID = Person.PersonID
     ORDER BY Audit.AuditDate DESC) AS FavouriteSportID
FROM
    Person