Как я могу создать временную таблицу, заполненную датами в SQL Server 2000? - PullRequest
11 голосов
/ 25 мая 2011

Мне нужно создать временную таблицу, которая содержит диапазон дат, а также пару столбцов, которые содержат значения заполнителей (0) для будущего использования.Даты, которые мне нужны, это первый день каждого месяца между $ startDate и $ endDate, где эти переменные могут быть разнесены на несколько лет.

Мой оригинальный SQL-запрос выглядел так:

select dbo.FirstOfMonth(InsertDate) as Month, 0 as Trials, 0 as Sales
into #dates
from customer
group by dbo.FirstOfMonth(InsertDate)

"FirstOfMonth "- это пользовательская функция, которую я сделал, которая в основном выполняет то, что говорит, возвращая первый день месяца для предоставленной даты со временем ровно в полночь.

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

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

declare $startDate set $startDate = select min(InsertDate) from customer
declare $endDate set $endDate = select max(InsertDate) from customer

... но я понятия не имею, что делать отсюда.

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

Ответы [ 10 ]

23 голосов
/ 02 июля 2013

Мне нужно что-то подобное, но все ДНИ вместо всех МЕСЯЦЕВ.

Используя код из MatBailie в качестве отправной точки, вот SQL для создания постоянной таблицы со всеми датами с 2000-01-01 по 2099-12-31:

CREATE TABLE _Dates (
  d DATE,
  PRIMARY KEY (d)
)
DECLARE @dIncr DATE = '2000-01-01'
DECLARE @dEnd DATE = '2100-01-01'

WHILE ( @dIncr < @dEnd )
BEGIN
  INSERT INTO _Dates (d) VALUES( @dIncr )
  SELECT @dIncr = DATEADD(DAY, 1, @dIncr )
END
15 голосов
/ 25 мая 2011

Это быстро заполнит таблицу датами на 170 лет.

CREATE TABLE CalendarMonths (
  date DATETIME,
  PRIMARY KEY (date)
)

DECLARE
  @basedate DATETIME,
  @offset   INT
SELECT
  @basedate = '01 Jan 2000',
  @offset = 1

WHILE (@offset < 2048)
BEGIN
  INSERT INTO CalendarMonths SELECT DATEADD(MONTH, @offset, date) FROM CalendarMonths
  SELECT @offset = @offset + @offset
END

Затем вы можете использовать его, ЛЕВО присоединяясь к этой таблице, для требуемого диапазона дат.

3 голосов
/ 06 января 2016

Это, конечно, не будет работать в SQL-Server 2000, но в современной базе данных, где вы не хотите создавать постоянную таблицу.Вы можете использовать переменную таблицы вместо создания таблицы, чтобы вы могли присоединиться к данным, попробуйте это.Измените ДЕНЬ на ЧАС и т. Д., Чтобы изменить тип приращения.

declare @CalendarMonths table (date DATETIME,  PRIMARY KEY (date)
)

DECLARE
  @basedate DATETIME,
  @offset   INT
SELECT
  @basedate = '01 Jan 2014',
  @offset = 1
  INSERT INTO @CalendarMonths SELECT @basedate

WHILE ( DATEADD(DAY, @offset, @basedate) < CURRENT_TIMESTAMP)
BEGIN
  INSERT INTO @CalendarMonths SELECT DATEADD(HOUR, @offset, date) FROM @CalendarMonths where DATEADD(DAY, @offset, date) < CURRENT_TIMESTAMP
  SELECT @offset = @offset + @offset
END
3 голосов
/ 25 мая 2011

Я бы, наверное, использовал таблицу календаря.Создайте постоянную таблицу в вашей базе данных и заполните ее всеми датами.Даже если вы охватите 100-летний диапазон, таблица все равно будет содержать ~ 36 525 строк.

CREATE TABLE dbo.Calendar (
    calendar_date    DATETIME    NOT NULL,
    is_weekend       BIT         NOT NULL,
    is_holiday       BIT         NOT NULL,
    CONSTRAINT PK_Calendar PRIMARY KEY CLUSTERED (calendar_date)
)

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

Тогда ваш запрос может выглядеть примерно так:

SELECT
    C.calendar_date,
    0 AS trials,
    0 AS sales
FROM
    dbo.Calendar C
WHERE
    C.calendar_date BETWEEN @start_date AND @end_date AND
    DAY(C.calendar_date) = 1

Вы можете присоединиться к таблице «Клиенты» так, как вам нужно, к внешнему объединению на FirstOfMonth(InsertDate) = C.calendar_date, если это то, чтовы хотите.

