2011-04-10 126 views
1

看起來這個標題已被多次使用過,不幸的是我不知道如何描述我的問題。所以,首先,如果你有一個更好的標題,這將有助於未來的搜索者的建議,開火!需要幫助編寫SQL來根據複雜的規則檢索列表

無論如何,我的問題是試圖寫SQL返回基於一組適用於以下模式規則一個結果:

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 

有[測試]和[TestVersions]上的關係[Tests]。[ID] = [TestVersions]。[TestID]。 [TestSessions]上[TestSessions]和[TestVersions]之間也有關係[TestID] = [TestVersions]。[TestID]和[T​​estSessions]。[TestVersion] = [TestVersions]。[Version]

The 。結果集應該返回[測試] [ID],[測試] [名]和[TestVersions] [版本]基於以下規則:。

  1. 已在TestSessions相關的記錄,其中完成所有測試是錯誤的。
     
  2. 任何TestSessions中沒有關聯記錄但IsActive的最大版本爲true。
     
  3. 這是一個複雜的問題。如果測試在TestSession中有關聯的記錄(全部完成)(Complete爲true),並且在TestVersions中至少有一個關聯的記錄並且IsActive爲true,那麼我需要驗證最近的CompletionDate是否至少在30天前,如果是,從TestVersions返回最高版本。
     

希望這是有道理的。

回答

0

下面是一個快速腳本,用於創建臨時表格,填充數據並向您展示如何處理您遇到的問題。讓我先說,這種東西有時最好留給業務邏輯層。另外,我不確定您的描述是否準確,因爲似乎存在一些不一致之處。據說,我希望你能拿走如何解決你的問題,把它分解成部分並解決它。不要擔心事先優化,只需編寫乾淨的邏輯代碼即可。優化器會告訴你,如果你犯了什麼錯誤。這是假設的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 
     ) 
+0

你覺得什麼是 '不一致'? – SonOfPirate 2011-04-10 13:09:03

+0

至於「返回」,我的意思是我需要該記錄的版本列中的最高值。在哪裏變得麻煩的是,我可以創建一個新版本的Test,同時前一個版本的TestSession仍然處於打開狀態(Complete爲false)。當TestSession完成時(Complete爲true),我需要返回新的(更高的)版本,而不是與最後一個(最近的)TestSession關聯的版本。 – SonOfPirate 2011-04-10 13:11:25

+0

我也認爲第三個子句中的子查詢('no uncompleted sessions')可能有缺陷。如果TestSessions中不存在記錄,則不存在與Completed = 0也會導致真實條件。我在考慮用Completed = 1檢查'exists'來測試以前完成的會話會更好。 – SonOfPirate 2011-04-10 13:41:37

0

這裏是我結束了(基於Milimetric的建議):

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))