J'ai 2 colonnes
date number
---- ------
1 3
2 NULL
3 5
4 NULL
5 NULL
6 2
.......
J'ai besoin de remplacer les valeurs NULL par de nouvelles valeurs prend la valeur de la dernière valeur connue dans la date précédente dans la colonne de date, par exemple: date = 2 nombre = 3, date 4 et 5 nombre = 5 et 5. Les valeurs NULL apparaissent au hasard.
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
Voici une solution MySQL:
UPDATE mytable
SET number = (@n := COALESCE(number, @n))
ORDER BY date;
Ceci est concis, mais ne fonctionnera pas nécessairement dans d'autres marques de SGBDR. Pour les autres marques, il peut exister une solution spécifique à la marque plus pertinente. C'est pourquoi il est important de nous indiquer la marque que vous utilisez.
Il est agréable d'être indépendant du fournisseur, comme l'a commenté @Pax, mais à défaut, il est également agréable d'utiliser la marque de base de données que vous avez choisie à son meilleur avantage.
Explication de la requête ci-dessus:
@n
Est une variable utilisateur MySQL. Il commence par NULL et se voit attribuer une valeur sur chaque ligne lorsque la MISE À JOUR passe par les lignes. Lorsque number
n'est pas NULL, @n
Reçoit la valeur de number
. Où number
est NULL, la fonction COALESCE()
prend par défaut la valeur précédente de @n
. Dans les deux cas, cela devient la nouvelle valeur de la colonne number
et la MISE À JOUR passe à la ligne suivante. La variable @n
Conserve sa valeur de ligne en ligne, donc les lignes suivantes obtiennent des valeurs qui proviennent des lignes précédentes. L'ordre de la mise à jour est prévisible, en raison de l'utilisation spéciale de MySQL de ORDER BY avec UPDATE (ce n'est pas du SQL standard).
La meilleure solution est celle proposée par Bill Karwin. J'ai récemment dû résoudre ce problème dans un ensemble de résultats relativement volumineux (1000 lignes avec 12 colonnes nécessitant chacune ce type de "montrez-moi la dernière valeur non nulle si cette valeur est nulle sur la ligne actuelle") et en utilisant la méthode de mise à jour avec un top 1 sélectionner pour la valeur connue précédente (ou sous-requête avec un top 1) s'est déroulée très lentement.
J'utilise SQL 2005 et la syntaxe pour un remplacement de variable est légèrement différente de 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 variable @n à la valeur actuelle de 'nombre' si le 'nombre' n'est pas nul (COALESCE renvoie le premier argument non nul que vous lui passez) La deuxième instruction set met à jour la valeur réelle valeur de colonne pour 'nombre' pour lui-même (sinon nul) 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 consacrées à l'analyse de la table temporaire encore et encore ... La mise à jour en ligne de @n prend en charge le suivi de la dernière valeur non nulle.
Je n'ai pas assez de représentants pour voter sa réponse, mais quelqu'un devrait le faire. C'est le plus élégant et le plus performant.
Voici la solution Oracle (10g ou plus). Il utilise la fonction analytique last_value()
avec le ignore nulls
option, qui remplace la dernière valeur non nulle de la colonne.
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>
Le script suivant résout ce problème et utilise uniquement du code ANSI SQL simple. J'ai testé cette solution sur SQL2008 , SQLite 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;
Je sais que c'est un très vieux forum, mais je suis tombé sur ce problème lors du dépannage de mon problème :) je viens de réaliser que les autres gars ont donné une solution peu complexe au problème ci-dessus. Veuillez 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
J'espère que cela peut aider quelqu'un :)
Si vous cherchez une solution pour Redshift, cela fonctionnera avec la clause frame:
SELECT date,
last_value(columnName ignore nulls)
over (order by date
rows between unbounded preceding and current row) as columnName
from tbl
Dans un sens très général:
UPDATE MyTable
SET MyNullValue = MyDate
WHERE MyNullValue IS NULL
Ceci est la solution pour MS Access.
L'exemple de table s'appelle 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;
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 qu'il s'agit d'une clé primaire (clusterisée), cette requête devrait être assez rapide. Mais vérifiez le plan de requête: il pourrait être préférable d'avoir un index de couverture incluant également la colonne Val
.
De plus, si vous n'aimez pas les procédures lorsque vous pouvez les éviter, vous pouvez également utiliser une 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 fonctionner sur "SQL Server".
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