2009-08-28 1 views
18

Ho 2 colonneSQL QUERY sostituire valore NULL in una riga con un valore dal valore noto precedente

date number  
---- ------ 
1  3   
2  NULL   
3  5   
4  NULL   
5  NULL   
6  2   
....... 

necessario sostituire i valori NULL con i nuovi valori assume il valore dall'ultimo valore noto in la data precedente nella colonna data ad esempio: data = 2 numero = 3, data 4 e 5 numero = 5 e 5. I valori NULL appaiono casualmente.

+2

Puoi identificare la marca del database SQL che stai utilizzando, ad es. MySQL, Oracle, SQL-Server e modifica la tua domanda per aggiungere quel tag? –

+0

Intendi la data in una riga precedente o la data precedente (giorno -1)? Puoi dare un esempio dei dati che hai nelle colonne e un esempio dell'output che desideri. – Degan

+0

@Bill, potrebbe volere una soluzione generica. Alcuni di noi in realtà * gradiscono * la possibilità di passare facilmente tra DBMS 'quando diventano PITA :-) Tuttavia, è molto difficile (forse impossibile) farlo in SQL standard, @ mike, quindi se si ha un DBMS specifico in mente, con ogni mezzo facci sapere. – paxdiablo

risposta

2

In un senso molto generale:

UPDATE MyTable 
SET MyNullValue = MyDate 
WHERE MyNullValue IS NULL 
17

Se si utilizza SQL Server questo dovrebbe funzionare

DECLARE @Table TABLE(
     ID INT, 
     Val INT 
) 

INSERT INTO @Table (ID,Val) SELECT 1, 3 
INSERT INTO @Table (ID,Val) SELECT 2, NULL 
INSERT INTO @Table (ID,Val) SELECT 3, 5 
INSERT INTO @Table (ID,Val) SELECT 4, NULL 
INSERT INTO @Table (ID,Val) SELECT 5, NULL 
INSERT INTO @Table (ID,Val) SELECT 6, 2 


SELECT *, 
     ISNULL(Val, (SELECT TOP 1 Val FROM @Table WHERE ID < t.ID AND Val IS NOT NULL ORDER BY ID DESC)) 
FROM @Table t 
0
UPDATE TABLE 
    SET number = (SELECT MAX(t.number) 
        FROM TABLE t 
       WHERE t.number IS NOT NULL 
        AND t.date < date) 
WHERE number IS NULL 
+0

max (t.value) non funziona: si desidera il valore con l'ID max SquareCog

+0

@SquareCog: rileggere l'OP: ... sostituire i valori NULL [numero colonna] con nuovi valori presi dall'ultimo valore [colonna numero] noto nella data precedente nella colonna data es .: data = 2 numero = 3, data 4 e 5 numero = 5 e 5. –

15

Ecco una soluzione MySQL:

UPDATE mytable 
SET number = (@n := COALESCE(number, @n)) 
ORDER BY date; 

Questo è conciso, ma non sarà necessario lavorare in altre marche di RDBMS. Per altri marchi, potrebbe esserci una soluzione specifica per il marchio che è più rilevante. Ecco perché è importante dirci il marchio che stai utilizzando.

È bello essere indipendenti dal fornitore, come ha commentato @Pax, ma in caso contrario, è anche bello utilizzare il database del database prescelto al massimo vantaggio.


Spiegazione della query precedente:

@n è una variabile utente MySQL. Inizia NULL e viene assegnato un valore su ogni riga mentre UPDATE scorre attraverso le righe. Dove number non è NULL, @n viene assegnato il valore di number. Dove number è NULL, il valore COALESCE() corrisponde al valore precedente di @n. In entrambi i casi, questo diventa il nuovo valore della colonna number e UPDATE passa alla riga successiva. La variabile @n mantiene il suo valore da riga a riga, quindi le righe successive ottengono i valori che provengono dalle righe precedenti. L'ordine di UPDATE è prevedibile, a causa dell'uso speciale di ORDER BY da parte di MySQL con UPDATE (non è SQL standard).

+1

COALESCE è supportato su SQL Server (2000?), e Oracle 9i +, ma non capisco cosa stia facendo @n. –

+0

@rexem: vedi modifica sopra. –

6

Ecco la soluzione Oracle (10g o successiva).

SQL> select * 
    2 from mytable 
    3 order by id 
    4/

     ID SOMECOL 
---------- ---------- 
     1   3 
     2 
     3   5 
     4 
     5 
     6   2 

6 rows selected. 

SQL> select id 
    2   , last_value(somecol ignore nulls) over (order by id) somecol 
    3 from mytable 
    4/

     ID SOMECOL 
---------- ---------- 
     1   3 
     2   3 
     3   5 
     4   5 
     5   5 
     6   2 

6 rows selected. 

SQL> 
+0

Potresti usare il LAG invece di LAST_VALUE? Se è così, questo lo renderebbe compatibile con 8i +. –

+1

No. LAG() funziona solo con un offset fisso. I dati di test forniti hanno un offset variabile. – APC

+1

Ho fatto molta strada per trovare questa risposta! IGNORE NULLS ha fatto tutta la magia! – vdolez

1

Prima di tutto, è davvero necessario memorizzare i valori? Si può solo usare la vista che fa il lavoro:

SELECT t."date", 
     x."number" AS "number" 
FROM @Table t 
JOIN @Table x 
    ON x."date" = (SELECT TOP 1 z."date" 
        FROM @Table z 
        WHERE z."date" <= t."date" 
         AND z."number" IS NOT NULL 
        ORDER BY z."date" DESC) 

