2014-05-22 15 views
6

Ho due tabelle in MS SQL Server:Qual è il modo più efficace per abbinare i valori tra 2 tabelle in base alla data precedente più recente?

dailyt - che contiene dati giornalieri:

date    val 
--------------------- 
2014-05-22  10 
2014-05-21  9.5 
2014-05-20  9 
2014-05-19  8 
2014-05-18  7.5 
etc... 

E periodt - che contiene i dati arrivano a periodi irregolari:

date    val 
--------------------- 
2014-05-21  2 
2014-05-18  1 

Dato una riga in dailyt, voglio regolare il suo valore aggiungendo il valore corrispondente in periodt con la data più vicina precedente o uguale alla data dello dailyt riga. Così, l'output sarà simile:

addt

date    val 
--------------------- 
2014-05-22  12  <- add 2 from 2014-05-21 
2014-05-21  11.5 <- add 2 from 2014-05-21 
2014-05-20  10  <- add 1 from 2014-05-18 
2014-05-19  9  <- add 1 from 2014-05-18 
2014-05-18  8.5  <- add 1 from 2014-05-18 

So che un modo per farlo è quello di unire le tabelle dailyt e periodt su periodt.date <= dailyt.date e poi imponendo una condizione ROW_NUMBER() (PARTITION BY dailyt.date ORDER BY periodt.date DESC), e poi avere un WHERE condizione sul numero di riga a = 1.

C'è un altro modo per farlo che sarebbe più efficiente? O è abbastanza ottimista?

risposta

4

Penso usando APPLY sarebbe il modo più efficace:

SELECT d.Val, 
     p.Val, 
     NewVal = d.Val + ISNULL(p.Val, 0) 
FROM Dailyt AS d 
     OUTER APPLY 
     ( SELECT TOP 1 Val 
      FROM Periodt p 
      WHERE p.Date <= d.Date 
      ORDER BY p.Date DESC 
     ) AS p; 

Example on SQL Fiddle

0

Se relativamente pochi periodt righe, poi c'è un'opzione che può rivelarsi abbastanza efficiente.

Convertire periodt in una tabella di intervalli Da/A utilizzando sottoquery o CTE. (Ovviamente le prestazioni dipendono dall'efficienza con cui questo passaggio iniziale può essere eseguito, motivo per cui è preferibile un numero limitato di righe periodt.) Quindi il join su dailyt sarà estremamente efficiente. Per esempio.

;WITH PIds AS (
     SELECT ROW_NUMBER() OVER(ORDER BY PDate) RN, * 
     FROM @periodt 
    ), 
    PRange AS (
     SELECT f.PDate AS FromDate, t.PDate as ToDate, f.PVal 
     FROM PIds f 
       LEFT OUTER JOIN PIds t ON 
        t.RN = f.RN + 1 
    ) 
SELECT d.*, p.PVal 
FROM @dailyt d 
     LEFT OUTER JOIN PRange p ON 
      d.DDate >= p.FromDate 
     AND (d.DDate < p.ToDate OR p.ToDate IS NULL) 
ORDER BY 1 DESC 

Se volete provare la query, il seguente produce i dati di esempio utilizzando le variabili di tabella. Nota Ho aggiunto una riga in più a dailyt per dimostrare nessuna voce periodt con una data più piccola.

DECLARE @dailyt table (
    DDate date NOT NULL, 
    DVal float NOT NULL 
) 

INSERT INTO @dailyt(DDate, DVal) 
      SELECT '20140522', 10 
UNION ALL SELECT '20140521', 9.5 
UNION ALL SELECT '20140520', 9 
UNION ALL SELECT '20140519', 8 
UNION ALL SELECT '20140518', 7.5 
UNION ALL SELECT '20140517', 6.5 

DECLARE @periodt table (
    PDate date NOT NULL, 
    PVal int NOT NULL 
) 

INSERT INTO @periodt 
      SELECT '20140521', 2 
UNION ALL SELECT '20140518', 1 
Problemi correlati