2009-08-28 81 views
18

tengo 2 columnasSQL sustituir el valor NULL en una fila con un valor entre el valor anterior conocida

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

tengo que sustituir los valores NULL con nuevos valores adquiere el valor del último valor conocido en la fecha anterior en la columna de fecha por ejemplo: fecha = 2 número = 3, fecha 4 y 5 número = 5 y 5. Los valores NULL aparecen aleatoriamente.

+2

¿Puede identificar la marca de la base de datos SQL que está utilizando, p. MySQL, Oracle, SQL-Server, y edite su pregunta para agregar esa etiqueta? –

+0

¿Quiere decir la fecha en una fila anterior o la fecha anterior (día -1)? Quizás proporcione un ejemplo de los datos que tiene en las columnas y un ejemplo del resultado que desea. – Degan

+0

@ Bill, es posible que desee una solución genérica. A algunos de nosotros * les gusta * la capacidad de cambiar fácilmente entre DBMS 'cuando se convierten en PITA :-) Aún así, es muy difícil (quizás imposible) hacer esto en SQL estándar, @mike, así que si tienes un DBMS específico en mente, de todos modos avísanos. – paxdiablo

Respuesta

2

En un sentido muy general:

UPDATE MyTable 
SET MyNullValue = MyDate 
WHERE MyNullValue IS NULL 
17

Si está utilizando Servidor SQL esto debería funcionar

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) no funciona; desea el valor con el id. Máximo SquareCog

+0

@SquareCog: vuelva a leer el OP: ... reemplace los valores NULL [number column] con new valores tomados del último valor conocido de [columna numérica] en la fecha anterior en la columna de fecha, por ejemplo: fecha = 2 número = 3, fecha 4 y 5 número = 5 y 5. –

15

Aquí hay una solución MySQL:

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

Este es conciso, pero no será necesario trabajar en otras marcas de RDBMS. Para otras marcas, puede haber una solución específica de la marca que sea más relevante. Es por eso que es importante que nos diga la marca que está usando.

Es bueno ser independiente del proveedor, como comentó @Pax, pero en su defecto, también es bueno utilizar la base de datos de su marca elegida para su máxima ventaja.


Explicación de la consulta anterior:

@n es una variable de usuario MySQL. Comienza NULL y se le asigna un valor en cada fila a medida que la ACTUALIZACIÓN se ejecuta en las filas. Donde number no es NULL, @n tiene asignado el valor number. Donde number es NULO, el COALESCE() se predetermina al valor anterior de @n. En cualquier caso, este se convierte en el nuevo valor de la columna number y la ACTUALIZACIÓN avanza a la siguiente fila. La variable @n conserva su valor de una fila a otra, por lo que las filas subsiguientes obtienen valores que provienen de la (s) fila (s) anterior (es). El orden de la ACTUALIZACIÓN es predecible, debido al uso especial de MySQL de ORDER BY con UPDATE (esto no es SQL estándar).

+1

COALESCE es compatible con SQL Server (2000?), y Oracle 9i +, pero no entiendo lo que está haciendo @n. –

+0

@rexem: ver la edición anterior. –

6

Aquí está la solución de Oracle (10g o superior).

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

¿Podría usar LAG en lugar de LAST_VALUE? Si es así, eso lo haría 8i + compatible. –

+1

No. LAG() solo funciona con un desplazamiento fijo. Los datos de prueba dados tienen un desplazamiento variable. – APC

+1

¡He recorrido un largo camino para encontrar esta respuesta! ¡IGNORE NULLS hizo toda la magia! – vdolez

1

Antes que nada, ¿realmente necesita almacenar los valores? Es posible que sólo utiliza la vista que hace el trabajo:

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 realmente tienen la columna de la ID ("date") y es una clave principal (agrupado), entonces esta consulta debe ser bastante rápido. Pero revise el plan de consulta: podría ser mejor tener un índice de portada que incluya la columna Val también.

Además, si no te gusta procedimientos cuando se puede evitar, se puede también utilizar para la consulta similar 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: El código debe obras en "SQL Server".

8

La mejor solución es la que ofrece Bill Karwin.Recientemente tuve que resolver esto en un conjunto de resultados relativamente grande (1000 filas con 12 columnas cada una necesitando este tipo de "mostrarme el último valor no nulo si este valor es nulo en la fila actual") y usar el método de actualización con un top 1 seleccione para el valor conocido anterior (o subconsulta con un top 1) ejecutó super lento.

Estoy utilizando SQL 2005 y el sintaxis para un reemplazo variable es ligeramente diferente a la de MySQL:

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

El primer comunicado conjunto se actualiza el valor de la @ n variable para el valor de la fila actual de 'número' si el 'número' no es nulo (COALESCE devuelve el primer argumento no nulo que pase a él) La segunda instrucción set actualiza el valor real de la columna para 'number' a sí mismo (si no es nulo) o la variable @n (que siempre contiene el último valor no NULL encontrado).

Lo bueno de este enfoque es que no se gastan recursos adicionales en escanear la tabla temporal una y otra vez ... La actualización en línea de @n se encarga de rastrear el último valor no nulo.

No tengo suficientes representantes para votar su respuesta, pero alguien debería hacerlo. Es el más elegante y el mejor rendimiento.

+0

SQLite no admite variables. Sin embargo, si las entradas tienen una identificación autoincrement y se han insertado con la fecha como una función monotónica, cada fila consecutiva podría hacer referencia a la anterior. Un caso muy específico, sin embargo. UPDATE mytable SET number = COALESCE (número, (SELECCIONE t.número FROM mytable t WHERE mytable.id = t.id + 1)); – Sussch

+0

SQL 2012 no admite un pedido por. Tenía que ir con la respuesta de Adriaan Stander. –

4

Sé que es un foro muy antiguo, pero me encontré con esto mientras solucionaba mi problema :) simplemente me di cuenta de que los otros chicos le habían dado una solución poco compleja al problema anterior. Por favor, vea mi solución a continuación:

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 

la esperanza que esto puede ayudar a alguien :)

+0

Un +1 tardío a su enfoque tardío pero excelente. – pilcrow

1

Ésta es la solución de MS Access.

La tabla de ejemplo se llama tab, con los campos id y 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

La siguiente secuencia de comandos resuelve este problema y solo utiliza ANSI SQL sin formato. Probé esta solución en SQL2008, SQLite3 y 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 usted está buscando una solución para el desplazamiento hacia el rojo, esto va a funcionar con la cláusula del marco:

fecha SELECT, last_value (columnName ignorar los valores nulos) sobre (orden por fecha entre filas sin límites precedente y fila actual) como columnName de TBL

-3

Prueba esto:

update Projects 
set KickOffStatus=2 
where KickOffStatus is null 
Cuestiones relacionadas