需要帮助编写SQL以基于复杂规则检索列表吗
似乎这个标题以前已经用过很多次了,不幸的是,我不知道如何描述我的问题。所以,首先,如果你有一个更好的标题,将有助于未来的搜索,火了 无论如何,我的问题是尝试编写SQL以根据应用于以下架构的一组规则返回resultset:需要帮助编写SQL以基于复杂规则检索列表吗,sql,join,Sql,Join,似乎这个标题以前已经用过很多次了,不幸的是,我不知道如何描述我的问题。所以,首先,如果你有一个更好的标题,将有助于未来的搜索,火了 无论如何,我的问题是尝试编写SQL以根据应用于以下架构的一组规则返回resultset: TABLE: Tests COLUMNS: ID (PK), Name TABLE: TestVersions COLUMNS: TestID (PK), Version (PK), IsActive TABLE: TestSessions COLUMNS: Tes
TABLE: Tests
COLUMNS: ID (PK), Name
TABLE: TestVersions
COLUMNS: TestID (PK), Version (PK), IsActive
TABLE: TestSessions
COLUMNS: TestID (PK), TestVersion (PK), UserID (PK), Iteration (PK), Completed, CompletionDate
[Tests].[ID]=[TestVersions].[TestID]上的[Tests]和[TestVersions]之间存在关系。[TestSessions].[TestID]=[TestVersions].[TestID]和[TestSessions].[TestVersion]=[TestVersions].[TestVersion].[TestVersions]上的[TestSessions]和[TestVersions]之间也存在关系
结果集应根据以下规则返回[Tests].[ID]、[Tests].[Name]和[TestVersions].[Version]:
在TestSessions中有关联记录且Complete为false的任何测试。
TestSessions中没有关联记录但IsActive的任何测试的最大版本为true。
这是最复杂的一个。如果一个测试在TestSessions中有关联的记录,其中“所有已完成”为true,并且在TestVersions中至少有一条关联的记录为IsActive true,那么我需要验证最近的CompletionDate是否至少在30天前,如果是,则从TestVersions返回最高版本。
希望这是有意义的。这里有一个快速脚本,可以创建临时表,用数据填充它们,并向您展示如何解决您遇到的问题。让我先说一下,这种东西有时最好留给业务逻辑层。另外,我不确定你的描述是否准确,因为似乎有一些不一致之处。话虽如此,我希望你能把解决问题的方法去掉,把它分解成几个部分,然后解决它。不要担心预先优化,只需编写干净的逻辑代码。优化器将告诉您是否犯了任何错误。这假定SQL Server 2008:
declare @Tests table
(
ID int,
Name varchar(100)
)
declare @TestVersions table
(
TestID int,
Version int,
IsActive bit
)
declare @TestSessions table
(
TestID int,
TestVersion int,
UserID varchar(100),
Iteration int,
Completed bit,
CompletionDate date
)
insert into @Tests
select 1, 'one' union all
select 2, 'two' union all
select 3, 'three' union all
select 4, 'four' union all
select 5, 'five'
insert into @TestVersions
select 1, 1, 0 union all
select 2, 1, 1 union all
select 3, 1, 0 union all
select 4, 1, 0 union all
select 5, 1, 1 union all
select 1, 2, 0 union all
select 2, 2, 0 union all
select 3, 2, 1 union all
select 4, 2, 0 union all
select 1, 3, 1 union all
select 2, 3, 1
insert into @TestSessions
select 1, 1, 'a', 1, 1, GETDATE()-101 union all
select 2, 1, 'b', 1, 1, GETDATE()-11 union all
select 3, 1, 'c', 1, 0, null union all
select 4, 1, 'd', 1, 1, GETDATE()-103 union all
select 5, 1, 'e', 1, 1, GETDATE()-101 union all
select 1, 2, 'f', 1, 1, GETDATE()-15 union all
select 2, 2, 'g', 1, 0, null union all
select 3, 2, 'h', 1, 1, GETDATE()-17 union all
select 4, 2, 'i', 1, 0, null union all
select 1, 3, 'j', 2, 1, GETDATE()-109 union all
select 2, 3, 'k', 2, 1, GETDATE()-101 union all
select 2, 1, 'l', 3, 1, GETDATE()-120 union all
select 3, 1, 'm', 1, 1, GETDATE()-11 union all
select 4, 1, 'n', 1, 1, GETDATE()-140 union all
select 5, 1, 'a', 1, 0, null union all
select 1, 2, 'b', 1, 1, GETDATE()-160 union all
select 2, 2, 'c', 2, 0, null union all
select 3, 2, 'd', 1, 1, GETDATE()-17 union all
select 4, 2, 'e', 1, 0, null union all
select 1, 3, 'f', 2, 1, GETDATE()-4 union all
select 2, 3, 'g', 3, 1, GETDATE()-101
select Id
,Name
,Version
from @Tests t
inner join
@TestVersions v on t.ID = v.TestID
-- any test with a session with completed = false
where exists
(select *
from @TestSessions s1
where s1.Completed = 0
and s1.TestID = t.ID
and s1.TestVersion = v.Version
)
or (
-- any max version of a test which has no sessions
not exists
(select *
from @TestSessions s1
where s1.TestID = t.ID
and s1.TestVersion = v.Version
)
and v.Version =
(select MAX(Version)
from @TestVersions v2
where v2.TestID = t.ID
)
)
or (
-- no uncompleted sessions
not exists
(select *
from @TestSessions s1
where s1.Completed = 0
and s1.TestID = t.ID
and s1.TestVersion = v.Version
)
-- we're already inner joining to versions, so we just need to check IsActive
and IsActive = 1
-- the most recent completion date is at least 30 days ago (not quite 30 days ago but i'm lazy)
and GETDATE() - 30 <=
(select MAX(CompletionDate)
from @TestSessions s2
where s2.TestID = t.ID
and s2.TestVersion = v.Version
)
-- not sure what you mean by "return", but I'm sure you can figure out how to project what you need
)
以下是我根据米利米特的建议得出的结论:
SELECT Tests.ID, Tests.Name, TestVersions.Version
FROM Tests INNER JOIN
TestVersions ON Tests.ID = TestVersions.TestID
WHERE (
EXISTS (SELECT *
FROM TestSessions
WHERE Complete = 0
AND TestID = Test.ID
AND TestVersion = TestVersions.Version)
)
OR
(
NOT EXISTS (SELECT *
FROM TestSessions
WHERE TestID = Tests.ID
AND TestVersion = TestVersions.Version)
AND (TestVersions.Version = (SELECT MAX(Version)
FROM TestVersions
WHERE TestID = Tests.ID))
AND TestVersions.IsActive = 1
AND TestVersions.StartDate <= GetDate()
AND (TestVersions.EndDate IS NULL OR TestVersions.EndDate >= GetDate())
)
OR
(
EXISTS (SELECT *
FROM TestSessions
WHERE Complete = 1
AND TestID = Tests.ID
AND TestVersion = TestVersions.Version)
AND (TestVersions.Version = (SELECT MAX(Version)
FROM TestVersions
WHERE TestID = Tests.ID))
AND (TestVersions.IsActive = 1)
AND (TestVersions.StartDate <= GetDate())
AND (TestVersions.EndDate IS NULL OR TestVersions.EndDate >= GetDate())
AND (GetDate() >= (SELECT DATEADD(day, 30, MAX(TestSessions.CompletionDate))
FROM TestSessions
WHERE TestSessions.TestID = Tests.ID
AND TestSessions.TestVersion = TestVersions.Version))
您觉得什么是“不一致”?至于return,我的意思是我需要该记录的Version列中的最高值。最糟糕的是,我可以创建一个新版本的测试,而以前版本的TestSession仍然处于打开状态。如果Complete为false,则会创建一个新版本的测试。当TestSession完成时,如果是真的,我需要返回新的更高版本,而不是与最近的TestSession关联的版本。我还认为第三个子句“no uncompleted sessions”中的子查询可能有缺陷。如果TestSessions中不存在任何记录,则Completed=0的Not exists也将导致出现true条件。我认为最好用Completed=1检查“exists”,以测试以前完成的会话。是的,我听到了,但注意到我写的内容与您最初要求的内容是如何匹配的。这就是我所说的矛盾。就像你现在说的有道理,但是你最初的指导有点混乱。我并不是要100%满足您的要求,只是想让您了解如何在查询中分解逻辑。希望有帮助。