2012-05-01 13 views
5

Un colega mío tiene un problema con una consulta SQL: -¿Cómo reemplazar NULL en un conjunto de resultados con el último valor NOT NULL en la misma columna?

tomar las siguientes como ejemplo, dos tablas temporales: -

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'; 

Llevamos a cabo la siguiente consulta en las tablas para darnos una de resultados unió: -

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 

como no existe 'Dave' en la tabla #jobs, se le da un valor NULL como se esperaba.

Mi colega desea modificar la consulta para que cada valor NULO tenga el mismo valor que la entrada anterior.

Así que lo anterior sería: -

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

Nota que Dave es ahora un 'Controlador'

Puede haber más de un valor NULL en secuencia,

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

En en este caso, Dave, Joe y Pete deberían ser todos 'Conductores', ya que 'Conductor' es la última entrada no nula.

+2

¿Hay una restricción de orden o son los controladores Dave, Joe y Pete solo porque se devuelven arbitrariamente después de Alyn? – GolfWolf

+2

Esto es algo que probablemente quieras hacer en código. – Magnus

+0

En el ejemplo actual, no hay orden, por lo que solo se devuelve arbitrariamente. –

Respuesta

6

Probablemente haya mejores formas de hacerlo. Esta es una de las formas en que pude lograr el resultado usando Common Table Expressions (CTE) y usando esa salida para realizar un OUTER APPLY para encontrar el trabajo de la anterior persion. La consulta aquí usa id para ordenar los registros y luego determina cuál fue el trabajo de la persona anterior. Necesita al menos un criterio para ordenar los registros porque los datos en las tablas se consideran conjuntos no ordenados.

Además, la suposición es que la primera persona en la secuencia debe tener un trabajo. Si la primera persona no tiene un trabajo, entonces no hay ningún valor para elegir.

Click here to view the demo in SQL Fiddle.

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

Guión:

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; 

de salida:

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 No sabía acerca de SQLFiddle. Además, ¡gran solución! – GolfWolf

+1

SELECCIONE EL 100 POR CIENTO SUPERIOR .. ORDER BY dentro de un CTE, vista o subconsulta no tiene ningún efecto. Hace años, generalmente garantizaba un pedido, pero eso nunca se documentó, y no se puede confiar en que haga nada en absoluto. –

+0

Una versión modificada de lo anterior ha hecho el trabajo muy bien. Gracias. –

0

Qué hacer yo ¿Quiere decir por "última" entrada no nula? Necesita un orden bien definido para que "último" tenga un significado consistente. Aquí hay una consulta con definiciones de datos que usa la columna "valor" para definir la última, y ​​que podría estar cerca de lo que desea.

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; 

Podría ser más eficiente insertar los datos y luego actualizarlos.

Cuestiones relacionadas