Получить самую раннюю дату, соответствующую последнему появлению повторяющегося имени

#sql #sql-server #tsql #sql-server-2008 #select

Вопрос:

У меня есть таблица со столбцами Name и Date. Я хочу получить самую раннюю дату появления текущего имени. Например:

Имя Дата
X 30 января-2021
X 29 января-2021
X 28 января-2021
Y 27 января-2021
Y 26 января-2021
Y 25 января-2021
Y 24 января-2021
X 23 января-2021
X 22 января-2021

Теперь, когда я пытаюсь получить самую раннюю дату, когда начало появляться текущее имя (X), я хочу получить 28 января, но sql-запрос выдаст 22 января 2021 года, потому что именно тогда X появился изначально в первый раз.

Обновление: это был запрос, который я использовал:

 Select min(Date) from myTable where Name='X'
 

Я использую более старый SQL Server 2008 (в процессе обновления), поэтому у меня нет доступа к функциям ОПЕРЕЖЕНИЯ / ЗАДЕРЖКИ.
Предлагаемые ниже решения работают так, как предполагалось. Спасибо.

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

1. Вы случайно имеете в виду: вам нужна самая ранняя дата последней группы строк с одинаковым Name значением?

2. Вам необходимо предоставить всю соответствующую информацию, такую как первичный ключ вашей таблицы (предположительно, он у вас есть), поскольку, как отмечалось, предоставленного вами недостаточно для поддержки требования.

Ответ №1:

Это тип проблемы пробелов и островов.

Существует много решений. Вот тот, который оптимизирован для вашего случая

  • Используется LEAD/LAG для определения первой строки в каждой группе
  • Фильтровать только эти строки
  • Пронумеруйте их строками и возьмите первую
 WITH StartPoints AS (
    SELECT *,
      IsStart = CASE WHEN Name <> LEAD(Name, 1, '') OVER (ORDER BY Date DESC) THEN 1 END
    FROM YourTable
),
Numbered AS (
    SELECT *,
      rn = ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Date DESC)
    FROM StartPoints
    WHERE IsStart = 1 AND Name = 'X'
)
SELECT
  Name, Date
FROM Numbered
WHERE rn = 1;
 

db<>скрипка

Для SQL Server 2008 или более ранней версии (с которой я настоятельно рекомендую вам обновиться), вы можете использовать самосоединение с нумерацией строк для имитации LEAD/LAG

 WITH RowNumbered AS (
    SELECT *,
      AllRn = ROW_NUMBER() OVER (ORDER BY Date ASC)
    FROM YourTable
),
StartPoints AS (
    SELECT r1.*,
      IsStart = CASE WHEN r1.Name <> ISNULL(r2.Name, '') THEN 1 END
    FROM RowNumbered r1
    LEFT JOIN RowNumbered r2 ON r2.AllRn = r1.AllRn - 1
),
Numbered AS (
    SELECT *,
      rn = ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Date DESC)
    FROM StartPoints
    WHERE IsStart = 1
)
SELECT
  Name, Date
FROM Numbered
WHERE rn = 1;
 

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

1. Спасибо, но моя версия sql пока не поддерживает функции ОПЕРЕЖЕНИЯ / ЗАДЕРЖКИ. Что-нибудь без них?

2. @goodsamaritan это будет означать, что вы используете полностью неподдерживаемую версию SQL Server. Предполагается , что когда вы задаете вопрос, вы используете поддерживаемое программное обеспечение, и если это не так, вам нужно сообщить об этом пользователям (обычно путем пометки версии). Если у вас нет доступа к LEAD / LAG вы используете SQL Server 2008 или более раннюю версию; как это было добавлено в SQL server 2012 (у которого осталось около 9 месяцев расширенной поддержки). 2008 полностью не поддерживается более 2 лет, а предыдущие версии намного дольше. Давно пора отсортировать этот путь обновления.

3. Здесь вы можете смоделировать это с помощью самосоединения

Ответ №2:

Это проблема пробелов и островков. На основе выборочных данных это будет работать:

 WITH Groups AS(
    SELECT YT.[Name],
           YT.[Date],
           ROW_NUMBER() OVER (ORDER BY YT.Date DESC) - 
           ROW_NUMBER() OVER (PARTITION BY YT.[Name] ORDER BY Date DESC) AS Grp
    FROM dbo.YourTable YT),
FirstGroup AS(
    SELECT TOP (1) WITH TIES
           G.[Name],
           G.[Date]
    FROM Groups G
    WHERE [Name] = 'X'
    ORDER BY Grp ASC)
SELECT MIN(FG.[Date]) AS Mi
 

db<>скрипка

Ответ №3:

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

это и пример того, как это обнаружить

 SELECT name
 ,DATE
FROM (
SELECT *
    ,DATEDIFF(day, lead(DATE) OVER (
            PARTITION BY name ORDER BY DATE DESC
            ), DATE) DIF
FROM YourTable
) a
WHERE DIF > 1