LCJ*_*LCJ 5 sql t-sql sql-server
我在SQL Server 2012中有一个如下所示的StudentScores表.评分系统使用特殊规则加权.对于学生的每个MATHS结果,结果集中将有一行.根据"在科学的MATHS结果日期的两个月内"和"在数学的MATHS结果日期的一个月内"是否有可用的分数,该行可能有也可能没有科学和文学专栏的分数.
注意:这是我为简化实际业务域问题而创建的方案.
我用子查询创建了以下查询.有没有办法在没有子查询的情况下更有效地重写它?
表
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
Run Code Online (Sandbox Code Playgroud)
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'
Run Code Online (Sandbox Code Playgroud)
预期结果
我设法将查询减少为对数据表的两次调用 - 一次用于获取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;
Run Code Online (Sandbox Code Playgroud)
当然,在更复杂的示例中,您可以CTE在临时表中具体化子句(例如),以简化和优化查询。
| 归档时间: |
|
| 查看次数: |
76 次 |
| 最近记录: |