2017-10-24 118 views
5

Tôi có bảng StudentScores như được liệt kê bên dưới, trong SQL Server 2012. Hệ thống chấm điểm được cân bằng sử dụng các quy tắc đặc biệt. Đối với mỗi kết quả MATHS của học sinh, sẽ có một hàng trong tập kết quả. Hàng có thể có hoặc không có điểm cho các cột KHOA HỌC và LITERATURE dựa trên việc có điểm số "trong vòng hai tháng kể từ ngày kết thúc MATHS cho KHOA HỌC" và "trong vòng một tháng kể từ ngày kết thúc MATHS cho LITERATURE".Tránh truy vấn con để chọn các bản ghi từ cùng một bảng dựa trên ngày của bản ghi cơ sở

Lưu ý: Đây là trường hợp tôi đã tạo để đơn giản hóa vấn đề miền kinh doanh thực tế của mình.

Tôi đã tạo truy vấn sau với truy vấn phụ. Có cách nào để viết lại nó mà không có truy vấn phụ và hiệu quả hơn không?

TABLE

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' 

Dự kiến ​​kết quả

enter image description here

Trả lời

1

tôi đã quản lý để giảm truy vấn đến hai cuộc gọi đến các bảng dữ liệu - một để nhận được Maths chi tiết như ngày của họ được sử dụng để trích xuất các chi tiết cho các đối tượng khác và thứ hai cho các đối tượng khác:

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; 

Tất nhiên trong ví dụ phức tạp hơn của bạn, bạn có thể thực hiện các mệnh đề CTE trong các bảng tạm thời (ví dụ) để đơn giản hóa và tối ưu hóa truy vấn.

Các vấn đề liên quan