In general, one DELETE statement can change data only in one table.
So, in general, you need two separate DELETE statements to delete rows from two tables.
Technically, you don't have to write BId to a temp table, just read them twice from TableB.
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
DELETE FROM TableA
WHERE TableA.AId IN
(
SELECT TOP(1000) TableB.BId
FROM TableB
ORDER BY TableB.BId
)
;
DELETE FROM TableB
WHERE TableB.BId IN
(
SELECT TOP(1000) TableB.BId
FROM TableB
ORDER BY TableB.BId
)
;
COMMIT TRANSACTION;
You need to make sure that the set of 1000 BId are the same in both queries, which means that you have to use ORDER BY with TOP. Also, you should do something to prevent changes to TableB by another process between two DELETE statements.
In the end, taking care of concurrency issues (for example, by setting the transaction isolation level to serializable) may incur more overhead than writing these 1000 Ids into a temp table.
On the other hand, you said "BId is actually is a subset of AId". So, TableA is master, TableB is detail.
Technically, you can define a Foreign Key with ON DELETE CASCADE option. It is not clear to me from the question why you can't use foreign key constraint.
ALTER TABLE TableB WITH CHECK ADD CONSTRAINT [FK_TableB_TableA] FOREIGN KEY(BId)
REFERENCES TableA(AId) ON DELETE CASCADE
GO
ALTER TABLE TableB CHECK CONSTRAINT [FK_TableB_TableA]
GO
Then you will delete only from TableA and child rows from TableB would be deleted by the foreign key constraint. Whether that would be more efficient than two explicit DELETEs is hard to tell - you need to test yourself on your hardware.
In this case one explicit DELETE is enough:
DELETE FROM TableA
WHERE TableA.AId IN
(
SELECT TOP(1000) TableB.BId
FROM TableB
ORDER BY TableB.BId
)
;
This will take care of concurrency issues as well.