Месяцы между двумя датами - PullRequest
21 голосов
/ 25 октября 2011

Можно ли получить month names между двумя датами в SQl

, т. Е. 2011-05-01 И 2011-08-01 - это входные данные, которые я просто хочу вывести как

------------
Month
------------
May
June
July
August

Если какой-либо орган знает запрос, пожалуйста, поделитесь.

Ответы [ 9 ]

37 голосов
/ 25 октября 2011
DECLARE @StartDate  DATETIME,
        @EndDate    DATETIME;

SELECT   @StartDate = '20110501'        
        ,@EndDate   = '20110801';


SELECT  DATENAME(MONTH, DATEADD(MONTH, x.number, @StartDate)) AS MonthName
FROM    master.dbo.spt_values x
WHERE   x.type = 'P'        
AND     x.number <= DATEDIFF(MONTH, @StartDate, @EndDate);

Результаты:

MonthName
------------------------------
May
June
July
August

(4 row(s) affected)
29 голосов
/ 25 октября 2011

Вы можете сделать это с помощью рекурсивного CTE, создав таблицу дат и получив название месяца для каждого:

declare @start DATE = '2011-05-01'
declare @end DATE = '2011-08-01'

;with months (date)
AS
(
    SELECT @start
    UNION ALL
    SELECT DATEADD(month,1,date)
    from months
    where DATEADD(month,1,date)<=@end
)
select Datename(month,date) from months
8 голосов
/ 18 сентября 2014

Я изменил Ответ Джеймика , чтобы вывести также последний день месяца.

declare @start DATE = '2014-05-01'
declare @end DATE = getdate()

;with months (date)
AS
(
    SELECT @start
    UNION ALL
    SELECT DATEADD(month,1,date)
    from months
    where DATEADD(month,1,date) < @end
)
select     [MonthName]    = DATENAME(mm ,Date)  
            ,[MonthNumber]  = DATEPART(mm ,Date)  
            ,[LastDayOfMonth]  = DATEPART(dd,EOMONTH(Date))
            ,[MonthYear]    = DATEPART(yy ,Date) 
from months

Что дает вывод:

MonthName   MonthNumber LastDayOfMonth  MonthYear
May         5           31              2014
June        6           30              2014
July        7           31              2014
August      8           31              2014
September   9           30              2014
2 голосов
/ 23 ноября 2016

Вдохновлен ответом Джеймица , но исправил проблему с помощью значения от day больше, чем до day:

declare @start DATE
declare @end DATE 

SELECT  @start='2011-05-19' , @end='2011-08-15' 

;with months (date)
AS
(
   SELECT DATEADD(DAY,1,EOMONTH(@start,-1))
    UNION ALL
    SELECT DATEADD(month,1,date)
    from months
    where DATEADD(month,1,date) < EOMONTH(@end)
)
select Datename(month,date)
from months
1 голос
/ 10 октября 2013
    declare @start DATE = '2011-05-30'
    declare @end DATE = '2011-06-10' 
   ;with months (date)
    AS
    (
        SELECT @start
        UNION ALL
        SELECT DATEADD(month,1,date)
        from months
        where DATEADD(month,1,date)<= DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,@end)+1,0))
    )
    select Datename(month,date) from months
0 голосов
/ 01 апреля 2019

Функция создания базы данных, как показано ниже

CREATE FUNCTION [dbo].[DateRange] 
(@Identifier CHAR(1),@StartDate DATETIME,@EndDate DATETIME)
RETURNS @SelectedRange TABLE(Dates DATE) AS
 BEGIN

   ;WITH cteRange (DateRange) AS (
   SELECT @StartDate
     UNION ALL
   SELECT
     CASE
         WHEN Upper(@Identifier) = 'H' THEN DATEADD(hh, 1, DateRange)
         WHEN Upper(@Identifier) = 'D' THEN DATEADD(dd, 1, DateRange)
         WHEN Upper(@Identifier) = 'W' THEN DATEADD(ww, 1, DateRange)
         WHEN Upper(@Identifier) = 'M' THEN DATEADD(mm, 1, DateRange)
         WHEN Upper(@Identifier) = 'Y' THEN DATEADD(yy, 1, DateRange)
     END
   FROM cteRange
   WHERE DateRange <=
    CASE
        WHEN Upper(@Identifier) = 'H' THEN DATEADD(hh, -1, @EndDate)
        WHEN Upper(@Identifier) = 'D' THEN DATEADD(dd, -1, @EndDate)
        WHEN Upper(@Identifier) = 'W' THEN DATEADD(ww, -1, @EndDate)
        WHEN Upper(@Identifier) = 'M' THEN DATEADD(mm, -1, @EndDate)
        WHEN Upper(@Identifier) = 'Y' THEN DATEADD(yy, -1, @EndDate)
    END)
 INSERT INTO @SelectedRange (Dates) SELECT DateRange FROM cteRange
  OPTION (MAXRECURSION 3660);
