我试图想出可以完成这项工作的查询,但我决定向你寻求帮助。
我有一个答案表和一个AnswerHistory表。在我的BLL中,我有两个单独的方法来更新Answer表。当用户更改Answer表的Title或Body列时,将使用第一个。无论是哪两个都更新,都会在更改发生之前创建一个新的AnswerHistory,其中包含Answer实体的所有字段。这意味着,如果我将答案实体的标题从“标题1”更改为“标题2”,我将有一个新的AnswerHistory实体,在标题列中具有“标题1”,以及此更改的日期发生。此外,另一个函数将IsAccepted列从0更改为1,反之亦然。当然,它还会创建一个AnswerHistory实体。
总结: - 函数1更改标题和正文,创建一个新的AnswerHistory实体(在发生更改之前,Answer实体的副本); - 功能2仅更改IsAccepted列。还会创建一个新的Answer实体(在更改发生之前,Answer实体的副本)。
我想执行这样一个查询,它将检索由于运行Function 1而创建的所有AnswerHistory实体。意思是,我想选择所有在一些用户更改了Title或Body之后出现的AnswerHistories原来的答案实体。我不想检索用户更改Answer的IsAccepted属性时创建的那些。
如果我在答案表中有以下数据,
Id QeustionId Title Body IsAccepted CreationUserId CreationDate
1 1 Title1_3 Body1_3 1 2 2014-07-22
2 2 Title2_1 Body2_1 1 2 2014-07-22
3 3 Title3_2 Body3_2 0 2 2014-07-22
这些实体在AnswerHistory表中
Id AnswerId Title Body IsAccepted ModificationDate
13 1 Title1_1 Body1_1 0 2014-08-06
14 1 Title1_1 Body1_1 1 2014-09-06
15 1 Title1_2 Body1_2 1 2014-10-06
16 2 Title2_1 Body2_1 0 2014-08-06
17 3 Title3_1 Body3_1 0 2014-08-06
我的查询应该返回:
Id AnswerId Title Body IsAccepted ModificationDate
14 1 Title1_1 Body1_1 1 2014-09-06
15 1 Title1_2 Body1_2 1 2014-10-06
17 3 Title3_1 Body3_1 0 2014-08-06
这是可能的,如果是的话 - 然后怎么样?
顺便说一句,我不想改变我的架构并创建不同的历史表来处理不同的场景 - 用户更改问题的标题或正文时的场景对于IsAccepted的更改。
修改 我正在使用Microsoft SQL Server 2012。
答案 0 :(得分:0)
为什么不在创建答案历史记录表时将其直接放入答案历史记录表中?将数据拆分为两个表会使查询更难编写。
让我们从CTE开始获取真实历史(包括当前值)。然后添加一个方法来进行比较。我将假设SQL Server 2012+,因为lead()
函数最有用。
with changes as (
select Id, AnswerId, Title, Body, IsAccepted, ModificationDate
from AnswerHistory
union all
select NULL as Id, AnswerId, Title, Body, IsAccepted, ModificationDate
from Answer
),
changeslead as (
select c.*,
lead(title) over (partition by AnswerId order by ModificationDate) as next_title,
lead(body) over (partition by AnswerId order by ModificationDate) as next_body
from changes c
)
select id, AnswerId, Title, Body, IsAccepted, ModificationDate
from changeslead
where title <> next_title or body <> next_body;
这假定title
和body
不是NULL
,这与问题的表达方式一致。
答案 1 :(得分:0)
首先我创建一些测试表
使用tempdb - 创建测试表 创建表答案 (ID int PRIMARY KEY,QuestionId int not null,Title varchar(25)not null,Body varchar(25)not null,IsAccepted bit not null,CreationUserId int not null,CreationDate date not null)
CREATE TABLE AnswerHistory
(Id int not null PRIMARY KEY, AnswerId int not null, Title varchar(25) not null, Body varchar(25) not null, IsAccepted bit not null, ModificationDate date not null)
CREATE INDEX IX_AnswerHistory_AnswerID ON AnswerHistory(AnswerID)
go
--Populate Test Tables
INSERT Answer(Id, QuestionId, Title, Body, IsAccepted, CreationUserId, CreationDate) VALUES (1, 1, 'Title1_3', 'Body1_3', 1, 2, '2014-07-22')
INSERT Answer(Id, QuestionId, Title, Body, IsAccepted, CreationUserId, CreationDate) VALUES (2, 2, 'Title2_1', 'Body2_1', 1, 2, '2014-07-22')
INSERT Answer(Id, QuestionId, Title, Body, IsAccepted, CreationUserId, CreationDate) VALUES (3, 3, 'Title3_2', 'Body3_2', 0, 2, '2014-07-22')
INSERT INTO AnswerHistory(Id, AnswerId, Title, Body, IsAccepted, ModificationDate) VALUES(13, 1, 'Title1_1', 'Body1_1', 0, '2014-08-06')
INSERT INTO AnswerHistory(Id, AnswerId, Title, Body, IsAccepted, ModificationDate) VALUES(14, 1, 'Title1_1', 'Body1_1', 1, '2014-09-06')
INSERT INTO AnswerHistory(Id, AnswerId, Title, Body, IsAccepted, ModificationDate) VALUES(15, 1, 'Title1_2', 'Body1_2', 1, '2014-10-06')
INSERT INTO AnswerHistory(Id, AnswerId, Title, Body, IsAccepted, ModificationDate) VALUES(16, 2, 'Title2_1', 'Body2_1', 0, '2014-08-06')
INSERT INTO AnswerHistory(Id, AnswerId, Title, Body, IsAccepted, ModificationDate) VALUES(17, 3, 'Title3_1', 'Body3_1', 0, '2014-08-06')
一旦我创建了测试表,我就使用子查询将当前的Answers和Answer History表组合成一组使用UNION ALL子句的记录。修改日期设置为9月31日9999,因此它始终位于最后一个历史记录项目之后。
SELECT Id, AnswerId, Title, Body, IsAccepted, ModificationDate FROM AnswerHistory
UNION ALL
SELECT NULL as Id, ID AS AnswerId, Title, Body, IsAccepted, CONVERT(date,'9999-12-31')as ModificationDate FROM Answer
然后我们使用SQL Server中的LEAD函数来获取每一行的下一个Title和Body。
SELECT
u.Id,
u.AnswerId,
u.Title,
u.Body,
u.IsAccepted,
u.ModificationDate,
Lead(Title) OVER (PARTITION BY u.AnswerId ORDER BY u.ModificationDate) as nextTitle,
Lead(Body) OVER (PARTITION BY u.AnswerId ORDER BY u.ModificationDate) as nextBody
FROM
(
SELECT Id, AnswerId, Title, Body, IsAccepted, ModificationDate FROM AnswerHistory
UNION ALL
SELECT NULL as Id, ID AS AnswerId, Title, Body, IsAccepted, CONVERT(date,'9999-12-31')as ModificationDate FROM Answer
)u --Union of historic and current queries
这给了我们
Id AnswerId Title Body IsAccepted ModificationDate nextTitle nextBody
----------- ----------- --------------- --------------- ---------- ---------------- --------------- ---------------
13 1 Title1_1 Body1_1 0 2014-08-06 Title1_1 Body1_1
14 1 Title1_1 Body1_1 1 2014-09-06 Title1_2 Body1_2
15 1 Title1_2 Body1_2 1 2014-10-06 Title1_3 Body1_3
NULL 1 Title1_3 Body1_3 1 9999-12-31 NULL NULL
16 2 Title2_1 Body2_1 0 2014-08-06 Title2_1 Body2_1
NULL 2 Title2_1 Body2_1 1 9999-12-31 NULL NULL
17 3 Title3_1 Body3_1 0 2014-08-06 Title3_2 Body3_2
NULL 3 Title3_2 Body3_2 0 9999-12-31 NULL NULL
(8 row(s) affected)
LEAD()不能在WHERE子句中使用,因此必须将其包装在另一个子查询中以包含结果,从而允许我们应用过滤器。
SELECT
w.Id ,w.AnswerId ,w.Title,w.Body ,w.IsAccepted ,w.ModificationDate
FROM
(SELECT
u.Id,
u.AnswerId,
u.Title,
u.Body,
u.IsAccepted,
u.ModificationDate,
Lead(Title) OVER (PARTITION BY u.AnswerId ORDER BY u.AnswerId,u.ModificationDate) as nextTitle,
Lead(Body) OVER (PARTITION BY u.AnswerId ORDER BY u.ModificationDate) as nextBody
FROM
(
SELECT Id, AnswerId, Title, Body, IsAccepted, ModificationDate FROM AnswerHistory
UNION ALL
SELECT NULL as Id, ID AS AnswerId, Title, Body, IsAccepted, CONVERT(date,'9999-12-31')as ModificationDate FROM Answer
)u --Union of historic and current queries
)w --Subquery with windowed Functions
WHERE w.Body <> w.nextBody OR w.Title <> w.nextTitle
有关LEAD的更多信息,请查看Microsoft http://msdn.microsoft.com/en-us/library/hh213125(v=sql.110).aspx
如果您使用的是早期版本的SQL Server,您将无法使用此功能,实施起来会更加尴尬