none
Estrarre il primo successivo e l'ultimo tra i precedenti RRS feed

  • Domanda

  • Ciao a tutti,
    avrei bisogno di estrarre i due record più vicini temporalmente, precedente e successivo, di una tabella che ha un
    campo DataOra ed io vi entro appunto con una variabile DateTime.

    Ad esempio, se ho questi valori:


    ID - DataOra - GruppoID

    17 2012-06-20 10:00:00.000 463
    21 2012-06-22 12:00:00.000 463
    13 2012-06-10 10:00:00.000 463
    15 2012-06-02 10:00:00.000 463
    15 2012-06-08 10:00:00.000 463
    16 2012-06-26 10:00:00.000 463
    25 2012-07-01 10:00:00.000 463
    29 2012-06-30 15:00:00.000 463

    ed ho come parametro '2012-06-08 15:00:00.000'

    dovrei ottenere i record:


    15 2012-06-08 10:00:00.000 463 -- Il primo precedente
    13 2012-06-10 10:00:00.000 463 -- Il primo successivo


    Come è possibile fare una query del genere?

    Grazie

    Luigi

    PS

    Nel caso, poi, abbia una data che è superiore a tutte le altre già inserite, troverei solo il Previous, e non il Next, ottenendo tutti i dati su di una stessa riga, del tipo:

    PrevID PrevDate NextID NextDate

    25 2012-07-01 10:00:00 NULL NULL

    • Modificato Ciupaz venerdì 27 luglio 2012 14:01
    venerdì 27 luglio 2012 12:38

Risposte

  • salve,

    se ho compreso, personalmente proverei valutando il delta tra il parametro data utilizzato e il valore dell'attributo DataOra... possiamo quindi, in base al parametro, avere il massimo dei valori negativi per avere il [Precedente] ed il minimo dei positivi per il [Successivo]

    conoscendo questi delta, possiamo poi recuperare il relativo ID, ed avendo questo, ovviamente ottenere gli attributi ulteriori laddove richiesti...

    trivialmente:

    SET NOCOUNT ON;
    USE tempdb;
    GO
    CREATE TABLE dbo.t (
    	ID int,
    	DataOra datetime,
    	GruppoID int
    	);
    GO
    INSERT INTO dbo.t
    	VALUES (17,  '2012-06-20 10:00:00.000', 463), 
    		(21, '2012-06-22 12:00:00.000', 463),
    		(13, '2012-06-10 10:00:00.000', 463),
    		(14, '2012-06-02 10:00:00.000', 463),
    		(15, '2012-06-08 10:00:00.000', 463),
    		(16, '2012-06-26 10:00:00.000', 463),
    		(25, '2012-07-01 10:00:00.000', 463),
    		(29, '2012-06-30 15:00:00.000', 463);
    GO
    DECLARE @p datetime = '2012-06-08 15:00:00.000';
    WITH cte AS ( 
    	SELECT t.ID, DATEDIFF(SECOND, @p, t.DataOra) AS [delta]
    		FROM dbo.t t
    	),
    	cte2 AS (
    		SELECT MAX(CASE WHEN c.delta < 0 THEN delta ELSE NULL END) AS [Down]
    			, MIN(CASE WHEN c.delta >= 0 THEN delta ELSE NULL END) AS [Up]
    			FROM cte c
    	),
    	cteData AS (
    		SELECT cMin.ID AS [MinId], cMax.ID AS [MaxId]
    			FROM cte2 c
    				LEFT JOIN cte cMin ON c.Down = cMin.delta
    				LEFT JOIN cte cMax ON c.Up = cMax.delta
    	)
    	SELECT cteData.MinId AS [PrevId], t1.DataOra AS [PrevDate],
    		cteData.MaxId AS [NextID], t2.DataOra AS [NextDate]
    		FROM cteData
    			LEFT JOIN dbo.t t1 ON t1.ID = cteData.MinId
    			LEFT JOIN dbo.t t2 ON t2.ID = cteData.MaxId;
    GO
    DROP TABLE dbo.t;

    in questo caso ho utilizzato una "differenza" in minuti secondi, che consente un'ampia finestra temporale... avrei preferito utilizzare almeno i millisecondi in quanto non hai espresso in che scenario di differenza temporale ti stai muovendo, ma questo consente una finestra temporale piuttosto limitata..

    quindi, la prima common table expression (cte) ottiene il delta positivo o negativo, la seconda (cte2) ottiene il delta relativo a PrevDate e NextDate, la terza (cteData) restituisce gli Id di PrevDate e NextDate, e quindi la proiezione finale estrate dalla join con la tabella base gli attributi completi... il tutto in un'unica istruzione articolata...

    saluti


    http://www.asql.biz - DbaMgr2k - DbaMgr and further SQL Tools http://www.hotelsole.com/

    • Contrassegnato come risposta Ciupaz mercoledì 1 agosto 2012 19:10
    mercoledì 1 agosto 2012 00:20
    Moderatore