RETURN
END

Затем с помощью функции мы можем сгенерировать диапазон дат

SELECT * from dbo.DateRange('M','1953-01-01','2019-01-01')

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

DECLARE @tblDateRange TABLE (AutoID INT IDENTITY(1,1),DateRange DATE)
INSERT INTO @tblDateRange SELECT * from dbo.DateRange('M','1953-01-01','2019-01-01')
SELECT 
LEFT(DATENAME(MONTH,DateRange),3) [MonthYearValue],YEAR(DateRange) AS [Year]
FROM @tblDateRange

На основании наших потребностей мы можем изменить

OPTION (MAXRECURSION 3660)
0 голосов
/ 29 сентября 2016

Ну, @bogdhan sahlean дал хорошее решение на основе множеств, но ограничивает значения до 2048, учитывая тип данных date и datetime2, для которого диапазон для года составляет 0001-01-01 до 9999-12-31, от MSDN

Диапазон дат от 0001-01-01 до 9999-12-31

С 1 января по 31 декабря 9999 года CE

хотя это крайний случай, но стоит знать.Так как, если в один прекрасный день кто-то попытается спроецировать месяцы более 170 лет:)

Даже самые одобренные ответы не удовлетворяют некоторым крайним случаям (когда дата начала> дата окончания не будет показывать месяц даты окончания,также рекурсивный запрос не выполняется после 100 выполнений по умолчанию).А также использование рекурсивного cte для итерации, что является резким падением производительности при массовом использовании.

Теперь лучшим решением (IMHO) является использование таблицы календаря или таблицы подсчета для генерации месяцев между двумя датами.Если невозможно создать таблицу, есть лучшая альтернатива - использовать каскадный CTE Ицик Бен Ганса для создания таблицы чисел. ( здесь ) Что быстрее, нет логических, физических чтений, нет рабочего стола NADA

Вот код

DECLARE @start DATETIME2 = '00010101'
DECLARE @end DATETIME2 = '99991231'
;WITH lv0 AS (SELECT 0 g UNION ALL SELECT 0)
    ,lv1 AS (SELECT 0 g FROM lv0 a CROSS JOIN lv0 b) -- 4
    ,lv2 AS (SELECT 0 g FROM lv1 a CROSS JOIN lv1 b) -- 16
    ,lv3 AS (SELECT 0 g FROM lv2 a CROSS JOIN lv2 b) -- 256
    ,lv4 AS (SELECT 0 g FROM lv3 a CROSS JOIN lv3 b) -- 65,536
    ,lv5 AS (SELECT 0 g FROM lv4 a CROSS JOIN lv4 b) -- 4,294,967,296
    ,Tally (n) AS (SELECT 0 UNION SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM lv5)
SELECT DATENAME(YEAR,DATEADD(MONTH,N,@start)) AS [Year Part], DATENAME(MONTH,DATEADD(MONTH,n,@start)) AS [Month Part]
FROM Tally where N between 0 and DATEDIFF(mm,@start,@end)  
ORDER BY n;

Примечание: я добавил SELECT 0, чтобы начать числа с 0-й позиции

Производительность, показанная на моем ПК,

Метод Ицик

(затронуто 119988 строк)

Время выполнения SQL Server: время ЦП = 187 мс, истекшее время = 706 мс.Время анализа и компиляции SQL Server: время ЦП = 0 мс, истекшее время = 0 мс.

Одно из приведенных здесь рекурсивных решений, которое занимает время

(119988 строка (и) затронута) Таблица «Рабочий стол».Число сканирований 2, логическое чтение 719923, физическое чтение 0, чтение с опережением 0, чтение логического объекта 0, чтение с физического объекта 0, чтение с опережением 0.

Время выполнения SQL Server: время ЦП = 890мс, прошедшее время = 1069 мс.

Производительность между таблицей подсчета, таблицей календаря и таблицей номеров Ицик может немного отличаться, но работает как очарование для всего диапазона дат, который вы предоставляете.

0 голосов
/ 09 января 2015

Попробуйте это:

declare 
       @sd date=getdate(),
       @ld date='2016-01-01'

select 
     Datename(month,dateadd(month,number,GETDATE())), 
     number
from master.dbo.spt_values 
where type='p' 
     and dateadd(month,number,GETDATE()) <= @ld
0 голосов
/ 25 октября 2011

Если у вас есть таблица MonthNames, содержащая названия каждого месяца, вы можете запустить

SELECT MonthName FROM MonthNames WHERE MonthNumber BETWEEN Month(&date1) AND Month(&date2);

Таблица MonthNames будет выглядеть как

MonthName, MonthNumber
1 января 1008 года 2 февраля
3 марта

и так далее ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...