需要帮助编写SQL以基于复杂规则检索列表吗

需要帮助编写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

似乎这个标题以前已经用过很多次了,不幸的是,我不知道如何描述我的问题。所以,首先,如果你有一个更好的标题,将有助于未来的搜索,火了

无论如何,我的问题是尝试编写SQL以根据应用于以下架构的一组规则返回resultset:

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%满足您的要求,只是想让您了解如何在查询中分解逻辑。希望有帮助。