4

Is there an efficient way to delete records from SQL Server without using the keyword IN in the below scenario:

-- If Documents were deleted, remove them from your ocean
DELETE FROM IndexHistory 
WHERE DocId IN (SELECT IH.Docid 
                FROM IndexHistory IH 
                LEFT JOIN IndexedLineItems I ON IH.Docid = I.DocId
                WHERE I.Docid IS NULL)

If you study this scenario, you have all the records you want to delete from the query inside the parenthesis.

It is documented fact that using 'IN' is inefficient. So one would think that there should be a way to delete these without the 'IN', because all your records are identify-able without using the "IN".

So for example, I am thinking that something like this might be possible, but I don't have the right syntax:

DELETE FROM IndexHistory IH 
LEFT JOIN IndexedLineItems I ON IH.Docid = I.DocId
WHERE I.Docid IS NULL

I appreciate the answer may still be "no, it's not possible."

If it is not possible, maybe it can be a possible suggestion to improve the language for Microsoft. Before I do that, I thought I would post here to see if I'm missing something.

halfer
  • 19,824
  • 17
  • 99
  • 186
Heap of Pinto Beans
  • 677
  • 2
  • 12
  • 23
  • 3
    Don't you mean this: https://stackoverflow.com/questions/16481379/how-to-delete-using-inner-join-with-sql-server – Konrad Z. Oct 09 '18 at 15:47
  • Marc, not exactly, but it is related and a good find, and helpful for me to post an additional possible answer (mine is not an inner join, but a left join). Some of the answers on bottom also seem to be good. – Heap of Pinto Beans Oct 09 '18 at 16:14

4 Answers4

6

Use not exists:

Delete ih from IndexHistory ih
    where not exists (select 1 from IndexedLineItems ili where ih.Docid = ili.DocId)
Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
5

I prefer CTEs for readability reason, it can also be changed to use a SELECT to see what i'm going to delete. This NOT EXISTS might also be more efficient:

WITH DeleteData AS
(
  SELECT IH.Docid 
  FROM IndexHistory IH 
  WHERE NOT EXISTS( SELECT 1 FROM IndexedLineItems I
                    WHERE I.DocId = IH.DocId)
)
DELETE FROM DeleteData

You don't need an additional JOIN from this CTE to the table because only one table is selected.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • this seemed to have worked. i've never seen this before (With...). I will keep the floor open for now, incase there are additional answers. – Heap of Pinto Beans Oct 09 '18 at 16:05
  • 1
    @Kam: it's a [common-table-expression](https://learn.microsoft.com/en-us/sql/t-sql/queries/with-common-table-expression-transact-sql?view=sql-server-2017). No matter how complex the CTE itself is, you can always write `SELECT * FROM DeleteData` to see what you will delete. Then it might also be useful to add more columns in the CTE(or simply `SELECT IH.* ...`). Its not necessary to select only the Docid. – Tim Schmelter Oct 09 '18 at 16:19
1

In a different thinking, use inner join instead, so you only delete the matches:

DELETE FROM IndexHistory IH 
JOIN IndexedLineItems I ON IH.Docid = I.DocId
WHERE I.Docid IS NOT NULL
MEdwin
  • 2,940
  • 1
  • 14
  • 27
0

This works fine...

DELETE IH
    FROM IndexHistory IH
    Left JOIN IndexedLineItems LI
      ON IH.Docid = LI.DocId
    WHERE LI.DocId is Null
MEdwin
  • 2,940
  • 1
  • 14
  • 27
Heap of Pinto Beans
  • 677
  • 2
  • 12
  • 23