2010-04-27 22 views
7

Ho la seguente definizione di tabella con dati di esempio. Nella tabella che segue, prodotto cliente & Data sono campi chiaveCome trovare N record consecutivi in ​​una tabella utilizzando SQL

Table One 
Customer Product Date   SALE 
    X   A  01/01/2010 YES 
    X   A  02/01/2010 YES 
    X   A  03/01/2010 NO 
    X   A  04/01/2010 NO 
    X   A  05/01/2010 YES 
    X   A  06/01/2010 NO 
    X   A  07/01/2010 NO 
    X   A  08/01/2010 NO 
    X   A  09/01/2010 YES 
    X   A  10/01/2010 YES 
    X   A  11/01/2010 NO 
    X   A  12/01/2010 YES 

Nella tabella di cui sopra, ho bisogno di trovare la N o> N record consecutivi in ​​cui non c'era la vendita, il valore di vendita è stato 'NO' Per esempio , se N è 2, il risultato sarebbe tornato il seguente

 Customer Product Date   SALE 
     X   A  03/01/2010 NO 
     X   A  04/01/2010 NO 
     X   A  06/01/2010 NO 
     X   A  07/01/2010 NO 
     X   A  08/01/2010 NO 

qualcuno mi può aiutare con una query SQL per ottenere i risultati desiderati. Sto usando SQL Server 2005. Ho iniziato a giocare usando le clausole ROW_NUMBER() AND PARTITION ma non ho avuto fortuna. Grazie per qualsiasi aiuto

+0

Avete accesso per modificare lo schema della tabella? – munch

+0

Ciao, Ho accesso allo schema della tabella. – user320587

+0

Le date nell'esempio sembrano essere il primo del mese. È così che i dati sono effettivamente organizzati, o si tratta solo di dati di esempio? In caso contrario, cosa significa "consecutivo" in questo contesto. – MJB

risposta

3

Devi abbinare il tuo tavolo a se stesso, come se ci fossero 2 tavoli. Quindi si utilizzano due alias, O1 e O2 per fare riferimento al vostro tavolo:

SELECT DISTINCT o1.customer, o1.product, o1.datum, o1.sale 
    FROM one o1, one o2 
    WHERE (o1.datum = o2.datum-1 OR o1.datum = o2.datum +1) 
    AND o1.sale = 'NO' 
    AND o2.sale = 'NO'; 
customer | product | datum | sale 
----------+---------+------------+------ 
X  | A  | 2010-01-03 | NO 
X  | A  | 2010-01-04 | NO 
X  | A  | 2010-01-06 | NO 
X  | A  | 2010-01-07 | NO 
X  | A  | 2010-01-08 | NO 

Si noti che ho eseguito la query su un database PostgreSQL - forse la sintassi si differenzia su MS-SQL-Server, forse alla alias 'DA uno AS o1 'forse, e forse non puoi aggiungere/sottrarre in quel modo.

+0

Ciao Stefan, Il tuo suggerimento funziona perfettamente. Come gestisco il caso N consecutivo? – user320587

+0

Temo di avere quella parte N della tua domanda proprio adesso. Hm. Difficile. Vuoi spezificare N = 16 o N = 375 e ottenere un risultato analogico per almeno 16 o 375 date? Di quali N stiamo parlando? Ho bisogno di più spazio e aggiungere una risposta aggiuntiva. –

+0

Ciao Stefan, la N è variabile. Questo N sarebbe specificato dall'utente in fase di esecuzione della query. Sono stato in grado di trovare una soluzione per questo e lo posterò presto. Grazie per tutto il vostro aiuto. Lo apprezzo. Javid – user320587

0

Ok, abbiamo bisogno di una risposta variabile. Cerchiamo una data, dove abbiamo N seguenti date, tutte con il campo di vendita NO.

SELECT d1.datum 
FROM one d1, one d2, i 
WHERE d1.sale = 'NO' AND d2.sale = 'NO' 
    AND d1.datum = (d2.datum - i) 
    AND i > 0 AND i < 4 
GROUP BY d1.datum 
HAVING COUNT (*) = 3; 

Questo ci darà la data, che usiamo per subquerying.

