2015-07-24 19 views
9

Al momento ho queste tabelle:SQL Partecipa alla data più vicina a disposizione

CREATE TABLE #SECURITY_TEMP (ID CHAR(30)) 
CREATE TABLE #SECURITY_TEMP_PRICE_HISTORY (ID CHAR(30), PRICEDATE DATE, PRICE FLOAT) 
CREATE TABLE #SECURITY_POST (ID CHAR(30), SECPOS int) 

INSERT INTO #SECURITY_TEMP (ID) VALUES ('APPL') ,('VOD'),('VOW3'), ('AAA') 
INSERT INTO #SECURITY_TEMP_PRICE_HISTORY (ID,PRICEDATE, PRICE) VALUES 
('APPL', '20150101',10.4), ('APPL', '20150116',15.4), ('APPL', '20150124',22.4), 
('VOD', '20150101', 30.5), ('VOD', '20150116',16.5), ('VOD', '20150124',16.5), 
('VOW3', '20150101', 45.5), ('VOW3', '20150116',48.8) ,('VOW3', '20150124',50.55), 
('AAA', '20100118', 0.002) 

INSERT INTO #SECURITY_POST (ID,SECPOS) VALUES ('APPL', 100), ('VOD', 350), ('VOW3', 400) 

voglio avere un tavolo pulito che mi mostra l'ID di sicurezza, la posizione di sicurezza e l'ultimo prezzo disponibile per la sicurezza, quando una data è passato.

Ora, quando faccio la seguente:

SELECT sec.ID, sec.SECPOS, t.PRICE 
FROM #SECURITY_POST as SEC INNER JOIN #SECURITY_TEMP_PRICE_HISTORY as t 
ON sec.ID = t.ID 
WHERE t.PriceDate = '20150101' 
GROUP BY sec.ID, secPos, t.price 

ottengo il risultato corretto

1. ID SECPOS PRICE 
2. APPL 100 10.4 
3. VOD 350 30.5 
4. VOW3 400 45.5 

Tuttavia, ci possono essere circostanze in cui i singoli, il prezzo di un titolo non è disponibile. In questo senso, voglio quindi essere in grado di ottenere il prezzo più recente disponibile.

Facendo

SELECT sec.ID, sec.SECPOS, t.PRICE 
FROM #SECURITY_POST as SEC INNER JOIN 
    #SECURITY_TEMP_PRICE_HISTORY as t 
    ON sec.ID = t.ID 
WHERE t.PriceDate = '20150117' 
GROUP BY sec.ID, secPos, t.price 

Restituisce 0 file a causa della non ci sono dati, e facendo

SELECT sec.ID, sec.SECPOS, t.PRICE 
FROM #SECURITY_POST as SEC INNER JOIN 
    #SECURITY_TEMP_PRICE_HISTORY as t 
    ON sec.ID = t.ID 
WHERE t.PriceDate <= '20150117' 
GROUP BY sec.ID, sec.secPos, t.price 
HAVING sec.secpos <> 0 

Restituisce le righe duplicate.

Ho provato un sacco di diverse metodologie e non riesco proprio a ottenere l'output desiderato. Inoltre, vorrei anche essere in grado di ottenere una colonna con il prezzo più vicino a una data (chiamatelo START_DATE) e una colonna con il prezzo più vicino a una seconda data (chiamatelo END_DATE) e una colonna che sarà la posizione [email protected]_DATE - [email protected]_DATE . Il prezzo è sempre preso dallo stesso #SECURITY_TEMP_PRICE_HISTORY.

Tuttavia, la mia conoscenza di SQL è semplicemente imbarazzante e non sono riuscito a trovare un modo efficace per farlo. Qualsiasi aiuto sarebbe apprezzato. Si noti inoltre che la tabella #SECURITY_PRICE_HISTORY table may contain more securities than the #SECURITY_POST.

+1

Grazie a tutti per aver pulito il mio post! – buellboy

risposta

12

Questo dovrebbe fare il trucco. OUTER APPLY è un operatore di join che (come CROSS APPLY) consente a una tabella derivata di avere un riferimento esterno.

