Delete with "Join" in Oracle sql Query - sql

I am not deeply acquainted with Oracle Sql Queries, therefore I face a problem on deleting some rows from a table which must fulfill a constraint which includes fields of another (joining) table. In other words I want to write a query to delete rows including JOIN.
In my case I have a table ProductFilters and another table Products joined on fields ProductFilters.productID = Products.ID. I want to delete the rows from ProductFilters having an ID higher or equal to 200 and the product they refer has the name 'Mark' (name is a field in Product).
I would like to be informed initially if JOIN is acceptable in a Delete Query in Oracle. If not how should I modify this Query in order to make it work, since on that form I receive an error:
DELETE From PRODUCTFILTERS pf
where pf.id>=200
And pf.rowid in
(
Select rowid from PRODUCTFILTERS
inner join PRODUCTS on PRODUCTFILTERS.PRODUCTID = PRODUCTS.ID
And PRODUCTS.NAME= 'Mark'
);

Recently I learned of the following syntax:
DELETE (SELECT *
FROM productfilters pf
INNER JOIN product pr
ON pf.productid = pr.id
WHERE pf.id >= 200
AND pr.NAME = 'MARK')
I think it looks much cleaner then other proposed code.

Based on the answer I linked to in my comment above, this should work:
delete from
(
select pf.* From PRODUCTFILTERS pf
where pf.id>=200
And pf.rowid in
(
Select rowid from PRODUCTFILTERS
inner join PRODUCTS on PRODUCTFILTERS.PRODUCTID = PRODUCTS.ID
And PRODUCTS.NAME= 'Mark'
)
);
or
delete from PRODUCTFILTERS where rowid in
(
select pf.rowid From PRODUCTFILTERS pf
where pf.id>=200
And pf.rowid in
(
Select PRODUCTFILTERS.rowid from PRODUCTFILTERS
inner join PRODUCTS on PRODUCTFILTERS.PRODUCTID = PRODUCTS.ID
And PRODUCTS.NAME= 'Mark'
)
);

Use a subquery in the where clause. For a delete query requirig a join, this example will delete rows that are unmatched in the joined table "docx_document" and that have a create date > 120 days in the "docs_documents" table.
delete from docs_documents d
where d.id in (
select a.id from docs_documents a
left join docx_document b on b.id = a.document_id
where b.id is null
and floor(sysdate - a.create_date) > 120
);

Personally, I would use the EXISTS construct. As described in the examples on this web page:
DELETE ProductFilters pf
WHERE EXISTS (
SELECT *
FROM Products p
WHERE p."productID"=pf."productID"
AND p.NAME= 'Mark'
)
AND pf."id">=200;

Please use a subquery
delete from productfilters
where productid in (Select id from products where name='Mark') and Id>200;

Related

update statement main table based on records in temp all types of an order when a product present in temp table

I have to update status to all items of an product type for each order_id if even a single item of a product type of an order is present in temp table . Below are 3 tables
product table
temp table
main table
I'm updating status with below statement in main table for all items of a product_type for an order_id if even a single item of a product_type of an order is present in temp table
update main_table t
set status='inactive'
from ( select i.order_id,
pt.type
from temp_table i
inner join product_table pt on i.product_id=pt.product_id
where i.key is not null ) as a1
inner join ( select mt.order_id,
mt.key,pt1.type
from main_table mt
inner join product_table pt1 on mt.product_id=pt1.product_id) a2
on a1.order_id=a2.order_id
and a1.type=a2.type
where t.key=a2.key
expected output would be
main table has 10 million records and temp table may have 2K records. I think above query is complex as it has 2 subquery and joins.
Is there a way can we rewrite query to improve query execution ?
Your query is a bit too complex to simplify too much further due to needing to join product more than once to obtain all product_id's of the same product_type. However, we can rework your a2 query into the main UPDATE statement, thereby eliminating at least 1 join against main.
update main mt
set prodstatus = 'inactive'
from product p
inner join (select i.order_id,
pt.prodtype
from temp i
inner join product pt on i.product_id=pt.product_id
where i.pk is not null) as tempProds
on p.prodtype = tempProds.prodType
where mt.product_id = p.product_id
and mt.order_id = tempProds.order_id
This fiddle shows you the execution advantage if you run our UPDATE statements separately using the EXPLAIN option.

Deleting data from one table if the reference doesn't exist in two other tables

I managed to import too much data into one of my database tables. I want to delete most of this data, but I need to ensure that the reference doesn't exist in either of two other tables before I delete it.
I figured this query would be the solution. It give me the right result on a test database, but in the production environment it returns no hits.
select product
from products
where 1=1
and product not in (select product from location)
and product not in (select product from lines)
You are getting no results/hits it means that you table location and/or lines having the null values in the product column. in clause failed if column having null value.
try below query just added the null condition on the top of your shared query.
select product from products
where 1=1
and product not in ( select product from location where product is not null)
and product not in ( select product from lines where product is not null)
Use EXISTS instead of IN which is more efficient
DELETE FROM products WHERE
NOT EXISTS
(
SELECT
1
FROM [Location]
WHERE Product = Products.Product
) AND
NOT EXISTS
(
SELECT
1
FROM lines
WHERE Product = Products.Product
)
Try this..
DELETE FROM Products where not exists
(select 1 from Location
join lines on lines.Product = Location.Product
and Location.Product = Products.Product
);
It's difficult to tell from your post why the query would return results in the test database but not production other than there is different data or different structures. You might try including the DDL for the participating tables in your post so that we know what the table structures are. For example, is the "product" column a PK or a text name?
One thing that does jump out is that your query will probably perform poorly. Try something like this instead: (Assuming the "product" column is a PK in Products and FK in the other tables.)
Select product
From Products As p
Left Outer Join Location As l
On p.product = l.product
And l.product is null
Left Outer Join Lines as li
On p.product = li.product
And li.product is null;
This simple set based approach may help ...
DELETE p
FROM products p
LEFT JOIN location lo ON p.product = lo.product
LEFT JOIN lines li ON p.product = li.product
WHERE lo.product IS NULL AND li.product IS NULL

