2012-05-01 13 views
5

Un mio collega ha un problema con una query SQL: -Come sostituire NULL in un set di risultati con l'ultimo valore NOT NULL nella stessa colonna?

Prendiamo il seguente titolo di esempio, due tabelle temporanee: -

select 'John' as name,10 as value into #names 
UNION ALL SELECT 'Abid',20 
UNION ALL SELECT 'Alyn',30 
UNION ALL SELECT 'Dave',15; 

select 'John' as name,'SQL Expert' as job into #jobs 
UNION ALL SELECT 'Alyn','Driver' 
UNION ALL SELECT 'Abid','Case Statement'; 

Corriamo il seguente query sui tavoli a darci una di risultati aderito: -

select #names.name, #names.value, #jobs.job 
FROM #names left outer join #jobs 
on #names.name = #jobs.name 

name value job 
John 10  SQL Expert 
Abid 20  Case Statement 
Alyn 30  Driver 
Dave 15  NULL 

come 'Dave' non esiste nella tabella #jobs, gli viene dato un valore NULL come previsto.

Il mio collega desidera modificare la query in modo che a ogni valore NULL venga assegnato lo stesso valore della voce precedente.

Così quanto sopra sarebbe: -

name value job 
John 10  SQL Expert 
Abid 20  Case Statement 
Alyn 30  Driver 
Dave 15  Driver 

Nota che Dave ora è un 'Driver'

Ci può essere più di un valore NULL in sequenza,

name value job 
John 10  SQL Expert 
Abid 20  Case Statement 
Alyn 30  Driver 
Dave 15  NULL 
Joe  15  NULL 
Pete 15  NULL 

In questo caso Dave, Joe e Pete dovrebbero essere tutti 'Driver', poiché 'Driver' è l'ultima voce non nulla.

+2

c'è un vincolo di ordine, o sono i driver di Dave, Joe e Pete solo perché vengono restituiti arbitrariamente dopo Alyn? – GolfWolf

+2

Questo è qualcosa che vorresti probabilmente fare in codice, invece. – Magnus

+0

Nell'esempio corrente non vi è alcun ordine, quindi si è appena ritornato arbitrariamente. –

risposta

6

Ci sono probabilmente modi migliori per farlo. Ecco uno dei modi in cui è possibile ottenere il risultato utilizzando Common Table Expressions (CTE) e utilizzando quell'output per eseguire uno OUTER APPLY per trovare il lavoro della precedente persione. La query qui utilizza id per ordinare i record e quindi determina il lavoro della persona precedente. È necessario almeno un criterio per ordinare i record perché i dati nelle tabelle sono considerati set non ordinati.

Inoltre, il presupposto è che la prima persona nella sequenza debba avere un lavoro. Se la prima persona non ha un lavoro, allora non c'è alcun valore tra cui scegliere.

Click here to view the demo in SQL Fiddle.

Click here to view another demo in SQL Fiddle with second data set.

Script:

CREATE TABLE names 
    (
      id  INT   NOT NULL IDENTITY 
     ,  name VARCHAR(20) NOT NULL 
     ,  value INT   NOT NULL 
    ); 

    CREATE TABLE jobs 
    (
      id INT   NOT NULL 
     ,  job VARCHAR(20) NOT NULL 
    ); 

    INSERT INTO names (name, value) VALUES 
     ('John', 10), 
     ('Abid', 20), 
     ('Alyn', 30), 
     ('Dave', 40), 
     ('Jill', 50), 
     ('Jane', 60), 
     ('Steve', 70); 

    INSERT INTO jobs (id, job) VALUES 
     (1, 'SQL Expert'), 
     (2, 'Driver'), 
     (5, 'Engineer'), 
     (6, 'Barrista'); 

    ;WITH empjobs AS 
    (
     SELECT 
     TOP 100 PERCENT n.id 
        , n.name 
        , n.value 
        , job 
     FROM   names n 
     LEFT OUTER JOIN jobs j 
     on    j.id = n.id 
     ORDER BY  n.id 
    ) 
    SELECT  e1.id 
      , e1.name 
      , e1.value 
      , COALESCE(e1.job , e2.job) job FROM empjobs e1 
    OUTER APPLY (
        SELECT 
        TOP 1  job 
        FROM  empjobs  e2 
        WHERE  e2.id < e1.id 
        AND  e2.job IS NOT NULL 
        ORDER BY e2.id DESC 
       ) e2; 

uscita:

ID NAME VALUE JOB 
--- ------ ----- ------------- 
1 John  10 SQL Expert 
2 Abid  20 Driver 
3 Alyn  30 Driver 
4 Dave  40 Driver 
5 Jill  50 Engineer 
6 Jane  60 Barrista 
7 Steve  70 Barrista 
+0

+1 Non sapevo di SQLFiddle. Inoltre, ottima soluzione! – GolfWolf

+1

SELEZIONA IL TOP 100 PERCENT. ORDINA DA all'interno di un CTE, la vista o la sottoquery non hanno alcun effetto. Anni fa, di solito garantiva un ordine, ma non era mai stato documentato e non si può fare affidamento su nulla. –

+0

Una versione modificata di quanto sopra ha fatto bene il lavoro. Grazie. –

0

Cosa fare yo vuoi dire con "ultimo" non-nullo? È necessario un ordinamento ben definito per "ultimo" per avere un significato coerente. Ecco una query con definizioni dei dati che utilizza la colonna "valore" per definire l'ultima e che potrebbe essere vicina a ciò che desideri.

CREATE TABLE #names 
    (
      id  INT   NOT NULL IDENTITY 
     ,  name VARCHAR(20) NOT NULL 
     ,  value INT   NOT NULL PRIMARY KEY 
    ); 

    CREATE TABLE #jobs 
    (
      name VARCHAR(20)   NOT NULL 
     ,  job VARCHAR(20) NOT NULL 
    ); 

    INSERT INTO #names (name, value) VALUES 
     ('John', 10), 
     ('Abid', 20), 
     ('Alyn', 30), 
     ('Dave', 40), 
     ('Jill', 50), 
     ('Jane', 60), 
     ('Steve', 70); 

    INSERT INTO #jobs (name, job) VALUES 
     ('John', 'SQL Expert'), 
     ('Abid', 'Driver'), 
     ('Alyn', 'Engineer'), 
     ('Dave', 'Barrista'); 

with Partial as (
    select 
    #names.name, 
    #names.value, 
    #jobs.job as job 
    FROM #names left outer join #jobs 
    on #names.name = #jobs.name 
) 
    select 
    name, 
    value, 
    (
     select top 1 job 
     from Partial as P 
     where job is not null 
     and P.value <= Partial.value 
     order by value desc 
    ) 
    from Partial; 

Potrebbe essere più efficiente inserire i dati, quindi aggiornare.

Problemi correlati