Выберите каждую вторую запись, затем определите самую раннюю дату

#sql #sql-server #sql-order-by #greatest-n-per-group #window-functions

#sql #sql-сервер #sql-порядок по #наибольшее число записей на группу #окно-функции

Вопрос:

У меня есть таблица, которая выглядит следующим образом

введите описание изображения здесь

Я должен выбрать каждую вторую запись для каждого идентификатора пациента, которая дала бы следующий результат (мой последний запрос возвращает этот результат)

введите описание изображения здесь

Затем я должен выбрать запись с самой старой датой, которая будет следующей (это конечный результат, который я хочу)

введите описание изображения здесь

Что я сделал до сих пор: у меня есть CTE, который получает все необходимые мне данные

 WITH cte
AS 
(
    SELECT visit.PatientTreatmentVisitID, mat.PatientMatchID,pat.PatientID,visit.RegimenDate AS VisitDate, 
                        ROW_NUMBER() OVER(PARTITION BY mat.PatientMatchID, pat.PatientID ORDER BY visit.VisitDate ASC) AS RowNumber
    FROM tblPatient pat INNER JOIN tblPatientMatch mat ON mat.PatientID = pat.PatientID
    LEFT JOIN tblPatientTreatmentVisit visit ON visit.PatientID = pat.PatientID
)
  

Затем я пишу запрос к CTE, но пока я могу вернуть только вторую строку для каждого PatientID

 SELECT *
FROM 
(
    SELECT PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate,  RowNumber FROM cte    
) as X
WHERE RowNumber = 2 
  

Как мне вернуть запись только с самой старой датой? Возможно, есть функция MIN (), которую я мог бы где-нибудь включить?

Комментарии:

1. Было бы полезно, если бы вы указали, какую СУБД и версию вы используете, например, SQL Server 2008 (или что-то еще)

2. Я использую SQL Server 2016

3. Выделение нас таким жирным шрифтом не облегчает прочтения вопроса. Также в будущем мы предпочитаем, чтобы данные были форматированным текстом, а не изображениями.

Ответ №1:

Если я правильно вас понял, вы можете просто упорядочить свой существующий результирующий набор и сохранить только верхнюю строку.

В стандартном SQL вы бы написали это, используя FETCH предложение:

 SELECT *
FROM (
    SELECT
        visit.PatientTreatmentVisitID, 
        mat.PatientMatchID,
        pat.PatientID,
        visit.RegimenDate AS VisitDate, 
        ROW_NUMBER() OVER(PARTITION BY mat.PatientMatchID, pat.PatientID ORDER BY visit.VisitDate ASC) AS rn
    FROM tblPatient pat 
    INNER JOIN tblPatientMatch mat ON mat.PatientID = pat.PatientID
    LEFT JOIN tblPatientTreatmentVisit visit ON visit.PatientID = pat.PatientID
) t
WHERE rn = 2
ORDER BY VisitDate
OFFSET 0 ROWS FETCH FIRST 1 ROW ONLY
  

Этот синтаксис поддерживается в Postgres, Oracle, SQL Server (и, возможно, в других базах данных).

Ответ №2:

Если вам нужно получить самую старую дату из всех выбранных дат (каждая вторая строка для каждого идентификатора пациента), вы можете попробовать window function Min:

 SELECT * FROM
(
SELECT *, MIN(VisitDate) OVER (Order By VisitDate) MinDate
  FROM 
   (
    SELECT    PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate,  
    RowNumber FROM cte  
   ) as X
   WHERE RowNumber = 2 
) Y
WHERE VisitDate=MinDate
  

Или вы можете использовать инструкцию SELECT TOP. SELECT TOP clause Позволяет ограничить количество строк, возвращаемых в наборе результатов запроса:

 SELECT TOP 1 PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate FROM  
   (
     SELECT *
     FROM 
     (
      SELECT PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate,  
      RowNumber FROM cte    
     ) as X
   WHERE RowNumber = 2 
  ) Y
  ORDER BY VisitDate
  

Комментарии:

1. Грааф, ты пробовал запрос?

Ответ №3:

Для простоты добавьте order desc в столбец даты и используйте TOP для получения только первой строки

 SELECT TOP 1 *
FROM 
(
    SELECT PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate,  RowNumber FROM cte    
) as X
WHERE RowNumber = 2 
order by VisitDate desc