2010-07-22 23 views
32

Tengo dos tablas (tareas y Timeentries), que están conectados por una clave externa (TimeEntries.TaskID referencia Tasks.ID)manera elegante para eliminar filas que no se hace referencia por otra tabla

Ahora me gustaría para eliminar todas las filas de Tareas a las que no hace referencia la tabla TimeEntries. Pensé que esto debería funcionar:

DELETE FROM Tasks WHERE ID not IN (SELECT TaskID FROM TimeEntries) 

sino que afecta a 0 filas, a pesar de que hay una gran cantidad de filas no referenciados en la tabla de tareas.

¿Cuál podría ser el problema aquí? Por supuesto, podría escribir un SP que itere todas las filas, pero parece que esto podría hacerse en un trazador de líneas.

supongo que este es uno de esos errores de subdesbordamiento sleeptime. ¡Por favor ayuda!

+2

¿Usted consigue los resultados esperados si sólo ejecuta la subconsulta SELECT en su propio? – JNK

+0

@ J-N-K: sí, lo hice. –

Respuesta

49

Hay una Gotcha notorio para not in. Básicamente, id not in (1,2,3) es la abreviatura de:

id <> 1 and id <> 2 and id <> 3 

Ahora bien, si su mesa TimeEntries contiene ninguna fila con un TaskID de null, la not in se traduce en:

ID <> null and ID <> 1 and ID <> 2 AND ... 

El resultado de una comparación con null siempre es unknown . Como unknown no es verdadero en SQL, la cláusula where filtra todas las filas y usted no elimina nada.

Una solución fácil es un adicional de que la cláusula de la subconsulta:

DELETE FROM Tasks 
WHERE ID not IN 
     (
     SELECT TaskID 
     FROM TimeEntries 
     WHERE TaskID is not null 
     ) 
+0

¿Tiene alguna sugerencia de cómo evitar esto? – DOK

+0

@DOK: Claro, se agregó una forma a la respuesta. SQLMenace publicó otra buena – Andomar

+0

Excelente respuesta. ¡Muchas gracias! –

3
Delete FROM Tasks 
     WHERE not Exists 
      (SELECT 'X' FROM TimeEntries where TimeEntries.TaskID = Tasks.ID) 

El SQL de arriba debe eliminar todas las Filas de las Tareas donde Task.ID no existe en la Tabla de Entradas de Tiempo. Yo corría como una instrucción SELECT primero en probar :)

9

Dado que está ejecutando SQL Server 2008, puede utilizar la sintaxis ingeniosa nueva combinación.

MERGE Tasks AS target 
USING TimeEntries as Source ON (Target.TaskID=Source.TaskID) 
WHEN NOT MATCHED BY Source THEN DELETE; 
+1

+1: Un exceso de exceso en este caso, pero sin duda es útil saber sobre el comando de fusión. Ni siquiera sabía que algo así existía. –

+1

¿Por qué crees que es excesivo? Está hecho para tareas como esta (hacer coincidir filas entre tablas). – JohnFx

+1

¿Qué hay de su rendimiento en comparación con las soluciones anteriores? ¿Tiene alguna ventaja o es esta la manera admitida de hacer tales acciones? Thx –

3

Sé que esto es viejo, pero me pregunto por qué nadie menciona una consulta de eliminación como se describe here. Por lo tanto, como referencia:

DELETE FROM Tasks 
FROM Tasks LEFT OUTER JOIN 
    TimeEntries ON TimeEntries.TaskID = Tasks.ID 
WHERE TimeEntries.TaskID IS NULL; 

Esta sintaxis no es compatible con la norma ISO, por lo que sólo funcionará para T-SQL.

Cuestiones relacionadas