避免子查询根据基本记录的日期从同一个表中选择记录

时间:2017-10-24 17:18:47

标签: sql sql-server tsql

我在 SQL Server 2012 中有一个如下所示的StudentScores表。评分系统使用特殊规则加权。对于学生的每个MATHS结果,结果集中将有一行。根据是否有可用的分数"在MATHS结果日期的两个月内,该行可能有也可能没有科学和文学专栏的分数。和#34;在MATHS结果日期的一个月内,LITERATURE"。

注意:这是我为简化实际业务领域问题而创建的方案。

我使用子查询创建了以下查询。有没有办法在没有子查询的情况下更有效地重写它?

DECLARE @StudentScores TABLE (StudentMarkID INT IDENTITY(1,1) NOT NULL, StudentID INT, SubjectCode VARCHAR(10), ResultDate DATETIME, Score DECIMAL(5,2))
INSERT INTO @StudentScores (StudentID,SubjectCode,ResultDate,Score)
SELECT 1, 'MATHS','2016-01-10',35
UNION ALL
SELECT 1, 'LITERATURE','2016-01-10',62
UNION ALL
SELECT 1, 'SCIENCE','2016-01-30',65
UNION ALL
SELECT 1, 'SCIENCE','2016-02-02',61
UNION ALL
SELECT 1, 'LITERATURE','2016-02-03',60
UNION ALL
SELECT 1, 'MATHS','2016-03-25',55
UNION ALL
SELECT 2, 'LITERATURE','2016-01-10',12
UNION ALL
SELECT 2, 'SCIENCE','2016-01-30',14
UNION ALL
SELECT 2, 'SCIENCE','2016-02-14',12
UNION ALL
SELECT 2, 'LITERATURE','2016-02-14',15
UNION ALL
SELECT 2, 'MATHS','2016-03-25',18

QUERY

SELECT SS.StudentID, Score AS MathsScore, 
ResultDate AS MathsResultDate,
    (SELECT TOP 1 Score 
            FROM @StudentScores S2 
            WHERE S2.StudentID = SS.StudentID 
            AND S2.SubjectCode = 'SCIENCE'
            AND S2.ResultDate >= DATEADD(MONTH,-2,SS.ResultDate)
            ORDER BY s2.ResultDate DESC
    ) AS ScienceScore,
    (SELECT TOP 1 ResultDate 
            FROM @StudentScores S2 
            WHERE S2.StudentID = SS.StudentID 
            AND S2.SubjectCode = 'SCIENCE'
            AND S2.ResultDate >= DATEADD(MONTH,-2,SS.ResultDate)
            ORDER BY s2.ResultDate DESC
    ) AS ScienceResultDate,
    (SELECT TOP 1 Score 
            FROM @StudentScores S2 
            WHERE S2.StudentID = SS.StudentID 
            AND S2.SubjectCode = 'LITERATURE'
            AND S2.ResultDate >= DATEADD(MONTH,-1,SS.ResultDate)
            ORDER BY s2.ResultDate DESC
    ) AS LiteratureScore,
    (SELECT TOP 1 ResultDate 
            FROM @StudentScores S2 
            WHERE S2.StudentID = SS.StudentID 
            AND S2.SubjectCode = 'LITERATURE'
            AND S2.ResultDate >= DATEADD(MONTH,-1,SS.ResultDate)
            ORDER BY s2.ResultDate DESC
    ) AS LiteratureResultDate
FROM @StudentScores SS
WHERE SS.SubjectCode = 'MATHS'

预期结果

enter image description here

1 个答案:

答案 0 :(得分:1)

我设法将查询减少到两次调用数据表 - 一次用于获取Maths详细信息,因为他们的日期用于提取其他主题的详细信息,其次用于其他主题:

WITH DataSource_Maths AS
(
    SELECT SS.[StudentID]
          ,SS.[Score] AS [MathsScore]
          ,SS.[ResultDate] AS [MathsResultDate]
          -- we are using this interal ID later in the final join between the two CTEs
          -- in order to know which record, for which date period refers
          ,ROW_NUMBER() OVER(ORDER BY SS.[StudentID], SS.[ResultDate]) AS InternalID
    FROM @StudentScores SS
    WHERE SS.[SubjectCode] = 'MATHS'
),
DataSource_Others AS
(
    SELECT DS.[StudentID]
          ,DS.[SubjectCode]
          ,DS.[Score]
          ,DS.[ResultDate]
          ,Ds.[RowID]
          ,SS.[InternalID]
    FROM DataSource_Maths SS
    OUTER APPLY
    (
        SELECT *
               -- calculating row ID for each record across student and subject (we are going to take only the latest ones)
               -- this is achived using TOP in your example
              ,DENSE_RANK() OVER (PARTITION BY [StudentID], [SubjectCode] ORDER BY [ResultDate] DESC) AS [RowID]
        FROM @StudentScores
        WHERE
        ( 
            [ResultDate] >= DATEADD(MONTH, -2, SS.[MathsResultDate]) AND [SubjectCode] = 'SCIENCE'
            OR
            [ResultDate] >= DATEADD(MONTH, -1, SS.[MathsResultDate]) AND [SubjectCode] = 'LITERATURE' 
        ) AND [StudentID] = SS.[StudentID]
    ) DS
)
SELECT FDS_M.[StudentID]
      ,FDS_M.[MathsScore] AS [MathsScore]
      ,FDS_M.[MathsResultDate] AS [MathsResultDate]
      ,FDS_S.[Score] AS [ScienceScore]
      ,FDS_S.[ResultDate] AS [ScienceResultDate] 
      ,FDS_L.[Score] AS [LiteratureScore]
      ,FDS_L.[ResultDate] AS [LiteratureResultDate] 
FROM DataSource_Maths FDS_M
LEFT JOIN DataSource_Others FDS_S
    ON FDS_M.[InternalID] = FDS_S.[InternalID]
    AND FDS_S.[SubjectCode] = 'SCIENCE'
    AND FDS_S.[RowID] = 1
LEFT JOIN DataSource_Others FDS_L
    ON FDS_M.[InternalID] = FDS_L.[InternalID]
    AND FDS_L.[SubjectCode] = 'LITERATURE'
    AND FDS_L.[RowID] = 1;

当然,在更复杂的示例中,您可以实现临时表中的CTE子句(例如),以简化和优化查询。