2011-05-25 285 views
9

Necesito hacer una tabla temporal que contenga rango de fechas, así como un par de columnas que contengan valores de marcador (0) para uso futuro. Las fechas que necesito son el primer día de cada mes entre $ startDate y $ endDate, donde estas variables pueden tener varios años de diferencia.¿Cómo puedo generar una tabla temporal llena de fechas en SQL Server 2000?

Mi instrucción SQL original, era la siguiente:

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

"FirstOfMonth" es una función definida por el usuario que hice que más o menos hace lo que dice, volviendo el primer día del mes de la fecha prevista con la hora exactamente a la medianoche.

Esto produjo casi exactamente lo que necesitaba hasta que descubrí que había ocasionalmente lagunas en mis fechas en las que tenía unos meses si no había registros insertos. Como mi resultado aún debe tener los meses faltantes, necesito un enfoque diferente.

he añadido las siguientes declaraciones al procedimiento almacenado anticipar su necesidad para el rango de las fechas que necesito ...

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

... pero no tengo ni idea de qué hacer desde aquí.

Sé que esta pregunta es similar to this question pero, francamente, esa respuesta está por encima de mi cabeza (no suelo trabajar con SQL y cuando lo hago tiende a ser en versiones anteriores de SQL Server) y hay algunas diferencias menores que me están tirando.

+0

Utilice [MASTER.dbo.SPT_VALUES] (http://www.sqlteam.com/forums/topic.asp?TOPIC_ID = 102936) para obtener una lista de números, y utilizar DATEADD a su vez, aquellos en los datetimes –

Respuesta

14

Esto rellenará rápidamente una tabla con 170 años de fechas.

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 

entonces Se puede utilizar por IZQUIERDA unirse a esa mesa, para el rango de fechas que se necesita.

+2

tenía que sembrar la tabla CalendarMonths con el fechabase a conseguir que funcione: 'insertar en @CalendarMonths SELECT @ basedate' también cambió la tabla a una variable de tabla para que fuera todo temporal y también en memoria. –

+1

@edmicman - ¿Por qué temporal? Seguramente vale la pena persistir/almacenar en caché si alguna vez lo usas más de una vez? – MatBailie

+1

en mi caso específico, quiero obtener una lista de meses/años dentro de un rango de fecha de entrada. Tal vez una tabla estática sería buena, pero no me topa con la necesidad con la frecuencia suficiente como para crear un lugar donde solo crearlo sobre la marcha es un problema. –

3

Probablemente usaría una tabla de calendario. Cree una tabla permanente en su base de datos y llénela con todas las fechas. Incluso si cubrieras un rango de 100 años, la tabla solo tendría ~ 36,525 filas.

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) 
) 

Una vez creada la tabla, justo lo pueblan una vez en un bucle, por lo que siempre es por ahí y disponible para usted.

su consulta a continuación, podría ser algo como esto:

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 

Puede unirse a la tabla Clientes sin embargo es necesario, uniéndose exterior sobre FirstOfMonth(InsertDate) = C.calendar_date si eso es lo que quiere.

También puede incluir una columna para DAY_OF_MONTH si quieres que evite la sobrecarga de llamar a la función DAY(), pero eso es bastante trivial, por lo que probablemente no importa de una manera u otra.

1

Probado a continuación y funciona, aunque es un poco intrincado.

Asigné valores arbitrarios a las fechas de la prueba.

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 
16

Necesitaba algo similar, pero todos los DÍAS en lugar de todos los MESES.

Utilizando el código de MatBailie como punto de partida, aquí está el código SQL para crear una tabla permanente con todas las fechas desde el 2000-01-01 a 31/12/2099:

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 
0

Para SQL Server 2000, este stackoverflow post parece prometedor para generar temporalmente fechas calculadas a partir de una fecha de inicio y finalización. No es exactamente lo mismo, pero bastante similar. This post tiene una respuesta muy detallada sobre truncar fechas, si es necesario.

En caso de que alguien se tropieza en esta cuestión y está trabajando en PostgreSQL en lugar de SQL Server 2000, aquí es cómo puede hacerlo allí ...

PostgreSQL tiene un ingenioso series generating function. Para su ejemplo, puede usar esta serie de todos los días en lugar de generar una tabla de calendario completa, y luego hacer agrupaciones y enfrentamientos desde allí.

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) 

yo también mirar en date_trunc de PostgreSQL usando 'mes' para el campo truncador refactorizar tal vez su búsqueda original para que coincida con facilidad con una versión date_trunc de la serie del calendario.

+0

Esto no responde a la pregunta, que está dirigida a SQL Server 2000. – Lunyx

+0

Actualizada para hacer referencia a otra publicación de stackoverflow que responde por SQL Server y aclara la parte de la respuesta que es para PostgreSQL – Sia

3

Esto, por supuesto, no funcionará en SQL-Server 2000, pero en una base de datos moderna donde no desea crear una tabla permanente. Puede usar una variable de tabla en lugar de crear una tabla para que pueda unir los datos intente esto. Cambia el DÍA a la HORA, etc. para cambiar el tipo de incremento.

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 
0
select top (datediff(D,@start,@end)) dateadd(D,id-1,@start) 
from BIG_TABLE_WITH_NO_JUMPS_IN_ID 
0
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) 

El código anterior debe rellenar la tabla con todas las fechas entre el comienzo y el final. Entonces simplemente te unirías a esta mesa para obtener todas las fechas necesarias. Si solo necesita un cierto día de cada mes, puede agregar un mes en su lugar.

2

Un punto de partida de un kludge útil para especificar un rango o lista específica de fechas:

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') 

Puede recibe el 0 a 2047 de master..spt_values WHERE type='P', por lo que es de cinco y el valor de las fechas de un año y medio si ¡necesito!

0

Crear una variable de tabla que contiene una fecha para cada mes en un año:

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 
Cuestiones relacionadas