2014-04-25 50 views
13

Come ottenere le date tra due date?Ottieni tutte le date tra due date in SQL Server

Ho una variabile @MAXDATE che sta memorizzando la data massima dalla tabella. Ora voglio ottenere tutte le date tra @Maxdate e GETDATE() e voglio memorizzare queste date in un cursore.

Finora ho fatto come segue:

;with GetDates As 
( 
select DATEADD(day,1,@maxDate) as TheDate 
UNION ALL 
select DATEADD(day,1, TheDate) from GetDates 
where TheDate < GETDATE() 
) 

Questo è perfettamente funzionante, ma quando sto cercando di memorizzare questi valori in un cursore

SET @DateCurSor=CURSOR FOR 
       SELECT TheDate 
       FROM GetDates 

Errore di compilazione

Sintassi errata vicino alla parola chiave 'SET'.

Come risolvere questo.

Grazie in anticipo

+2

*** PERCHE 'sulla terra *** vuoi un ** cursore **?!? Dovresti provare a ** evitare i cursori ** il più possibile! –

+1

La situazione è questa che devo usare CURSOR. – user3193557

+4

*** W H Y? ? ? *** Sono sicuro al 99% di non dover ** usare un cursore! E sarebbe meglio se tu non usassi un cursore! –

risposta

23

Il mio primo suggerimento sarebbe utilizzare il calendar table, se non ne avete uno, quindi creare uno. Sono molto utili. La query è poi così semplice come:

DECLARE @MinDate DATE = '20140101', 
     @MaxDate DATE = '20140106'; 

SELECT Date 
FROM dbo.Calendar 
WHERE Date >= @MinDate 
AND  Date < @MaxDate; 

Se non si vuole, o non può creare una tabella di calendario che si può ancora fare questo al volo senza una CTE ricorsiva:

DECLARE @MinDate DATE = '20140101', 
     @MaxDate DATE = '20140106'; 

SELECT TOP (DATEDIFF(DAY, @MinDate, @MaxDate) + 1) 
     Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @MinDate) 
FROM sys.all_objects a 
     CROSS JOIN sys.all_objects b; 

Per ulteriori approfondimenti su questo vedi:

Per quanto riguarda l'utilizzo di questa sequenza di date in un cursore, mi raccomando di trovare un altro modo. Di solito c'è un'alternativa basata sul set che funzionerà molto meglio.

Quindi, con i tuoi dati:

date | it_cd | qty 
24-04-14 | i-1 | 10 
26-04-14 | i-1 | 20 

per ottenere la quantità in 28-04-2014 (che mi sembra di capire è il vostro requisito), in realtà non è necessario alcun di quanto sopra, si può semplicemente utilizzare :

SELECT TOP 1 date, it_cd, qty 
FROM T 
WHERE it_cd = 'i-1' 
AND  Date <= '20140428' 
ORDER BY Date DESC; 

Se non si desidera per un particolare elemento:

SELECT date, it_cd, qty 
FROM ( SELECT date, 
        it_cd, 
        qty, 
        RowNumber = ROW_NUMBER() OVER(PARTITION BY ic_id 
                ORDER BY date DESC) 
      FROM T 
      WHERE Date <= '20140428' 
     ) T 
WHERE RowNumber = 1; 
+0

Non sono sicuro che il cross join sia necessario. Sembra essere irrilevante quando si esegue la query. – pimbrouwers

+1

@PimBrouwers La necessità del cross join è in quante righe sono richieste. Se sono richieste meno date del numero di oggetti in 'sys.all_objects', non è necessario, ma se l'intervallo di date dovesse estendersi su 20 anni, sarebbe necessario il cross join. L'uso di 'TOP (Giorni necessari) 'significa che c'è poco o nessun sovraccarico dal cross join quando sono necessarie meno righe, quindi non c'è nulla di male lasciandolo dentro. – GarethD

+0

Impressionante! Grazie per averlo spiegato. Molto apprezzato. Domanda fantastica in generale. – pimbrouwers