How can I append records that don't already exist in a table from a cartesian product query?

My current query returns every possible outcome from 2 tables and inserts them into a junction table.
My current query looks like this:
INSERT INTO tblJunction_Courses_Software ( CourseID, SoftwareID )
SELECT tblCourses.CourseID, tblSoftware.SoftwareID
FROM tblSoftware, tblCourses
WHERE (((tblSoftware.Exclude)=No));
How can I add an additional clause to make sure that duplicate data is not appended every time the query is run?
I think you can do what yo want with a not exists clause:
INSERT INTO tblJunction_Courses_Software ( CourseID, SoftwareID )
SELECT c.CourseID, s.SoftwareID
FROM tblSoftware as s CROSS JOIN tblCourses as c
WHERE s.Exclude = No AND
NOT EXISTS (SELECT 1
FROM tblJunction_Courses_Software as cs
WHERE cs.CourseId = c.CourseId AND
cs.SoftwareId = s.SOftwareId
);

How to select records which don't exist in another table or have a different status?

I am trying to select records from a temp table based on another temp table which holds their previous statuses (StatusHistory).
So if the record doesn't exist in the status table, then it should be selected. If the status of the record is different than the one in the StatusHistory table, then the record should be selected. Otherwise, if it exists with the same status in the StatusHistory table, then it should be ignored.
I have this SQL but it doesn't seem to be the best solution. Can you please point me to a better way to achieve that assuming that there are thousands of records in the tables? Would it be possible to achieve the same result with a JOIN statement?
SELECT AI.item
FROM #AllItems AI
WHERE NOT EXISTS (
SELECT * FROM #StatusHistory HS
WHERE HS.itemId = AI.itemId
) OR NOT AI.itemStatus IN ( SELECT HS.itemStatusHistory
FROM #StatusHistory HS
WHERE HS.itemId = AI.itemId
AND HS.itemId = AI.itemId )
Yes, you can do this with a LEFT JOIN.
SELECT AI.item
FROM #AllItems AI
LEFT JOIN #StatusHistory HS ON AI.itemId = HS.itemId
AND AI.itemStatus = HS.itemStatusHistory
WHERE HS.itemId IS NULL
A better solution, however, is to use NOT EXISTS:
SELECT AI.item
FROM #AllItems AI
WHERE NOT EXISTS
(
SELECT 1 FROM #StatusHistory SH
WHERE SH.itemId = AI.itemId
AND SH.itemStatusHistory = AI.itemStatus
);
As pointed out by Aaron, this usually performs better than a LEFT JOIN.

How to return rows matched in a table without multiple EXISTS clauses?

I want to pull back results from one table that match ALL specified values where the specified values are in another table. I can do it like this:
SELECT * FROM Contacts
WHERE
EXISTS (SELECT 1 FROM dbo.ContactClassifications WHERE ContactID = Contacts.ID AND ClassificationID = '8C62E5DE-00FC-4994-8127-000B02E10DA5')
AND EXISTS (SELECT 1 FROM dbo.ContactClassifications WHERE ContactID = Contacts.ID AND ClassificationID = 'D2E90AA0-AC93-4406-AF93-0020009A34BA')
AND EXISTS etc...
However that falls over when I get up to about 40 EXISTS clauses. The error message is "The query processor ran out of internal resources and could not produce a query plan. This is a rare event and only expected for extremely complex queries or queries that reference a very large number of tables or partitions. Please simplify the query."
The gist of this is to
Select all contacts with any GUID from the IN statement
Use a DISTINCT COUNT to get a count for each contactid on matching GUID's
Use the HAVING to retain only those contacts that equal the amount of matching GUID's you've put into the IN statement
SQL Statement
SELECT *
FROM dbo.Contacts c
INNER JOIN (
SELECT c.ID
FROM dbo.Contacts c
INNER JOIN dbo.ContactClassifications cc ON c.ID = cc.ContactID
WHERE cc.ClassificationID IN ('..', '..', 38 other GUIDS)
GROUP BY
c.ID
HAVING COUNT(DISTINCT cc.ClassificationID) = 40
) cc ON cc.ID = c.ID
Test script at data.stackexchange
One solution is to demand that no classification exists without a matching contact. That's a double negation:
select *
from contacts c
where not exists
(
select *
from ContactClassifications cc
where not exists
(
select *
from ContactClassifications cc2
where cc2.ContactID = c.ID
and cc2.ClassificationID = cc.ClassificationID
)
)
This type of problem is known as relational division.
SELECT c.*
FROM Contacts c
INNER JOIN
(cc.ContactID, COUNT(DISTINCT cc.ClassificationID) as num_class
FROM ContactClassifications
WHERE ClassificationID IN (....)
GROUP BY cc.ContactID
) b ON c.ID = b.ContactID
WHERE b.num_class = [number of distinct values - how many different values you put in "IN"]
If you run SQLServer 2005 and higher, you can do pretty much the same with CROSS APPLY, supposedly more efficiently