SQL. Deleting data from a table, but maintaining the relationship - sql

I work in PostgreSQL
I have two tables. The first is the product and the second is the receipt. They are referenced using a foreign key (the product in the receipt table). I need to delete the product row from the product table, but to keep the reference. I wanted to make it a "virtual table" (product) where the receipt table will reference after deleting information from the main product table.
But I can't figure out how to do it... Can someone tell me or show me how to do this.

You can't have a foreign key relationship to a non-existent row. So, do a soft-delete. That is, add a column to the products table such as is_deleted.
Then don't actually delete the row. Just set the column to true.
It can be helpful to have a view for active products:
create view v_products as
select p.*
from products p
where not is_deleted;
EDIT:
If you want to change the row in the original table, you can use a cascading option on the trigger. Use on delete cascade to remove the row in receipts or on delete set null to set the referencing value to NULL. I'm not a fan of these because you lose the original data.

Related

How to implement cascading deletes in one -> many *from the many side*

I have a use case where multiple rows in table A are aggregated down to a single row in table B. We represent the origin of rows in table B with a foreign key column in table A, saying "as a row, I contributed to X row in table B".
We want to find the best solution so that once every row from table A which contributed to table B has been deleted, deleted the row in table B as an orphan.
I'm not sure if there's some way to use ON DELETE CASCADE to handle this. But I'm guessing not and that maybe triggers are the best option.
I can't just purge all orphans on a schedule because the changes need to be persisted very soon after occurring.
Using the given schema, what our best option? Alternatively, is there some other schema that better sets us up for the scenario I gave?

Delete an item (row) from table but keep the unused ID number for a future insertion

I have a database with an "inventory" table which contains products from a shop. Each product is identified using an ID number set in the "ID" column of the table.
I want to be able to delete a product from the table, but keeping the deleted product's id number for future product insertions into the database.
As a demonstration I inserted 4 items and named all of them "test"
And just as an example I named the "deleted" product as "vacio" (empty in spanish) to show the one that i deleted.
Now, if want to add another product in the future, the id number 2 is unused and I want to add the product with that id number instead of 4 (following the given example).
The DELETE query is no good since it erases the id number as well so its a no go.
I thought about checking for the first row of the table that contains the value "vacio" and using the UPDATE query in all fields except id but this doesnt feel "classy" and is not very efficient as It should have to update values a lot of times.
Is there some nice way of doing this?
I would not actually recommend reusing old identifiers. For one, this prevents you from using the auto_increment feature, which mean that you need to manually handle the column for each and every insertion: this adds complexity and is not efficient. Also, it might cause integrity issues in your database if you have other tables referencing the product id.
But if you really want to go that way: I would go for the deletion option. If there are foreign keys referencing the column, make sure that they have the on delete cascade option enabled so data is properly purged from dependent tables when a product is dropped.
Then, you can fill the first available gap the next time your create a new product with the following query:
insert into products(id, categoria, producto)
select min(id) + 1, 'my new category', 'my new product'
from products p
where not exists (select 1 from products p1 where p1.id = p.id + 1)
You could have a new column ESTADO where you handle if a record is active (1) or inactive (0). Then, to obtain only "undeleted" records you just have to filter by the new column. That way, you also prevent changing the product name to "vacio", which might be useful in the future.

Delete from 2 tables using INNER JOIN

I have 3 tables.
InvoiceOriginal
Invoice
InvoiceHistory
the invoice table has a foreign key constraint.
Each entry in the invoice table has a corresponding entry in Invoiceoriginal.
The invoiceOriginal table stores the original values of the invoice and invoice table stores the values which have been modified by the user.
this is done to get diferrences at the time of submission.
the SQL I am using is
DELETE i
FROM invoice i
INNER JOIN InvoiceHistory aih
ON i.ClientId = aih.HistoryClientNumber
AND i.invoiceNumber = HistoryInvoiceNumber
however deletion is not possible because of foreign Key constraint.
The table is as under:
Invoice InvoiceOriginal InvoiceHistory
Id FK_InvoiceId ClientId
ClientId ClientId InvoiceNumber
InvoiceNumber
I need to delete the entry in invoice and InvoiceOriginal once there is an entry for that invoice number in InvoiceHistory for the same clientId.
You cannot issue a delete statement against more than one table at a time, you need to have individual delete statements for each of the related tables before deleting the parent record(s)
I'm fairly sure you can't delete from multiple tables with a single statement. I would normally delete the child rows first with one statement and then delete the parent record. You may wish to do this inside a transaction if you might need to roll back on failure.
Alternatively, you could enable CASCADE ON DELETE on the foreign key which would automatically cascade the deletions through the child records if that is something that is suitable for this system.
You can't delete the records from multiple table from a single query. But you have two methods to solve this
Delete all the related records from child or mapping table, and then
delete the Parent / header table record. (Multiple queries required here. Use SQL Transaction for a better control).
Or, Modify your foreign key constraint to ON DELETE CASCADE
Yes, YOU CAN, I did it right now:
DELETE T1,T2 FROM T1
INNER JOIN
T2 ON T2.FIELD = T1.FIELD
WHERE
T1.SOMETHING='SOMETHING'