0
create procedure [dbo].[p_display_dates](@startdate datetime,@enddate datetime) 
as 
begin 
    declare @mxdate datetime 
    declare @indate datetime 
    create table #daterange (dater datetime) 
    insert into #daterange values (@startdate) 
    set @mxdate = (select MAX(dater) from #daterange) 
    while @mxdate < @enddate 
     begin 
      set @indate = dateadd(day,1,@mxdate) 
      insert into #daterange values (@indate) 
      set @mxdate = (select MAX(dater) from #daterange) 
     end 
    select * from #daterange 
end 
6

È possibile utilizzare questo script per trovare le date tra due date. Reference taken from this Article:

DECLARE @StartDateTime DATETIME 
DECLARE @EndDateTime DATETIME 

SET @StartDateTime = '2015-01-01' 
SET @EndDateTime = '2015-01-12'; 

WITH DateRange(DateData) AS 
(
    SELECT @StartDateTime as Date 
    UNION ALL 
    SELECT DATEADD(d,1,DateData) 
    FROM DateRange 
    WHERE DateData < @EndDateTime 
) 
SELECT DateData 
FROM DateRange 
OPTION (MAXRECURSION 0) 
GO 
0

creare facilmente una funzione di tabella valore che restituirà una tabella con tutte le date. date di ingresso come stringa È possibile personalizzare la data nel formato che piace '01/01/2017' o '01 -01-2017' in formati di stringa (103.126 ...)

Prova questa

CREATE FUNCTION [dbo].[DateRange_To_Table] (@minDate_Str NVARCHAR(30), @maxDate_Str NVARCHAR(30)) 

RETURNS @Result TABLE(DateString NVARCHAR(30) NOT NULL, DateNameString NVARCHAR(30) NOT NULL) 

AS 

begin 

    DECLARE @minDate DATETIME, @maxDate DATETIME 
    SET @minDate = CONVERT(Datetime, @minDate_Str,103) 
    SET @maxDate = CONVERT(Datetime, @maxDate_Str,103) 


    INSERT INTO @Result(DateString, DateNameString) 
    SELECT CONVERT(NVARCHAR(10),@minDate,103), CONVERT(NVARCHAR(30),DATENAME(dw,@minDate)) 



    WHILE @maxDate > @minDate 
    BEGIN 
     SET @minDate = (SELECT DATEADD(dd,1,@minDate)) 
     INSERT INTO @Result(DateString, DateNameString) 
     SELECT CONVERT(NVARCHAR(10),@minDate,103), CONVERT(NVARCHAR(30),DATENAME(dw,@minDate)) 
    END 




    return 

end 

Per eseguire la funzione fare ciò:

SELECT * FROM dbo.DateRange_To_Table ('01/01/2017','31/01/2017') 

l'uscita sarà

01/01/2017 Sunday 
02/01/2017 Monday 
03/01/2017 Tuesday 
04/01/2017 Wednesday 
05/01/2017 Thursday 
06/01/2017 Friday 
07/01/2017 Saturday 
08/01/2017 Sunday 
09/01/2017 Monday 
10/01/2017 Tuesday 
11/01/2017 Wednesday 
12/01/2017 Thursday 
13/01/2017 Friday 
14/01/2017 Saturday 
15/01/2017 Sunday 
16/01/2017 Monday 
17/01/2017 Tuesday 
18/01/2017 Wednesday 
19/01/2017 Thursday 
20/01/2017 Friday 
21/01/2017 Saturday 
22/01/2017 Sunday 
23/01/2017 Monday 
24/01/2017 Tuesday 
25/01/2017 Wednesday 
26/01/2017 Thursday 
27/01/2017 Friday 
28/01/2017 Saturday 
29/01/2017 Sunday 
30/01/2017 Monday 
31/01/2017 Tuesday 
0

solo dicendo ... ecco un approccio più semplice a questo:

declare @sdate date = '2017-06-25' 
    , @edate date = '2017-07-24' 

; with dates_CTE (date) as (
     select @sdate 
    Union ALL 
     select DATEADD(day, 1, date) 
     from dates_CTE 
     where date < @edate 
) select 
    * 
from dates_CTE 
0

ho elencato le date di 2 settimane più tardi. È possibile utilizzare @period variabile o funzione DateDiff (dd, @date_start, @date_end)

declare @period INT, @date_start datetime, @date_end datetime, @i int; 

set @period = 14 
set @date_start = convert(date,DATEADD(D, [email protected], curent_timestamp)) 
set @date_end = convert(date,current_timestamp) 
set @i = 1 

create table #datesList(dts datetime) 
insert into #datesList values (@date_start) 
while @i <= @period 
    Begin 
     insert into #datesList values (dateadd(d,@i,@date_start)) 
     set @i = @i + 1 
    end 
select cast(dts as DATE) from #datesList 
Drop Table #datesList 
0

Questo è il metodo che avrei usato.

DECLARE 
    @DateFrom DATETIME = GETDATE(), 
    @DateTo DATETIME = DATEADD(HOUR, -1, GETDATE() + 2); -- Add 2 days and minus one hour 


-- Dates spaced a day apart 

WITH MyDates (MyDate) 
AS (
    SELECT @DateFrom 
    UNION ALL 
    SELECT DATEADD(DAY, 1, MyDate) 
    FROM MyDates 
    WHERE MyDate < @DateTo 
    ) 

SELECT 
    MyDates.MyDate 
    , CONVERT(DATE, MyDates.MyDate) AS [MyDate in DATE format] 
FROM 
    MyDates; 

Ecco un esempio simile, ma questa volta le date sono distanziati l'uno ora a parte per ulteriore comprensione aiuto di come funziona la query:

-- Alternative example with dates spaced an hour apart 

WITH MyDates (MyDate) 
AS (SELECT @DateFrom 
    UNION ALL 
    SELECT DATEADD(HOUR, 1, MyDate) 
    FROM MyDates 
    WHERE MyDate < @DateTo 
    ) 

SELECT 
    MyDates.MyDate 
FROM 
    MyDates; 

Come si può vedere, la query è veloce, accurato e versatile.