web-dev-qa-db-fra.com

Comment générer une table temporaire remplie de dates dans SQL Server 2000?

Je dois créer une table temporaire contenant une plage de dates, ainsi que quelques colonnes contenant des valeurs de substitution (0) pour une utilisation ultérieure. Les dates dont j'ai besoin sont le premier jour de chaque mois entre $ startDate et $ endDate où ces variables peuvent être séparées de plusieurs années.

Ma déclaration SQL originale ressemblait à ceci:

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

"FirstOfMonth" est une fonction définie par l'utilisateur que j'ai faite qui fait à peu près ce qu'elle dit, en retournant le premier jour du mois pour la date fournie avec l'heure exacte minuit.

Cela a produit presque exactement ce dont j'avais besoin jusqu'à ce que je découvre qu'il y avait parfois des lacunes dans mes dates et que, quelques mois auparavant, il n'y avait pas de dates d'insertion de disques. Comme mon résultat doit encore avoir les mois manquants, il me faut une approche différente.

J'ai ajouté les déclarations suivantes à la procédure stockée en anticipant leur besoin pour la plage de dates dont j'ai besoin ...

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

... mais je ne sais pas quoi faire d'ici.

Je sais que cette question est semblable à cette question mais, très franchement, cette réponse est sur ma tête (je ne travaille pas souvent avec SQL et quand je le fais, cela a tendance à être sur des versions plus anciennes de SQL Server) et il y a quelques différences mineures qui me jettent.

10
Sailing Judo

Cela va rapidement peupler une table avec 170 années de dates.

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

Vous pouvez ensuite l'utiliser en rejoignant LEFT sur cette table, pour la plage de dates souhaitée.

14
MatBailie

J'avais besoin de quelque chose de similaire, mais tous les jours au lieu de tous les mois.

En utilisant le code de MatBailie comme point de départ, voici le code SQL permettant de créer une table permanente avec toutes les dates du 2000-01-01 au 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
22
ClubbedAce

J'utiliserais probablement une table de calendrier. Créez une table permanente dans votre base de données et remplissez-la avec toutes les dates. Même si vous couvriez une plage de 100 ans, le tableau ne contiendrait toujours que 36 525 lignes.

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

Une fois la table créée, remplissez-la une fois dans une boucle, de sorte qu'elle soit toujours disponible et disponible.

Votre requête pourrait alors ressembler à ceci:

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

Vous pouvez rejoindre la table Customers à votre guise, en vous joignant à FirstOfMonth(InsertDate) = C.calendar_date si c'est ce que vous voulez.

Vous pouvez également inclure une colonne pour day_of_month si vous le souhaitez, ce qui éviterait l’appel lié à l’appel de la fonction DAY(), mais c’est assez simple, ce qui n’importe donc probablement pas, d’une manière ou d’une autre.

3
Tom H

Bien entendu, cela ne fonctionnera pas dans SQL-Server 2000, mais dans une base de données moderne où vous ne souhaitez pas créer de table permanente. Vous pouvez utiliser une variable de table à la place pour créer une table afin de pouvoir rejoindre les données à gauche, essayez ceci. Changez le JOUR en HEURE, etc. pour changer le type d’incrément. 

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
kariato

Un point de départ d’un kludge utile pour spécifier une plage ou une liste spécifique de dates:

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

Vous pouvez obtenir de 0 à 2047 sur le master..spt_values WHERE type='P', ce qui fait cinq ans et demi de dates si vous en avez besoin!

2
AjV Jsy

Testé ci-dessous et cela fonctionne, même si c'est un peu compliqué.

J'ai attribué des valeurs arbitraires aux dates du test.

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
1
JNK

Pour SQL Server 2000, cette stackoverflow post semble prometteuse pour un moyen de générer temporairement des dates calculées à partir d’une date de début et d’une date de fin. Ce n'est pas exactement pareil mais assez similaire. Cet article a une réponse très détaillée sur les dates tronquées, si nécessaire.

Si quelqu'un tombe sur cette question et travaille dans PostgreSQL au lieu de SQL Server 2000, voici comment procéder:.

PostgreSQL a une fonction géniale série . Pour votre exemple, vous pouvez utiliser cette série de tous les jours au lieu de générer un tableau de calendrier complet, puis faire des regroupements et des confrontations à partir de là. 

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)

Je voudrais aussi regarder dans date_trunc de PostgreSQL en utilisant 'month' pour le champ troncateur afin de refactoriser éventuellement votre requête d'origine afin qu'elle corresponde facilement à une version date_trunc de la série calendaire.

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

Le code ci-dessus doit remplir la table avec toutes les dates comprises entre le début et la fin. Il vous suffirait alors de vous joindre à cette table pour obtenir toutes les dates nécessaires. Si vous n’avez besoin que d’un certain jour de chaque mois, vous pouvez vous connecter à un mois.

0
Brandon Barkley
select top (datediff(D,@start,@end)) dateadd(D,id-1,@start)
from BIG_TABLE_WITH_NO_JUMPS_IN_ID
0
user6619957

Créez une variable de table contenant une date pour chaque mois de l'année:

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
davidofmorris