SELECT 
    s.ID, 
    s.SecPos, 
    t.Price 
    t.PriceDate 
FROM 
    #SECURITY_POST s 
    OUTER APPLY (
     SELECT TOP 1 * 
     FROM #SECURITY_TEMP_PRICE_HISTORY t 
     WHERE 
     s.ID = t.ID 
     AND t.PriceDate <= '20150117' 
     ORDER BY t.PriceDate DESC 
    ) t 
; 

Si può anche prendere in considerazione i prezzi della segnalazione di sicurezza che sono molto vecchi, o limitare la ricerca per il più recente di sicurezza per un certo periodo (una settimana o un mese o qualcosa del genere).

Assicurarsi che la tabella della cronologia dei prezzi abbia un indice con (ID, PriceDate) in modo che le ricerche di sottoquery possano utilizzare la ricerca di intervalli e le prestazioni possono essere buone. Assicurati di fare un calcolo matematico delle date sulla tabella di sicurezza, non sulla tabella della cronologia, o forzerai la subquery di ricerca dei prezzi a non essere sganciabile, il che sarebbe negativo per le prestazioni poiché la ricerca di intervalli non sarebbe possibile.

Se non viene rilevato alcun prezzo per il titolo, OUTER APPLY continuerà a consentire l'esistenza della riga, pertanto il prezzo verrà visualizzato come NULL. Se si desidera che i titoli non vengano visualizzati quando non viene trovato un prezzo appropriato, utilizzare CROSS APPLY.

Per la vostra seconda parte della domanda, si può fare questo con due OUTER APPLY operazioni, in questo modo:

DECLARE 
    @StartDate date = '20150101', 
    @EndDate date = '20150118'; 

SELECT 
    S.ID, 
    S.SecPos, 
    StartDate = B.PriceDate, 
    StartPrice = B.Price, 
    EndDate = E.PriceDate, 
    EndPrice = E.Price, 
    Position = B.Price - E.Price 
FROM 
    #SECURITY_POST S 
    OUTER APPLY (
     SELECT TOP 1 * 
     FROM #SECURITY_TEMP_PRICE_HISTORY B 
     WHERE 
     S.ID = B.ID 
     AND B.PriceDate <= @StartDate 
     ORDER BY B.PriceDate DESC 
    ) B 
    OUTER APPLY (
     SELECT TOP 1 * 
     FROM #SECURITY_TEMP_PRICE_HISTORY E 
     WHERE 
     S.ID = E.ID 
     AND E.PriceDate <= @EndDate 
     ORDER BY E.PriceDate DESC 
    ) E 
; 

con i tuoi dati questo produce il set di risultati seguente:

ID SecPos StartDate StartPrice EndDate  EndPrice Position 
---- ------ ---------- ---------- ---------- -------- -------- 
APPL 100  2015-01-01 10.4  2015-01-16 15.4  -5 
VOD 350  2015-01-01 30.5  2015-01-16 16.5  14 
VOW3 400  2015-01-01 45.5  2015-01-16 48.8  -3.3 

scorso, anche se non tutti sono d'accordo, ti incoraggio a nominare le tue colonne ID con il nome della tabella come in SecurityID anziché ID. Nella mia esperienza l'uso di ID porta solo a problemi.

Nota: esiste un modo per risolvere questo problema utilizzando la funzione di finestra Row_Number(). Se hai relativamente pochi punti di prezzo rispetto al numero di azioni e stai cercando i prezzi per la maggior parte degli stock nella tabella della storia, allora potresti ottenere prestazioni migliori con quel metodo. Tuttavia, se esiste un numero elevato di punti di prezzo per azione o se stai filtrando su pochi titoli, puoi ottenere prestazioni migliori con il metodo che ti ho mostrato.

+0

Grazie v. Molto per questo ErikE. Riesco a prendere la tua croce applicare/applicare la metodologia esterna e l'ho applicata (davvero dispiaciuta per quello!) A una query SQL su cui stavo lavorando da mesi, ho potuto aggiungere tutti i tipi di cross/apply da altre tabelle con la query risultante in esecuzione circa 5 volte più veloce rispetto alla vecchia metodologia che stavo usando. – buellboy

Problemi correlati