Se avete veramente la colonna ID ("date") ed è una chiave primaria (cluster), allora questa query dovrebbe essere abbastanza veloce. Ma controlla il piano delle query: potrebbe essere meglio avere un indice di copertura che includa anche la colonna Val.

Anche se non ti piace procedure quando si può evitare di loro, si può anche utilizzare query simile per UPDATE:

UPDATE t 
SET  t."number" = x."number" 
FROM @Table t 
JOIN @Table x 
    ON x."date" = (SELECT TOP 1 z."date" 
        FROM @Table z 
        WHERE z."date" < t."date" --//@note: < and not <= here, as = not required 
         AND z."number" IS NOT NULL 
        ORDER BY z."date" DESC) 
WHERE t."number" IS NULL 

NOTA: Il codice deve opere in "SQL Server".

8

La soluzione migliore è quella offerta da Bill Karwin.Recentemente ho dovuto risolvere questo problema in un set di risultati relativamente ampio (1000 righe con 12 colonne ciascuna che richiedono questo tipo di "mostra l'ultimo valore non null se questo valore è nullo nella riga corrente") e utilizzando il metodo di aggiornamento con una top 1 selezionare per il precedente valore noto (o sottoquery con un primo 1) eseguito super slow.

Sto usando SQL 2005 e la sintassi per una sostituzione variabile è leggermente diversa rispetto a MySQL:

UPDATE mytable 
SET 
    @n = COALESCE(number, @n), 
    number = COALESCE(number, @n) 
ORDER BY date 

La prima istruzione SET aggiorna il valore della @n variabile per il valore della riga corrente del 'numero' se il 'numero' non è nullo (COALESCE restituisce il primo argomento non nullo che si passa in esso) La seconda istruzione del set aggiorna il valore effettivo della colonna per 'numero' a se stesso (se non null) o la variabile @n (che contiene sempre l'ultimo valore non NULL riscontrato).

La bellezza di questo approccio è che non ci sono risorse aggiuntive spese per la scansione della tabella temporanea più e più volte ... L'aggiornamento in-row di @n si occupa di tracciare l'ultimo valore non nullo.

Non ho abbastanza rep per votare la sua risposta, ma qualcuno dovrebbe. È il più elegante e il più performante.

+0

SQLite non supporta variabili. Tuttavia, se le voci hanno un ID autoincrement e sono state inserite con data come funzione monotona, ciascuna riga consecutiva potrebbe fare riferimento al precedente. Un caso molto specifico, però. UPDATE numero SET impostabile = COALESCE (numero, (SELEZIONA il numero t di mytable t WHERE mytable.id = t.id + 1)); – Sussch

+0

SQL 2012 non supporta un ordine di. Dovevo andare con la risposta di Adriaan Stander. –

4

So che è un forum molto vecchio, ma mi sono imbattuto in questo mentre risolvevo il mio problema :) mi sono appena reso conto che gli altri ragazzi hanno dato una soluzione un po 'complessa al problema di cui sopra. Si prega di vedere la mia soluzione qui di seguito:

DECLARE @A TABLE(ID INT, Val INT) 

INSERT INTO @A(ID,Val) SELECT 1, 3 
INSERT INTO @A(ID,Val) SELECT 2, NULL 
INSERT INTO @A(ID,Val) SELECT 3, 5 
INSERT INTO @A(ID,Val) SELECT 4, NULL 
INSERT INTO @A(ID,Val) SELECT 5, NULL 
INSERT INTO @A(ID,Val) SELECT 6, 2 

UPDATE D 
    SET D.VAL = E.VAL 
    FROM (SELECT A.ID C_ID, MAX(B.ID) P_ID 
      FROM @A AS A 
      JOIN @A AS B ON A.ID > B.ID 
      WHERE A.Val IS NULL 
      AND B.Val IS NOT NULL 
      GROUP BY A.ID) AS C 
    JOIN @A AS D ON C.C_ID = D.ID 
    JOIN @A AS E ON C.P_ID = E.ID 

SELECT * FROM @A 

Spero che questo possa aiutare qualcuno :)

+0

Un +1 in ritardo al tuo tardivo ma eccellente approccio. – pilcrow

1

Questa è la soluzione per MS Access.

La tabella di esempio è denominata tab, con campi id e val.

SELECT (SELECT last(val) 
      FROM tab AS temp 
      WHERE tab.id >= temp.id AND temp.val IS NOT NULL) AS val2, * 
    FROM tab; 
5

Il seguente script risolve questo problema e utilizza solo SQL ANSI semplice. Ho provato questa soluzione su SQL2008, SQLite3 e Oracle11g.

CREATE TABLE test(mysequence INT, mynumber INT); 

INSERT INTO test VALUES(1, 3); 
INSERT INTO test VALUES(2, NULL); 
INSERT INTO test VALUES(3, 5); 
INSERT INTO test VALUES(4, NULL); 
INSERT INTO test VALUES(5, NULL); 
INSERT INTO test VALUES(6, 2); 

SELECT t1.mysequence, t1.mynumber AS ORIGINAL 
, (
    SELECT t2.mynumber 
    FROM test t2 
    WHERE t2.mysequence = (
     SELECT MAX(t3.mysequence) 
     FROM test t3 
     WHERE t3.mysequence <= t1.mysequence 
     AND mynumber IS NOT NULL 
     ) 
) AS CALCULATED 
FROM test t1; 
0

Se siete alla ricerca di una soluzione per Redshift, questo funzionerà con la clausola di telaio:

data SELECT, last_value (columnName ignorare i valori nulli) sopra (ordine per data righe tra illimitata precedente e riga corrente) come columnName da tbl

-3

Prova questo:

update Projects 
set KickOffStatus=2 
where KickOffStatus is null