Tutte le risposte

  • Non è chiaro cosa succede nel caso ci siano 2 valori uguali
    come primo precedente o come primo successivo

    ____________________________________________________

    Poi noto che nell'esempio hai messo nel campo chiamato "ID"
    2 volte il numero 15

    Evidentemente quell'ID non è l'identificativo

    della tabella

    ________________________________________________________________

    Non ho capito le tue ultime 5 righe:

    Sembra che invece dei "classici" 3 campi ne vorresti 4 ?

    _____________________________________________________

    Prova questo script che si dovrebbe essere quasi giusto

    --   Uso master
    USE master
    GO
    --________________________________________________________________________________
    -- Creazione di un nuovo DB
    CREATE DATABASE Pppp 
    GO
    -- _______________________________________________________________________________
    --uso Pppp
    USE Pppp
    GO
    -- ______________________________________________________________________________
    --  Creo Tabella Tab
    CREATE TABLE [dbo].[Tab]
    (
    	[ID] [int] NULL,
    	[DaOr] [datetime2](7) NULL,
    	[GrID] [int] NULL
    ) ON [PRIMARY]
    GO
    -- ____________________________________________________________________________
    --  Inserisco un po di dati
    INSERT 
    [dbo].[Tab] 
    ([ID], [DaOr], [GrID]) 
    VALUES 
    (17, CAST(0x070046C323009F350B AS DateTime2), 463),
    (21, CAST(0x0700E0349564BF350B AS DateTime2), 463),
    (13, CAST(0x070000000000C1350B AS DateTime2), 463),
    (15, CAST(0x0700000000002D350B AS DateTime2), 463),
    (15, CAST(0x07000000000053350B AS DateTime2), 4630),
    (16, CAST(0x070000000000D6350B AS DateTime2), 463),
    (25, CAST(0x07000000000046350B AS DateTime2), 463),
    (29, CAST(0x0700000000009F350B AS DateTime2), 4630);
    GO
    -- _______________________________________________________________________________
    --   Creo vista  BB01
    --  Questa non serve   la metto solo per comprensione della BB10
    CREATE VIEW [dbo].[BB01]
    AS
    SELECT     
    ID, 
    DaOr, 
    GrID, 
    CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime) AS Diff
    FROM         
    dbo.Tab
    GO
    -- _____________________________________________________________________________
    --   Creo vista  BB02
    --  Questa non serve   la metto solo per comprensione della BB10
    CREATE VIEW [dbo].[BB02]
    AS
    SELECT     
    MIN(CASE WHEN (CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime)) > 0 THEN (CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime)) ELSE NULL END) AS Maggx, 
    MAX(CASE WHEN (CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime)) < 0 THEN (CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime)) ELSE NULL END) AS Minox
    FROM 
    dbo.Tab
    GO
    -- _______________________________________________________________________________
    --   Creo vista  BB03
    --  Questa non serve   la metto solo per comprensione della BB10
    --  è originata da BB01  e  BB02
    CREATE VIEW [dbo].[BB03]
    AS
    SELECT     
    dbo.BB01.ID, 
    dbo.BB01.DaOr, 
    dbo.BB01.GrID
    FROM         
    dbo.BB01 
    LEFT OUTER JOIN
    dbo.BB02 AS BB02Ma 
    ON 
    dbo.BB01.Diff = BB02Ma.Maggx 
    LEFT OUTER JOIN
    dbo.BB02 AS BB02Mi 
    ON 
    dbo.BB01.Diff = BB02Mi.Minox
    WHERE     
    (NOT (BB02Ma.Maggx IS NULL)) 
    OR
    (NOT (BB02Mi.Minox IS NULL))
    GO
    -- _________________________________________________________________________________
    --   Creo vista  BB10
    --  Questa è l'unica Vista che serve
    --  è nidifica al suo interno   BB01   BB02   BB03
    CREATE VIEW [dbo].[BB10]
    AS
    SELECT     
    BB01.ID, 
    BB01.DaOr, 
    BB01.GrID
    FROM 
    (
    SELECT     
    ID, 
    DaOr, 
    GrID, 
    CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime) AS Diff
    FROM         
    dbo.Tab
    ) AS BB01 
    LEFT OUTER JOIN
    (
    SELECT     
    MIN(CASE WHEN (CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime)) > 0 THEN (CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime)) ELSE NULL END) AS Maggx, 
    MAX(CASE WHEN (CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime)) < 0 THEN (CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime)) ELSE NULL END) AS Minox
    FROM 
    dbo.Tab
    ) AS BB02Ma 
    ON 
    BB01.Diff = BB02Ma.Maggx 
    LEFT OUTER JOIN
    (
    SELECT     
    MIN(CASE WHEN (CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime)) > 0 THEN (CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime)) ELSE NULL END) AS Maggx, 
    MAX(CASE WHEN (CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime)) < 0 THEN (CAST(DaOr - CAST('03/04/2012 12:05:05' AS datetime) AS datetime)) ELSE NULL END) AS Minox
    FROM 
    dbo.Tab
    ) AS BB02Mi 
    ON 
    BB01.Diff = BB02Mi.Minox
    WHERE     
    (NOT (BB02Ma.Maggx IS NULL)) 
    OR
    (NOT (BB02Mi.Minox IS NULL))
    GO
    --________________________________________________________________________________________
    --________________________________________________________________________________________
    --_____________________________________________________________________________________


    Oppure prova direttamente questa:

    -- _________________________________________
    USE Pppp;
    GO
    -- _________________________________________________________________________________
    DECLARE @ParDat datetime
    SET @ParDat = '2012-04-03 12:05:05'
    SELECT     
    BB01.ID, 
    BB01.DaOr, 
    BB01.GrID
    FROM 
    (
    SELECT     
    ID, 
    DaOr, 
    GrID, 
    CAST(DaOr - CAST(@ParDat AS datetime) AS datetime) AS Diff
    FROM         
    dbo.Tab
    ) AS BB01 
    LEFT OUTER JOIN
    (
    SELECT     
    MIN(CASE WHEN (CAST(DaOr - CAST(@ParDat AS datetime) AS datetime)) > 0 THEN (CAST(DaOr - CAST(@ParDat AS datetime) AS datetime)) ELSE NULL END) AS Maggx, 
    MAX(CASE WHEN (CAST(DaOr - CAST(@ParDat AS datetime) AS datetime)) < 0 THEN (CAST(DaOr - CAST(@ParDat AS datetime) AS datetime)) ELSE NULL END) AS Minox
    FROM 
    dbo.Tab
    ) AS BB02Ma 
    ON 
    BB01.Diff = BB02Ma.Maggx 
    LEFT OUTER JOIN
    (
    SELECT     
    MIN(CASE WHEN (CAST(DaOr - CAST(@ParDat AS datetime) AS datetime)) > 0 THEN (CAST(DaOr - CAST(@ParDat AS datetime) AS datetime)) ELSE NULL END) AS Maggx, 
    MAX(CASE WHEN (CAST(DaOr - CAST(@ParDat AS datetime) AS datetime)) < 0 THEN (CAST(DaOr - CAST(@ParDat AS datetime) AS datetime)) ELSE NULL END) AS Minox
    FROM 
    dbo.Tab
    ) AS BB02Mi 
    ON 
    BB01.Diff = BB02Mi.Minox
    WHERE     
    (NOT (BB02Ma.Maggx IS NULL)) 
    OR
    (NOT (BB02Mi.Minox IS NULL))
    GO
    --__________________________________


    Facci sapere



    • Modificato Mancini, venerdì 27 luglio 2012 22:49
    venerdì 27 luglio 2012 22:11
  • Ho provato con la seconda versione e direi che potrebbe funzionare.
    In teoria non possono esserci 2 valori uguali.
    Nei dati di esempio che ho postato, effettivamente, ci sono due 15, il che non è corretto (problema di Copia&Incolla).

    C'è solo un piccolo problema.
    Nel caso provi con una data antecedente o successiva a tutte, mi viene restituito un unico record, che a prima
    vista non mi permette di sapere se si tratta dell'ultimo precedente o del primo successivo.

    Luigi

    lunedì 30 luglio 2012 08:54
  • C'è solo un piccolo problema.
    Nel caso provi con una data antecedente o successiva a tutte, mi viene restituito un unico record, che a prima
    vista non mi permette di sapere se si tratta dell'ultimo precedente o del primo successivo.

    Io direi di:

    Aggiungere un campo con una
    __ "p" per ultimo precedente
    __ "s" per primo successivo

    Questo lo avrai anche nel caso in cui hai i 2 record ( precedente e successivo )

    Fammi sapere,

    Questa sera potro  riguardare la procedura sul server di prova.


    • Modificato Mancini, lunedì 30 luglio 2012 11:06
    lunedì 30 luglio 2012 11:05
  • Mi dici che "in teoria" Non ci possono essere 2 valori uguali,
    Comunque se ci fossero vengono prelevati entrambi

    ______________________________________

    Invece potrebbe capitare di avere il parametro uguale a un valore della tabella ?
    In questo caso quel valore verrebbe scartato  < -------------

    In alternativa è un gioco di = oppure di >=   oppure  <=

    ___________________________________________________

    Ma devi essere te a decidere se prendere
    il valore uguale + il maggiore
    oppure
    il valore uguale + il minore
    oppure
    il valore uguale + il maggiore + il minore

    ________________________________________________

    No,   non ho fatto la "p" e la "s"

    Ho messo un numero positivo o negativo che rappresenta la differenza
    in giorni e decimali di giornata

    fra il tuo parametro e la data considerata

    --   Uso master
    USE master
    GO
    --________________________________________________________________________________
    -- Creazione di un nuovo DB
    CREATE DATABASE Pppp 
    GO
    -- _______________________________________________________________________________
    --uso Pppp
    USE Pppp
    GO
    -- ______________________________________________________________________________
    --  Creo Tabella Tab
    CREATE TABLE [dbo].[Tab]
    (
    	[ID] [int] NULL,
    	[DaOr] [datetime2](7) NULL,
    	[GrID] [int] NULL
    ) ON [PRIMARY]
    GO
    -- ____________________________________________________________________________
    --  Inserisco un po di dati
    INSERT 
    [dbo].[Tab] 
    ([ID], [DaOr], [GrID]) 
    VALUES 
    (17, CAST(0x070046C323009F350B AS DateTime2), 463),
    (21, CAST(0x0700E0349564BF350B AS DateTime2), 463),
    (13, CAST(0x070000000000C1350B AS DateTime2), 463),
    (92, CAST(0x0700000000002D350B AS DateTime2), 463),
    (15, CAST(0x07000000000053350B AS DateTime2), 4630),
    (16, CAST(0x070000000000D6350B AS DateTime2), 463),
    (25, CAST(0x07000000000046350B AS DateTime2), 463),
    (29, CAST(0x0700000000009F350B AS DateTime2), 4630);
    GO
    -- _______________________________________________________________________________
    --   Creo vista  CC01
    --  Questa non ti serve   la metto solo per comprensione della prossima CC10
    CREATE VIEW [dbo].[CC01]
    AS
    SELECT     
    ID, DaOr, 
    GrID, 
    CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money) AS Diff
    FROM         
    dbo.Tab
    GO
    -- _____________________________________________________________________________
    --   Creo vista  CC02
    --  Questa non ti serve   la metto solo per comprensione della prossima CC10
    CREATE VIEW [dbo].[CC02]
    AS
    SELECT     
    MIN(CASE WHEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money)) > 0 THEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money)) END) AS Magx, 
    MAX(CASE WHEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money)) < 0 THEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money)) END) AS Minx
    FROM         
    dbo.Tab
    GO
    -- _______________________________________________________________________________
    --   Creo vista  CC03
    --  Questa non ti serve   la metto solo per comprensione della prossima CC10
    --  le sue fonti di origine sono CC01  e  CC02
    CREATE VIEW [dbo].[CC03]
    AS
    SELECT     
    dbo.CC01.ID, 
    dbo.CC01.DaOr, 
    dbo.CC01.GrID, 
    dbo.CC01.Diff
    FROM         
    dbo.CC01 
    LEFT OUTER JOIN
    dbo.CC02 AS CC02Ma 
    ON dbo.CC01.Diff = CC02Ma.Magx 
    LEFT OUTER JOIN
    dbo.CC02 AS CC02Mi 
    ON 
    dbo.CC01.Diff = CC02Mi.Minx
    WHERE     
    (NOT (CC02Ma.Magx IS NULL)) 
    OR
    (NOT (CC02Mi.Minx IS NULL))
    GO
    -- _________________________________________________________________________________
    --   Creo vista  CC10
    --  Questa è l'unica Vista che serve
    --  è nidifica al suo interno   CC01   CC02   CC03
    CREATE VIEW [dbo].[CC10]
    AS
    SELECT     
    CC01.ID, 
    CC01.DaOr, 
    CC01.GrID, 
    CC01.Diff
    FROM         
    (
    SELECT     
    ID, DaOr, 
    GrID, 
    CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money) AS Diff
    FROM         
    dbo.Tab
    ) AS CC01 
    LEFT OUTER JOIN
    (
    SELECT     
    MIN(CASE WHEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money)) > 0 THEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money)) END) AS Magx, 
    MAX(CASE WHEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money)) < 0 THEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money)) END) AS Minx
    FROM         
    dbo.Tab
    ) AS CC02Ma 
    ON 
    CC01.Diff = CC02Ma.Magx 
    LEFT OUTER JOIN
    (
    SELECT     
    MIN(CASE WHEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money)) > 0 THEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money)) END) AS Magx, 
    MAX(CASE WHEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money)) < 0 THEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST('03/04/2012 12:05:05' AS datetime) AS money)) END) AS Minx
    FROM         
    dbo.Tab
    ) AS CC02Mi 
    ON 
    CC01.Diff = CC02Mi.Minx
    WHERE     
    (NOT (CC02Ma.Magx IS NULL)) 
    OR
    (NOT (CC02Mi.Minx IS NULL))
    GO
    --________________________________________________________________________________________
    -- _____________

    oppure se preferisci cosi:

    -- _________________________________________
    USE Pppp;
    GO
    -- _________________________________________________________________________________
    DECLARE @ParDat datetime
    SET @ParDat = '2012-03-04 12:05:05'
    SELECT     
    CC01.ID, 
    CC01.DaOr, 
    CC01.GrID, 
    CC01.Diff
    FROM         
    (
    SELECT     
    ID, DaOr, 
    GrID, 
    CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST(@ParDat AS datetime) AS money) AS Diff
    FROM         
    dbo.Tab
    ) AS CC01 
    LEFT OUTER JOIN
    (
    SELECT     
    MIN(CASE WHEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST(@ParDat AS datetime) AS money)) > 0 THEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST(@ParDat AS datetime) AS money)) END) AS Magx, 
    MAX(CASE WHEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST(@ParDat AS datetime) AS money)) < 0 THEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST(@ParDat AS datetime) AS money)) END) AS Minx
    FROM         
    dbo.Tab
    ) AS CC02Ma 
    ON 
    CC01.Diff = CC02Ma.Magx 
    LEFT OUTER JOIN
    (
    SELECT     
    MIN(CASE WHEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST(@ParDat AS datetime) AS money)) > 0 THEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST(@ParDat AS datetime) AS money)) END) AS Magx, 
    MAX(CASE WHEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST(@ParDat AS datetime) AS money)) < 0 THEN (CAST(CAST(DaOr AS datetime) AS money) - CAST(CAST(@ParDat AS datetime) AS money)) END) AS Minx
    FROM         
    dbo.Tab
    ) AS CC02Mi 
    ON 
    CC01.Diff = CC02Mi.Minx
    WHERE     
    (NOT (CC02Ma.Magx IS NULL)) 
    OR
    (NOT (CC02Mi.Minx IS NULL))
    GO
    -- __________________________________________________________
    -- ________________

    Facci sapere

    • Modificato Mancini, lunedì 30 luglio 2012 21:14
    lunedì 30 luglio 2012 20:44
  • mmm prova con questa.

    L'idea è di considerare ogni riferimento con una query a se stante utilizzando il "TOP 1 + ORDER BY". In questo modo prendi il precedente e il successivo. Quello corrente penso non ci sia bisogno di spiegarlo, no :-)

    Ok, mi pare fin troppo banale... mi sono perso qualcosa?

    -- quella subito dopo
    select top 1 'SUCCESSIVO' as Rel, id, dataora, gruppoid from table where dataora > @input order by dataora
    union 
    -- quella che mi chiedi
    select 'RICHIESTA' as Rel, id, dataora, gruppoid from table where dataora = @input
    union 
    -- quella subito prima
    select top 1 'PRECEDENTE' as Rel ,id, dataora, gruppoid from table where dataora < @input order by dataora desc

    Let us know,

    Ciao!


    Adriano


    martedì 31 luglio 2012 07:31
  • L'ideale, Adriano, sarebbe quello di avere un unico record (una sola riga) con i valori - se presenti - precedente e successivo:

    PrevID PrevDate NextID NextDate

    o anche aggiungendo il valore che passo

    PrevID PrevDate NextID NextDate CurrID CurrDate

    considerando però i casi in cui manchi il valore successivo (se passo una data più recente tra tutte quelle presenti) o il valore subito precedente (se passo una data più antica di tutte).

    Luigi

    PS

    Per Renarig:

    Provo la tua soluzione, anche se mi sembra piuttosto complessa (speravo di qualcosa di più sintetico, ma va bene lo stesso)

    martedì 31 luglio 2012 07:41
  • Certo, si tratta di denormalizzare.

    guarda ho proprio provato ora su un mio database una soluzione simile. 

    declare @input datetime 
    set @input = '2008-10-16 15:32:57.263'
    
    select 
    	Orig.DocumentDate as CurrDate,
    	Orig.DocumentID as CurrID,
    	Succ.DocumentDate as NextDate,
    	Succ.DocumentID as NextID,
    	Prec.DocumentDate as PrevDate,
    	Prec.DocumentID as PrevID
    from 
    	(select @input as CurrDate) Input
    	left join (select * from Documents where DocumentDate = @input) Orig on (Input.CurrDate = @Input)
    	left join (select top 1 * from Documents where DocumentDate > @input order by DocumentDate) Succ on (Input.CurrDate = @Input)
    	left join (select top 1 * from Documents where DocumentDate < @input order by DocumentDate desc) Prec on (Input.CurrDate = @Input)
    	
    
    	
    

    ... e funziona.

    Non ho tempo per farlo sulla tua tabella, ma si tratta di sostituire alcuni nomi di campo... 

    HTH!

    Ciao!


    Adriano

    martedì 31 luglio 2012 07:52
  • Grazie Adriano, provo a calare la tua soluzione per il mio caso.

    Luigi

    martedì 31 luglio 2012 07:55
  • salve,

    se ho compreso, personalmente proverei valutando il delta tra il parametro data utilizzato e il valore dell'attributo DataOra... possiamo quindi, in base al parametro, avere il massimo dei valori negativi per avere il [Precedente] ed il minimo dei positivi per il [Successivo]

    conoscendo questi delta, possiamo poi recuperare il relativo ID, ed avendo questo, ovviamente ottenere gli attributi ulteriori laddove richiesti...

    trivialmente:

    SET NOCOUNT ON;
    USE tempdb;
    GO
    CREATE TABLE dbo.t (
    	ID int,
    	DataOra datetime,
    	GruppoID int
    	);
    GO
    INSERT INTO dbo.t
    	VALUES (17,  '2012-06-20 10:00:00.000', 463), 
    		(21, '2012-06-22 12:00:00.000', 463),
    		(13, '2012-06-10 10:00:00.000', 463),
    		(14, '2012-06-02 10:00:00.000', 463),
    		(15, '2012-06-08 10:00:00.000', 463),
    		(16, '2012-06-26 10:00:00.000', 463),
    		(25, '2012-07-01 10:00:00.000', 463),
    		(29, '2012-06-30 15:00:00.000', 463);
    GO
    DECLARE @p datetime = '2012-06-08 15:00:00.000';
    WITH cte AS ( 
    	SELECT t.ID, DATEDIFF(SECOND, @p, t.DataOra) AS [delta]
    		FROM dbo.t t
    	),
    	cte2 AS (
    		SELECT MAX(CASE WHEN c.delta < 0 THEN delta ELSE NULL END) AS [Down]
    			, MIN(CASE WHEN c.delta >= 0 THEN delta ELSE NULL END) AS [Up]
    			FROM cte c
    	),
    	cteData AS (
    		SELECT cMin.ID AS [MinId], cMax.ID AS [MaxId]
    			FROM cte2 c
    				LEFT JOIN cte cMin ON c.Down = cMin.delta
    				LEFT JOIN cte cMax ON c.Up = cMax.delta
    	)
    	SELECT cteData.MinId AS [PrevId], t1.DataOra AS [PrevDate],
    		cteData.MaxId AS [NextID], t2.DataOra AS [NextDate]
    		FROM cteData
    			LEFT JOIN dbo.t t1 ON t1.ID = cteData.MinId
    			LEFT JOIN dbo.t t2 ON t2.ID = cteData.MaxId;
    GO
    DROP TABLE dbo.t;

    in questo caso ho utilizzato una "differenza" in minuti secondi, che consente un'ampia finestra temporale... avrei preferito utilizzare almeno i millisecondi in quanto non hai espresso in che scenario di differenza temporale ti stai muovendo, ma questo consente una finestra temporale piuttosto limitata..

    quindi, la prima common table expression (cte) ottiene il delta positivo o negativo, la seconda (cte2) ottiene il delta relativo a PrevDate e NextDate, la terza (cteData) restituisce gli Id di PrevDate e NextDate, e quindi la proiezione finale estrate dalla join con la tabella base gli attributi completi... il tutto in un'unica istruzione articolata...

    saluti


    http://www.asql.biz - DbaMgr2k - DbaMgr and further SQL Tools http://www.hotelsole.com/

    • Contrassegnato come risposta Ciupaz mercoledì 1 agosto 2012 19:10
    mercoledì 1 agosto 2012 00:20
    Moderatore
  • Direi che la tua soluzione funziona perfettamente. Grazie Andrea.

    L

    mercoledì 1 agosto 2012 19:10