Получение данных за последний день месяца без использования циклов или переменных

#sql-server #database

#sql-сервер #База данных

Вопрос:

Я написал запрос, который должен выбирать последнюю запись каждого месяца в году. Я хотел бы создать представление на основе этого выбора, которое я мог бы запустить позже в своем проекте, но, к сожалению, я не могу использовать какие-либо циклы while или переменные в команде view. Есть ли способ выбрать все эти записи — последние дни месяца в представлении, которое я могу использовать позже?

Мой желаемый эффект от представления:

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

Запрос, который я пытаюсь реализовать в представлении:

 DECLARE @var_day01 DATETIME;
DECLARE @month int;
SET @month = 1;
DROP TABLE IF EXISTS #TempTable2;
CREATE TABLE #TempTable2 (ID int, date datetime, INP2D float, INP3D float, ID_device varchar(max));
WHILE @month < 13
BEGIN
SELECT @var_day01 = CONVERT(nvarchar, date)  FROM (SELECT TOP 1 * FROM data
WHERE DATEPART(MINUTE, CONVERT(nvarchar, date)) = '59'
AND
MONTH(CONVERT(nvarchar, date)) = (CONVERT(nvarchar, @month))
ORDER BY date DESC
 ) results
ORDER BY date DESC;

INSERT INTO #TempTable2 (ID, date, INP2D,INP3D,ID_device) 
SELECT * FROM data
WHERE DATEPART(MINUTE, CONVERT(nvarchar, date)) = '59'
AND
MONTH(CONVERT(nvarchar, date)) = (CONVERT(nvarchar, @month))
AND
DAY(CONVERT(nvarchar, date)) = CONVERT(datetime, DATEPART(DAY, @var_day01))
ORDER BY date DESC
PRINT @var_day01
SET @month = @month  1;
END


SELECT * FROM #TempTable2;
  

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

1. Вы говорите, что вам нужна «последняя запись каждого месяца», но ваш запрос фактически возвращает все записи, в которых указана минута 59 в пределах каждого месяца, что приводит к появлению нескольких строк за несколько месяцев, которые вы можете видеть на своем скриншоте, а также к нескольким другим очевидным проблемам. Чего вы на самом деле пытаетесь достичь здесь, поскольку ваш запрос вообще не соответствует вашему вопросу …?

Ответ №1:

Если вы на самом деле просматриваете только одну самую последнюю строку за каждый месяц, while цикл для достижения этой цели не требуется. Вам просто нужно определить max date значение для каждого месяца, а затем отфильтровать исходные данные для этих строк.

Один из способов добиться этого — с помощью row_number оконной функции:

 declare @t table(id int,dt datetime2);
insert into @t values(1,getdate()-40),(2,getdate()-35),(3,getdate()-25),(4,getdate()-10),(5,getdate());

select id
      ,id_device
      ,dt
from(select id
           ,id_device
           ,dt
           ,row_number() over (partition by id_device, year(dt), month(dt) order by dt desc) as rn
     from @t
    ) as d
where rn = 1;
  

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

1. Предоставленный вами ответ великолепен, и он работает, но у меня есть еще одна проблема, которую я не затронул в сообщении. У меня есть разные устройства, соответствующие строке ID_device, моя цель — получить все самые последние строки каждого месяца с каждого из уникальных устройств. В вашем запросе — я получаю самые последние данные за каждый месяц, но только с самого последнего устройства, отправившего данные. Есть ли способ изменить запрос, чтобы он также выбирал все записи, в которых ID_device является уникальным?

2. @Xarxes обновлен, чтобы включить id_device группировку

Ответ №2:

Вы можете добавить простое where к вашему select заявлению, в where предложении вы добавите один день в date поле, а затем select day из результирующего date . Если результат date равен 1 , то только вы получите select эту запись

where предложение для вашего запроса будет : Where Day(DATEADD(d,1,[date])) = 1

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

1. Это не сработает, если в последний день месяца имеется более одной записи, как это видно на скриншоте OP

2. Ооо, я пропустил эту часть. Также мой ответ касается последнего дня месяца. Поэтому полностью пропустил это. В этом случае нам нужно использовать функцию Rank() в select с разделением по месяцу и году RANK() (РАЗДЕЛЕНИЕ по месяцу (дате), порядок года (даты) по дате desc) и выбрать записи, ранг которых равен 1