Delete unmatched records in Access - sql

I have a table in an Access database where records may be referenced from either of two other tables. I need to delete records from that table if they are not referenced by either of the others.
My best solution so far has been to create a subquery to return the id's of the referenced records and to refer to the subquery from a delete query. (The subquery has to be separate because Access does not allow UNION in nested subqueries.)
So ...
SelectQuery:
SELECT TableB.id FROM TableB INNER JOIN TableA ON TableB.id = TableA.id
UNION
SELECT TableC.id FROM TableC INNER JOIN TableA ON TableC.id = TableA.id
DeleteQuery:
DELETE * FROM TableA WHERE id NOT IN (SELECT * FROM SelectQuery)
This is excruciatingly slow ... there must be a better way?
I was trying to avoid having to add a boolean 'Used' field to TableA ...

#Matthew PK suggests using two NOT IN subqueries, which is theoretically a good idea, but as I observed in a comment, NOT IN and NOT EXISTS are poorly optimized by Jet/ACE and will often not use the indexes on both sides of the comparison. I'm wondering whether or not subqueries are necessary or not:
DELETE *
FROM (TableA LEFT JOIN TableB ON TableA.ID = TableB.ID) LEFT JOIN TableC ON TableA.ID = TableC.ID
WHERE TableB.ID Is Null AND TableC.ID Is Null;
This would definitely use your indexes. If a subquery is necessary, you could replace TableB and TableC with the relevant subqueries.

Why not something like this:
DELETE FROM TableA
WHERE
id NOT IN (SELECT id FROM TableB)
AND
id NOT IN (SELECT id FROM TableC)
?

Is it acceptable to create a new table based on your SelectQuery, delete the original table, and rename the new one to the original name?

/* delete more records */
DELETE FROM table1
WHERE NOT EXISTS
(SELECT field FROM table2 WHERE table2.field = table1.field)

Related

What is the correct way of writing an exclusive left-join on two tables?

Do I need to write condition 'is NULL' for two (or could be more) keys when doing an exclusive left join of two tables? The question is if the 'WHERE B.columnName1 IS NULL' is enough?
Join with one key condition:
SELECT columns
FROM TableA
LEFT OUTER JOIN TableB
ON A.columnName = B.columnName
WHERE B.columnName IS NULL
Is the following more accurate? Do I need condition for columnName2?
SELECT columns
FROM TableA
LEFT OUTER JOIN TableB
ON A.columnName1 = B.columnName1
AND A.columnName2 = B.columnName2
WHERE B.columnName1 IS NULL
AND B.columnName2 IS NULL
So far it worked with one condition but I just got confused, I see this is simple question, sorry for that
Single column is enough(as long as it is not nullable):
SELECT columns
FROM TableA A
LEFT OUTER JOIN TableB B
ON A.columnName = B.columnName
WHERE B.columnName IS NULL;
Best practice is to use primary key for tableB
The ON clause contains the condition for the join:
ON A.columnName = B.columnName
If this condition is not met then the result is that all columns of the unmatched row of table B will be NULL, so your 2nd query is not wrong.
But, this also means that B.columnName itself is NULL and this condition only in the WHERE clause is enough.
It simply means that in this row A.columnName has no matching B.columnName.
The best practice is to use a column that is part of the ON clause, which your code is doing:
SELECT columns
FROM TableA A LEFT OUTER JOIN
TableB B
ON A.columnName = B.columnName
WHERE B.columnName IS NULL;
The JOIN does not treat NULL values as equal. Equally, the primary key (or any component of the primary key) also works fine.
If you don't want to think about this, use NOT EXISTS:
SELECT columns
FROM TableA A
WHERE NOT EXISTS (SELECT 1
FROM TableB B
WHERE A.columnName = B.columnName
);

Join equivalent query

I have a simple enough query that will be used as part of a SP to clean the database from time to time. TableA and TableB are related with a ID. I build a table variable with all the IDs in TableA and then delete all the one that appear in TableB. This sintax works just fine:
DELETE #TableIds
FROM #TableIds AS tids
WHERE tids.sharedID IN (SELECT tb.sharedID FROM TableB AS tb WITH (nolock))
How can this query be done with a JOIN instead of a IN (subquery)?
You don't need a table variable to do this. Use delete with a join so the matched id records can be deleted.
delete a
from tablea a
join tableb b on a.id=b.id
Try like below
DELETE Tids
FROM #TableIds AS tids
INNER JOIN TABLEb TB(Nolock) ON TIDS.SHAREID= TB.SHAREId

Join of Two Tables where Data Matches in One Column

For some reason I have a hard time grasping joins and this one should be very simple with the knowledge that I have in SQL.
Anyway, I have 2 tables. We will call them TableA and TableB. One of the columns in TableA is "ID". TableB only consists of the column "ID". I want to return all rows in TableA whose ID is present in TableB.
I know this should be very simple to figure out, but my brain doesn't want to work today.
You can do this using an EXISTS:
Select A.*
From TableA A
Where Exists
(
Select *
From TableB B
Where A.Id = B.Id
)
You can also use a JOIN if you wish, but depending on your data, you may want to couple that with a SELECT DISTINCT:
Select Distinct A.*
From TableA A
Join TableB B On A.Id = B.Id
One thing to keep in mind is that the ID of TableA is not necessarily related to the ID of TableB.
this should work
SELECT B.ID
FROM TableA A
JOIN TableB B
ON (A.ID=B.ID)
WHERE A.ID=B.ID
You can also use IN operator like this:
Select *
From TableA
Where ID in
(
Select distinct ID
From TableB
)

SQL Delete based on condition in join

