Seleccionar elementos que no existen

votos
0

Estoy trabajando en una aplicación que tiene que asignar códigos numéricos a los elementos. Estos códigos no son consecutivos y mi idea es no insertarlos en la base de datos hasta tener el elemento relacionado, pero me gustaría encontrar, en una cuestión sql, los códigos no asignados y no sé cómo hacerlo.

¿Algunas ideas? ¡¡¡Gracias!!!

Editar 1

La tabla puede ser tan simple:

code | element 
-----------------
3    | three 
7    | seven 
2    | two

Y me gustaría algo como esto: 1, 4, 5, 6. Sin otra tabla.

Editar 2

Gracias por los comentarios, sus respuestas han sido muy útiles.

Publicado el 25/03/2009 a las 17:10
fuente por usuario
En otros idiomas...                            


3 respuestas

votos
6

Esto volverá NULLsi no se asigna un código:

SELECT  assigned_codes.code
FROM    codes 
LEFT JOIN
        assigned_codes
ON      assigned_codes.code = codes.code
WHERE   codes.code = @code

Esto devolverá todos los códigos no asignados:

SELECT  codes.code
FROM    codes 
LEFT JOIN
        assigned_codes
ON      assigned_codes.code = codes.code
WHERE   assigned_codes.code IS NULL

No hay una SQLforma pura de hacer exactamente lo que quieres.

En Oracle, puedes hacer lo siguiente:

SELECT  lvl
FROM    (
        SELECT  level AS lvl
        FROM    dual
        CONNECT BY
                level <=
                (
                SELECT  MAX(code)
                FROM    elements
                )
        )
LEFT OUTER JOIN
        elements
ON      code = lvl
WHERE   code IS NULL

En PostgreSQL, puedes hacer lo siguiente:

SELECT  lvl
FROM    generate_series(
        1,
        (
        SELECT  MAX(code)
        FROM    elements
        )) lvl
LEFT OUTER JOIN
        elements
ON      code = lvl
WHERE   code IS NULL
Respondida el 25/03/2009 a las 17:11
fuente por usuario

votos
1

Contrariamente a la afirmación de que esto no se puede hacer usando SQL puro, aquí hay un ejemplo de contador que muestra cómo se puede hacer. (Tenga en cuenta que no dije que era fácil, es posible). Supongamos que el nombre de la tabla es value_listcon columnas codey valuecomo se muestra en las ediciones (¿por qué todos olvidan incluir el nombre de la tabla en la pregunta?):

SELECT b.bottom, t.top
    FROM (SELECT l1.code - 1 AS top
            FROM value_list l1
            WHERE NOT EXISTS (SELECT * FROM value_list l2
                                 WHERE l2.code = l1.code - 1)) AS t,    
         (SELECT l1.code + 1 AS bottom
            FROM value_list l1
            WHERE NOT EXISTS (SELECT * FROM value_list l2
                                 WHERE l2.code = l1.code + 1)) AS b
    WHERE b.bottom <= t.top
      AND NOT EXISTS (SELECT * FROM value_list l2
                         WHERE l2.code >= b.bottom AND l2.code <= t.top);

Las dos consultas paralelas en la cláusula from generan valores que están respectivamente en la parte superior e inferior de un espacio en el rango de valores en la tabla. El producto cruzado de estas dos listas se restringe entonces para que la parte inferior no sea mayor que la parte superior, y tal que no haya ningún valor en la lista original entre la parte inferior y superior.

En los datos de muestra, esto produce el rango 4-6. Cuando agregué una fila adicional (9, 'nueve'), también generé el rango 8-8. Claramente, también tiene otros dos rangos posibles para una definición adecuada de 'infinito':

  • -infinity .. MIN(code)-1
  • MAX(code)+1 .. +infinity

Tenga en cuenta que:

  1. Si usa esto de manera rutinaria, generalmente no habrá muchos vacíos en sus listas.
  2. Las brechas solo pueden aparecer cuando elimina filas de la tabla (o ignora los rangos devueltos por esta consulta o sus parientes al insertar datos).
  3. Por lo general, es una mala idea reutilizar los identificadores, por lo que, de hecho, este esfuerzo probablemente esté equivocado.

Sin embargo, si quieres hacerlo, aquí hay una forma de hacerlo.

Respondida el 28/03/2009 a las 21:09
fuente por usuario

votos
0

Esta es la misma idea que Quassnoi ha publicado. Acabo de vincular todas las ideas en un código similar a T-SQL.

DECLARE
    series @table(n int)

DECLARE
    max_n int,
    i int

SET i = 1
-- max value in elements table
SELECT
    max_n = (SELECT MAX(code) FROM elements)

-- fill @series table with numbers from 1 to n
WHILE i < max_n BEGIN
    INSERT IGNORE  INTO @series (n) VALUES (i)

    SET i = i + 1
END

-- unassigned codes -- these without pair in elements table
SELECT
    n
FROM
    @series AS series
    LEFT JOIN
        elements
    ON
        elements.code = series.n
WHERE
    elements.code IS NULL

EDITAR: Esta es, por supuesto, solución no ideal. Si tiene muchos elementos o verifica un código que no existe a menudo, esto podría causar problemas de rendimiento.

Respondida el 25/03/2009 a las 17:51
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more