Вы также можете включить столбец для day_of_month, если хотите, чтобы избежать затрат на вызов функции DAY(), но это довольно тривиально, так что, вероятно, это не имеет значения, так или иначе.

2 голосов
/ 02 августа 2017

Начальная точка полезного кладжа для указания диапазона или определенного списка дат:

SELECT *
FROM 
    (SELECT CONVERT(DateTime,'2017-1-1')+number AS [Date]
     FROM master..spt_values WHERE type='P' AND number<370) AS DatesList
WHERE DatesList.Date IN ('2017-1-1','2017-4-14','2017-4-17','2017-12-25','2017-12-26')

Вы можете получить от 0 до 2047 из master..spt_values WHERE type='P', так что это ценность пяти с половиной летдаты, если вам это нужно!

1 голос
/ 25 мая 2011

Проверено ниже, и это работает, хотя и немного запутанно.

Я назначил произвольные значения датам теста.

DECLARE @SD smalldatetime,
        @ED smalldatetime,
        @FD smalldatetime,
        @LD smalldatetime,
        @Mct int,
        @currct int = 0

SET @SD = '1/15/2011'
SET @ED = '2/02/2012'


SET @FD = (DATEADD(dd, -1*(Datepart(dd, @SD)-1), @sd))
SET @LD = (DATEADD(dd, -1*(Datepart(dd, @ED)-1), @ED))

SET @Mct = DATEDIFF(mm, @FD, @LD)

CREATE TABLE #MyTempTable (FoM smalldatetime, Trials int, Sales money)

WHILE @currct <= @Mct
BEGIN
    INSERT INTO #MyTempTable (FoM, Trials, Sales)
    VALUES
    (DATEADD(MM, @currct, @FD), 0, 0)
    SET @currct = @currct + 1
END


SELECT * FROM #MyTempTable

DROP TABLE #MyTempTable
0 голосов
/ 03 января 2018

Создание табличной переменной, содержащей дату для каждого месяца в году:

declare @months table (reportMonth date, PRIMARY KEY (reportMonth));
declare @start date = '2018', @month int = 0; -- base 0 month
while (@offset < 12)
begin
    insert into @reportMonths select dateAdd(month, @offset, @start);
    select @offset = @offset + 1; 
end
0 голосов
/ 30 сентября 2016
declare @start datetime
set @start = '2016-09-01'
declare @end datetime
set @end = '2016-09-30'

create table #Date
(
    table_id int identity(1,1) NOT NULL,
    counterDate datetime NULL
);

insert into #Date select top (datediff(D,@start,@end)) NULL from SOME_TABLE
update #Date set counterDate = dateadd(D,table_id - 1, @start)

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

0 голосов
/ 21 июля 2016
select top (datediff(D,@start,@end)) dateadd(D,id-1,@start)
from BIG_TABLE_WITH_NO_JUMPS_IN_ID
0 голосов
/ 28 декабря 2015

Для SQL Server 2000 этот пост stackoverflow выглядит многообещающим для способа временного создания дат, рассчитанных на основе начальной и конечной даты. Это не совсем то же самое, но довольно похоже. В этом посте есть очень подробный ответ об усеченных датах, если необходимо.

В случае, если кто-то наткнется на этот вопрос и будет работать в PostgreSQL вместо SQL Server 2000, вот как вы можете сделать это там ...

PostgreSQL имеет отличную функцию генерации series . Для вашего примера вы можете использовать эту серию всех дней вместо генерации всей таблицы календаря, а затем выполнять группировки и сопоставления оттуда.

SELECT current_date + s.a AS dates FROM generate_series(0,14,7) AS s(a);
   dates
------------
 2004-02-05
 2004-02-12
 2004-02-19
(3 rows)

SELECT * FROM generate_series('2008-03-01 00:00'::timestamp,
                              '2008-03-04 12:00', '10 hours');
   generate_series   
---------------------
 2008-03-01 00:00:00
 2008-03-01 10:00:00
 2008-03-01 20:00:00
 2008-03-02 06:00:00
 2008-03-02 16:00:00
 2008-03-03 02:00:00
 2008-03-03 12:00:00
 2008-03-03 22:00:00
 2008-03-04 08:00:00
(9 rows)

Я бы также посмотрел на date_trunc из PostgreSQL, используя 'month' для поля усечения, чтобы, возможно, реорганизовать ваш исходный запрос для простого соответствия с версией date_trunc серии календаря.

...