2009-08-28 6 views
18

J'ai 2 colonnesSQL QUERY remplacer la valeur NULL dans une ligne avec une valeur de la précédente valeur connue

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

Je dois remplacer les valeurs NULL avec de nouvelles valeurs prend la valeur de la dernière valeur connue en la date précédente dans la colonne de date par exemple: date = 2 numéro = 3, date 4 et 5 numéro = 5 et 5. Les valeurs NULL apparaissent de manière aléatoire.

+2

Pouvez-vous identifier la marque de base de données SQL que vous utilisez, par ex. MySQL, Oracle, SQL-Server, et éditez votre question pour ajouter cette balise? –

+0

Voulez-vous dire la date dans une rangée précédente, ou la date précédente (jour -1)? Peut-être donner un exemple des données que vous avez dans les colonnes et un exemple de la sortie que vous désirez. – Degan

+0

@ Bill, il peut vouloir une solution générique. Certains d'entre nous * aiment * la possibilité de passer facilement d'un SGBD à l'autre quand ils deviennent un PITA :-) Cependant, il est très difficile (voire impossible) de le faire en SQL standard, @mike, donc si vous avez un SGBD spécifique l'esprit, par tous les moyens laissez-nous savoir. – paxdiablo

Répondre

2

Dans un sens très général:

UPDATE MyTable 
SET MyNullValue = MyDate 
WHERE MyNullValue IS NULL 
17

Si vous utilisez Sql Server cela devrait fonctionner

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) ne fonctionne pas - vous voulez la valeur avec l'ID max SquareCog

+0

@SquareCog: Relire l'OP: ... remplacer les valeurs NULL [colonne de nombre] par de nouvelles valeurs prises à partir de la dernière valeur connue [colonne de nombre] dans la date précédente dans la colonne date par exemple: date = 2 nombre = 3, date 4 et 5 nombre = 5 et 5. –

15

Voici une solution MySQL:

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

C'est concis, mais ne fonctionnera pas nécessairement dans d'autres marques de SGBDR. Pour les autres marques, il pourrait y avoir une solution spécifique à la marque qui soit plus pertinente. C'est pourquoi il est important de nous indiquer la marque que vous utilisez.

Il est bon d'être indépendant du fournisseur, comme l'a commenté @Pax, mais à défaut, il est également agréable d'utiliser votre marque de base de données à son avantage.


Explication de la requête ci-dessus:

@n est une variable d'utilisateur MySQL. Il commence par NULL et se voit attribuer une valeur sur chaque ligne lorsque UPDATE parcourt les lignes. Lorsque number est non nul, @n reçoit la valeur number. Lorsque number est NULL, le COALESCE() utilise par défaut la valeur précédente de @n. Dans les deux cas, cela devient la nouvelle valeur de la colonne number et le UPDATE passe à la ligne suivante. La variable @n conserve sa valeur de ligne en ligne, de sorte que les lignes suivantes obtiennent des valeurs provenant de la ou des lignes précédentes. L'ordre de UPDATE est prévisible, en raison de l'utilisation spéciale de MySQL par ORDER BY avec UPDATE (ce n'est pas un SQL standard).

+1

COALESCE est pris en charge sur SQL Server (2000?), et Oracle 9i +, mais je ne comprends pas ce que fait le @n. –

+0

@rexem: Voir modification ci-dessus. –

6

Voici la solution Oracle (10g ou plus).

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

Pourriez-vous utiliser LAG au lieu de LAST_VALUE? Si c'est le cas, cela rendrait 8i + compatible. –

+1

Non. LAG() ne fonctionne qu'avec un décalage fixe. Les données de test données ont un décalage variable. – APC

+1

Je suis venu un long chemin pour trouver cette réponse! IGNORE NULLS a fait toute la magie! – vdolez

1

Tout d'abord, avez-vous vraiment besoin de stocker les valeurs? Vous pouvez simplement utiliser la vue qui fait le travail:

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) 

Si vous avez vraiment la colonne ID ("date") et il est une clé primaire (cluster), cette requête devrait être assez rapide. Mais vérifiez le plan de la requête: il serait peut-être préférable d'avoir un index de couverture incluant la colonne Val.

Aussi, si vous ne voulez pas les procédures quand vous pouvez les éviter, vous pouvez également utiliser requête similaire pour 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 

REMARQUE: le code doit travaux sur « SQL Server ».

8

La meilleure solution est celle offerte par Bill Karwin.J'ai récemment dû résoudre ceci dans un resultset relativement grand (1000 lignes avec 12 colonnes ayant chacune besoin de ce type de "show me last non-null si cette valeur est null sur la ligne courante") et utilisant la méthode update avec un top 1 sélectionner pour la valeur connue précédente (ou sous-requête avec un top 1) a couru super lent.

J'utilise SQL 2005 et la syntaxe pour un remplacement variable est légèrement différente de celle mysql:

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

La première instruction set met à jour la valeur de la @n variable à la valeur de ligne courante du « numéro » si le 'number' n'est pas nul (COALESCE renvoie le premier argument non nul que vous lui passez) La seconde instruction set met à jour la valeur de la colonne pour 'number' (sinon null) ou la variable @n (qui contient toujours la dernière valeur non NULL rencontrée). La beauté de cette approche est qu'il n'y a pas de ressources supplémentaires dépensées pour analyser la table temporaire encore et encore ... La mise à jour dans la ligne de @n prend soin de suivre la dernière valeur non nulle.

Je n'ai pas assez de rep pour voter sa réponse, mais quelqu'un devrait le faire. C'est le plus élégant et le plus performant.

+0

SQLite ne prend pas en charge les variables. Cependant, si les entrées ont un identifiant auto-incrémenté et ont été insérées avec la date comme fonction monotone, alors chaque rangée consécutive pourrait référencer la précédente. Un cas très spécifique, cependant. MISE À JOUR mytable SET numéro = COALESCE (numéro, (CHOISIR numéro t de ma table t WHERE mytable.id = t.id + 1)); – Sussch

+0

SQL 2012 ne prend pas en charge une commande par. Je devais aller avec la réponse d'Adriaan Stander. –

4

Je sais que c'est un très vieux forum, mais je suis tombé dessus tout en dépanner mon problème :) juste réalisé que les autres gars ont donné une solution peu complexe au problème ci-dessus. S'il vous plaît voir ma solution ci-dessous:

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 

Hope this peut aider quelqu'un :)

+0

Un +1 tardif à votre approche tardive mais excellente. – pilcrow

1

Ceci est la solution pour MS Access. La table d'exemple est appelée tab, avec les champs id et 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

Le script suivant résout ce problème et n'utilise que du code ANSI SQL simple. J'ai testé cette solution sur SQL2008, SQLite3 et 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

Si vous êtes à la recherche d'une solution pour Redshift, cela fonctionnera avec la clause de cadre:

Date SELECT, last_value (columnName ignorer les valeurs NULL) plus (par ordre chronologique lignes entre unbounded précédente et rangée courante) comme columnName de tbl

-3

Essayer ceci:

update Projects 
set KickOffStatus=2 
where KickOffStatus is null 
Questions connexes