It is possible to delete records based on a satisfied condition with a join query?
For instance, I have a linking table joining 3 records. The query I have at the moment deletes records from this table where one of the id's isn't IN() an imploded Php array. I've come to realise that the query should only remove records from this table if the id's don't exist in the array and they belong to a certain other table based on the a link to another table.
For SQL Server, the command is slightly different:
DELETE FROM TableA
FROM TableA LEFT OUTER JOIN TableB ON TableA.Column = TableB.Column
WHERE TableB.Column IS NULL
No, that's not a typo, yes, you do need "FROM TableA" twice. At least, you need the second FROM (the first is optional). The following has the advantage that it works for both SQL Server and MySQL:
DELETE TableA
FROM TableA LEFT OUTER JOIN TableB ON TableA.Column = TableB.Column
WHERE TableB.Column IS NULL
I like to use EXISTS clauses for this:
DELETE FROM TableA
WHERE
<<put your array condition here>>
AND NOT EXISTS
(SELECT 1 FROM TableB Where TableB.ID=TableA.ID)
You can use :
DELETE Based on a Join:
DELETE A
FROM TableA AS A
LEFT OUTER JOIN TableB As B ON A.Id = B.TabaleAId
WHERE B.Column IS NULL
Delete With SubQuery:
DELETE
FROM TableA AS A
Where
A.id not in ( Select B.TabaleAId From Tab;eB As B )
or
DELETE FROM TableA
WHERE Not EXISTS
(
SELECT *
FROM TableB As B
Where B.TableAId = TableA.Id
)
DELETE Using Table Expressions:
With A
As
(
Select TableA.*
FROM TableA AS A
LEFT OUTER JOIN TableB As B ON A.Id = B.TabaleAId
WHERE B.Column IS NULL
)
Delete From A
DELETE FROM TableA
LEFT OUTER JOIN TableB
WHERE TableB.Column IS NULL
Will delete the records in tableA that don't have a corresponding record in TableB. Is that like what you are after?
DELETE FROM a
FROM TableA AS a LEFT OUTER JOIN TableB AS b
on a.CALENDAR_DATE = b.CALENDAR_DATE AND a.ID = b.ID
Where b.ID is null
You can first use the select statement and verify your records that you want to delete and then remove the select statement and add Delete FROM tablename with the above query syntax.
The easiest way to Delete based on join is as follow:
1.Write your query using SELECT statement instead of DELETE statement
SELECT COLUMNS
FROM Table1
INNER JOIN Table2 ON Table1.YYY = Table2.XXX
2.Replace SELECT COLUMNS with DELETE FROM TABLE
DELETE FROM Table1
FROM Table1
INNER JOIN Table2 ON Table1.YYY = Table2.XXX
Note that we need to specify FROM twice, one for DELETE part and one for JOIN part.
delete from TableA
where id in
(
select id from TableA
except select id from TableB
)
Which means "delete from tableA where id in table a but not in table b)
Otherwise a Merge statement might help you (when matched/not matched delete etc)
http://technet.microsoft.com/en-us/library/bb510625.aspx

How do I get all records from tableA with a left outer join and a where condition on tableB?

Basically, what I want is if there is a record in tableB of type 'X' I want to see it, otherwise I don't, but I want all records from tableA.
I know I could accomplish this by putting the tableB.type = 'X' in the LEFT OUTER JOIN ON clause, but I can't do that because I'm limited to using only the where condition because I'm using a restricted query manager of a program I won't name, but I definitely hate. :)
SELECT *
FROM tableA
LEFT OUTER JOIN ON tableA.ID = tableB.ID
WHERE tableB.type = 'X'
How do I accomplish this?
EDIT
I've tried this, but I still don't get all records from tableA. I'm testing this on SQL server to avoid waiting for long periods for my query to run on the production system. I'm pretty sure the production system is using Oracle if that helps.
SELECT *
FROM tableA LEFT OUTER JOIN ON tableA.ID = tableB.ID
WHERE tableB.type = 'X' OR tableB.type IS NULL
Check for nulls in tableB:
SELECT *
FROM tableA LEFT OUTER JOIN ON tableA.ID = tableB.ID
WHERE tableB.type = 'X'
OR tableB.type IS NULL
That will get you everything from both tables when the join matches, and everything from tableA when there's no corresponding record in tableB.
If type can be null naturally, you'll want to change the condition to something more sound:
SELECT *
FROM tableA LEFT OUTER JOIN ON tableA.ID = tableB.ID
WHERE tableB.type = 'X'
OR tableB.ID IS NULL
Assuming ID is the primary key and cannot be null naturally, that will get the same result.
Don't know if you have access to the database, or if you have to query tableB specifically due to some other restriction, but you could always create a view of table b called tableBTypeX where the view is restricted to only those rows with type = x. Then you could left outer join against tableBTypeX . In your query, the join columns are ID columns, so they probably have indexes, making the query fine in terms of speed. In the case where the join columns are not indexed, joining against the view would be more efficient, because fewer rows are joined against, and joining against unindexed rows usually requires a full table scan, making it a much more time consuming query.
You can join against the "type X" records from tableB exclusively by amending the join condition:
SELECT
*
FROM
tableA
LEFT OUTER JOIN ON
tableA.ID = tableB.ID
AND tableB.type = 'X'
Is UNION possible?
SELECT *
FROM tableA
LEFT OUTER JOIN ON tableA.ID = tableB.ID
WHERE tableB.type = 'X'
UNION
SELECT *
FROM tableA
... or a CTE? Not sure how the name tableB would resolve though and can't test...
;WITH tableB AS
(
SELECT * FROM tableB WHERE type = 'X'
)
SELECT *
FROM
tableA
LEFT OUTER JOIN
tableB ON tableA.ID = tableB.ID