Note:

  • ho usato 'dato' al posto di data, in quanto la data è una parola chiave riservata su PostgreSQL.

  • In Oracle è possibile utilizzare un manichino di tabella virtuale, che contiene tutto ciò che si richiede, come 'SELCT foo FROM dual WHERE foo in (1, 2, 3);' che ti darà 1, 2, 3, se ricordo bene. A seconda del venditore, potrebbero esserci altri trucchi per ottenere una sequenza da 1 a N. Ho creato una tabella i con la colonna i e l'ho riempita con i valori da 1 a 100, e mi aspetto che N non superi i 100; Poiché alcune versioni, postgresql contiene una funzione 'generate_series (from, to) che risolve il problema e potrebbe presentare somiglianze con le soluzioni per il database specifico. Ma la tabella dovrei funzionare indipendente dal venditore.

  • se N == 17, è necessario modificare 3 posti da 3 a 17.

La query finale sarà:

SELECT o4.* 
FROM one o3, one o4 
WHERE o3.datum = (
    SELECT d1.datum 
    FROM one d1, one d2, i 
    WHERE d1.sale = 'NO' AND d2.sale = 'NO' 
     AND d1.datum = (d2.datum - i) 
     AND i > 0 AND i <= 3 
    GROUP BY d1.datum 
    HAVING COUNT (*) = 3) 
AND o4.datum <= o3.datum + 3 
AND o4.datum >= o3.datum; 
customer | product | datum | sale 
----------+---------+------------+------ 
X  | A  | 2010-02-06 | NO 
X  | A  | 2010-02-07 | NO 
X  | A  | 2010-02-08 | NO 
X  | A  | 2010-02-09 | NO 
+0

Oh, ho detto che questo restituirà solo corrispondenze esatte dove N = 3, non N> = 3. –

1

Un approccio diverso, ispirato Munchs ultimi linea.

Ottenere - per una data specifica la prima data con YES più tardi rispetto a quella e l'ultima data con YES prima di quella. Questi costituiscono il confine, dove i nostri date devono adattarsi.

SELECT (o1.datum), 
    MAX (o3.datum) - MIN (o2.datum) AS diff 
FROM one o1, one o2, one o3 
WHERE o1.sale = 'NO' 
AND o3.datum < 
    (SELECT MIN (datum) 
    FROM one 
    WHERE datum >= o1.datum 
    AND SALE = 'YES') 
AND o2.datum > 
    (SELECT MAX (datum) 
    FROM one 
    WHERE datum <= o1.datum 
    AND SALE = 'YES') 
GROUP BY o1.datum 
HAVING MAX (o3.datum) - MIN (o2.datum) >= 2 
ORDER BY o1.datum; 

Forse ha bisogno di qualche tipo di ottimizzazione, perché la tabella uno è 5 volte coinvolti nella query. :)

+0

-1 Per l'utilizzo della sintassi implicita 'where' – Johan

+0

Non ti piace o hai argomenti motivati ​​contro di esso? –

+0

A. è confusionario, B. non puoi dire quali sono i criteri di join e quali i criteri di filtro, C. è un errore incline a un errore e hai un cross join, D. Non consente i join di sinistra. E. È incoerente, si eseguono join interni con sintassi implicita ma si devono fare join esterni con sintassi esplicita F. Non segue le best practice G. Rende più difficile per lo Strumento di ottimizzazione delle query produrre codice efficiente (specialmente su SQL server). – Johan

0

Grazie a tutti per aver postato la tua soluzione.Pensiero, vorrei anche condividere la mia soluzione con tutti. Proprio come una FYI, ho ricevuto questa soluzione da un altro membro del forum di SQL Server Central. Sicuramente non mi prenderò il merito di questa soluzione.

DECLARE @CNT INT 
SELECT @CNT = 3 

SELECT * FROM 
(
    SELECT 
    [Customer], [Product], [Date], [Sale], groupID, 
    COUNT(*) OVER (PARTITION BY [Customer], [Product], [Sale], groupID) AS groupCnt 
    FROM 
    (
    SELECT 
     [Customer], [Product], [Date], [Sale], 
     ROW_NUMBER() OVER (PARTITION BY [Customer], [Product] ORDER BY [Date]) 
     - ROW_NUMBER() OVER (PARTITION BY [Customer], [Product], [Sale] ORDER BY [Date]) AS groupID 
    FROM 
     [TableSales] 
) T1 
) T2 
WHERE 
    T2.[Sale] = 'NO' AND T2.[groupCnt] >= @CNT 
+0

Questa query ha il potenziale per essere molto, molto lento (query N^3 se il database non è in grado di ottimizzarlo in alcun modo) – Nicole

Problemi correlati