Deleting using a join without where

I have two tables. adjustments and transactions. I want to delete all loan adjustments contained in transactions.
There are three corresponding fields to it. Date. ID. Value.
There are no fks. I used a delete join but it didn't work.
However when using a join it asks for me to specify data for trans.id. Trans.date. Trans.value. Is there anyway to forgo the where as I want to delete all of the related entries from adjustments in transactions.
If you want to delete all the row from Transactions table referencing in Adjustments table. There should be a column in Transactions table which references to some column in Adjustments table.
Suppose the ID column in your Transactions table References some ID column in Adjustments table then you would write this delete query something like this...
DELETE FROM Transactions
WHERE EXISTS ( SELECT 1
FROM Adjustments
WHERE ID = Transactions.ID)

How to configure reference to be deleted on parent table update?

I have two tables:
info: ID, fee_id
and
fee: ID, amount
and a reference between them (SQL Server 2008):
ALTER TABLE info WITH CHECK ADD CONSTRAINT FK_info_fee FOREIGN KEY(fee_id)
REFERENCES fee (ID)
ALTER TABLE info CHECK CONSTRAINT FK_info_fee
GO
How to configure this reference that way so a record in fee will be deleted if info.fee_id becomes NULL
EDIT: or maybe set info.fee_id to NULL on deleting the corresponding record in fee.
Anyway I can do it this way:
UPDATE info SET fee = NULL WHERE = ..
DELETE FROM fee WHERE ..
but I'm sure that this can be done by the database itself.
You probably don't want to do this. What would you expect to happen if multiple info rows referenced the same fee row?
If you really want to do something like this, adding logic to an AFTER UPDATE, DELETE trigger on the info table would probably be the way to go. Check if any other info rows reference that same fee row, and if not, delete the fee row.
Some thoughts:
If you have a one:one reference then can the 2 tables be combined?
Drilling up from child to parent is odd: if it's 1:1 then can you reverse the FK direction and simply CASCADE NULL?
Otherwise, you'll have to use a trigger but assuming 1:1 makes me uneasy...
... unless you have a unique constraint/index on info_fee.fee_id
Like so:
ALTER TABLE info WITH CHECK ADD
CONSTRAINT FK_fee_info_fee FOREIGN KEY (id) REFERENCES info_fee (fee_ID) ON DELETE SET NULL
If you really intend to remove rows when fee_id is set to null, one way is an update trigger. In an update trigger, the deleted table contains the old version of the updated rows, and the inserted table contains the new version. By joining them, you can take action when a fee_id changes to null:
CREATE TRIGGER deleteFee
ON info
FOR UPDATE
AS
DELETE FROM Fee
WHERE Fee.id IN (
SELECT old.fee_id
FROM deleted old
JOIN inserted new ON old.id = new.id
WHERE old.fee_id = fee.id
AND new.fee_id is null
)
This is tricky when multiple info rows refer to the same fee. The fee will be removed if any info row is set to null. A full synch trigger would avoid that:
CREATE TRIGGER deleteFee
ON info
FOR UPDATE
AS
DELETE FROM Fee
WHERE NOT EXISTS (
SELECT *
FROM Info
WHERE Fee.id = Info.fee_id
)
But this can have other unintended consequences, like deleting half the Fee table in response to an update. In this case, as in most cases, triggers add more complexity than they solve. Triggers are evil and should be avoided at almost any cost.