I have two tables, Table1 and Table2 with same primary key (FKTestID).
If I want to delete one row in Table1 and same FKTestID are in Table2 it will not work. You can only delete a row from Table1 if Table1.FKTestID not equals any FKTestID in Table2.
Please help me with this constraint?
You need to set the constraint to cascade on delete.
You can do this through SQL management studio by modifying the constraint.
Or you can do this via SQL when you created the constraint by including ON DELETE CASCADE at the end
You could also do it with the ALTER TABLE command.
Here is a code sample implementing what Simon suggested above.
CREATE TABLE dbo.Table1 (
FKTestID int NOT NULL PRIMARY KEY
)
GO
CREATE TABLE dbo.Table2 (
FKTestID int NOT NULL PRIMARY KEY
)
GO
ALTER TABLE dbo.Table2
ADD CONSTRAINT FK_Table2_FKTestID
FOREIGN KEY (FKTestID)
REFERENCES dbo.Table1 (FKTestID)
ON DELETE CASCADE
GO
INSERT INTO dbo.Table1 VALUES (1)
INSERT INTO dbo.Table2 VALUES (1)
INSERT INTO dbo.Table1 VALUES (2)
INSERT INTO dbo.Table2 VALUES (2)
DELETE FROM dbo.Table1 WHERE FKTestID = 1
SELECT 'Table1' AS [Table], * FROM dbo.Table1
SELECT 'Table2' AS [Table], * FROM dbo.Table2
=============================================
Table FKTestID
------ -----------
Table1 2
Table FKTestID
------ -----------
Table2 2
Note that I agree with Mitch Wheat's comment about CASCADE DELETE being dangerous. This feature is interesting, but I have never, ever used it in a production system.
If you are asking how to get rid of the constraint so that you can delete, DO NOT consider doing that. The constraint is there for a reason. If you don't know the reason, don't delete it.
Others have suggested adding a cascade delete. I suggest that this is poor idea as you can cause performance problems on the database. It is better to write a script that deletes the records in the correct order. In this case you need to delete the matching records from tables2 first, then run the delete on table 1.
You also need to evaluate the data in table2 before deciding to delete from it either by script or by cascade delte. If you should not be deleting those records from table2, then you should not be delting the records from table1 (by removing the constraint) as this will casue the table 2 records to be orphaned and you will lose the integrity of the data (which should be your first concern in any database actions (security and performance being a very close second and third)).
Let me give you a scenario where the data in table 2 would indicate you should not delete the record. Suppose you have a customer table and and order table. You want to delete customer A, but he has an order in the past. If you delete both records, you will mess up all the accounting information on orders. If you delete the customer but not the order (by eliminating the constraint), then you have an order that you can no longer tell who it was sent to. The correct thing to do in a case like this is to have an ISactive file in the customers table and mark him as an inactive customer. You would of course need to redesign the code that searches for customer information to make sure it includes the flag to only select active customers which is why this sort of thing should be thought out at the beginning of development not later (one reason why it is worth your while to hire database specialists for the design phase as many application developers don't consider maintaining the data over time as part of their design process).
Related
I am trying to keep integrity in a MEMORY OPTIMIZED table I have. In that table is a foreign key (uniqueidentifier) that points to another table and an Active flag (bit) denoting whether the record is active or not.
I want to stop inserts from happening if the incoming record has the same foreign key as an existing record, only if the existing record is active (Active = 1).
Because this is a memory optimized table, I am limited in how I can go about this. I have tried creating a unique index and discovered they are not allowed in memory optimized tables.
UPDATE:
I ended up using a stored procedure to solve my problem. The stored procedure will do the check for me prior to the insert or update of a record.
Most folks get around the limitations of In-Memory table constraints using triggers. There are a number of examples listed here:
https://www.mssqltips.com/sqlservertip/3080/workaround-for-lack-of-support-for-constraints-on-sql-server-memoryoptimized-tables/
Specifically for your case this will mimic a unique constraint for insert statements but the poster has examples for update and delete triggers as well in the link above.
-- note the use of checksum to make a single unique value from the combination of two columns
CREATE TRIGGER InMemory.TR_Customers_Insert ON InMemory.Customers
WITH EXECUTE AS 'InMemoryUsr'
INSTEAD OF INSERT
AS
SET NOCOUNT ON
--CONSTRAINT U_OnDisk_Customersg_1 UNIQUE NONCLUSTERED (CustomerName, CustomerAddress)
IF EXISTS (
-- Check if rows to be inserted are consistent with CHECK constraint by themselves
SELECT 0
FROM INSERTED I
GROUP BY CHECKSUM(I.CustomerName, I.CustomerAddress)
HAVING COUNT(0) > 1
UNION ALL
-- Check if rows to be inserted are consistent with UNIQUE constraint with existing data
SELECT 0
FROM INSERTED I
INNER JOIN InMemory.tblCustomers C WITH (SNAPSHOT)
ON C.ChkSum = CHECKSUM(I.CustomerName, I.CustomerAddress)
)
BEGIN
;THROW 50001, 'Violation of UNIQUE Constraint! (CustomerName, CustomerAddress)', 1
END
INSERT INTO InMemory.tblCustomers WITH (SNAPSHOT)
( CustomerID ,
CustomerName ,
CustomerAddress,
chksum
)
SELECT NEXT VALUE FOR InMemory.SO_Customers_CustomerID ,
CustomerName ,
CustomerAddress,
CHECKSUM(CustomerName, CustomerAddress)
FROM INSERTED
GO
What's the difference between truncate, drop and delete of tables? And when to choose for which? Does anyone has a quick comparison? I've seen a lot of info about this, but haven't found it in a clear overview yet. I hope this post helps in the understanding.
I mean like being used in these statements in t-sql:
truncate table TableX
drop table TableX
delete table_name
Based on an answer by #Michal here and some more searching I made a comparison beneath for the following statements (in t-sql): truncate table TableX, drop table TableXand delete table_name.
Truncate Drop Delete
Speed [Fast] Slow Slowest
Rolback possibility No No [Yes]
Specifiable conditions No No [Yes]
Scope All records All record+Headers Some records/All records
=whole table
Cascading effects No* No* [Yes]**
**For example: in a Table_1 there is a PK, in Table_2 there is a FK that relates with
the PK of Table_1, other words there is referential integrity. If the PK has `'ON DELETE CASCADE'`
and `delete Table_1` is ordered, then the data in Table_2 will be deleted too,
automatically. For more info about ON DELETE CASCADE and ON ALTER CASCADE, see:
https://technet.microsoft.com/en-us/library/ms186973%28v=sql.105%29.aspx.
Cascading does automatic alterations and deletes of depending objects such as foreign keys (FK),
views, and triggers. Sometimes very useful, sometimes very dangerous..
*The drop and truncate statements of a Table_1 (with PK and FK in Table_2, as decribed
in **) can't be executed, because the ssdms prohibits that. To accomplish the truncation
or dropping of a Table_1: first get rid of the FK in Table_2, by altering the table design, or
by dropping table2.
See the comparison to base the decision when to use which statement...
As a thumb:
If you want to get rid of only records:
use delete when a conditional deleting is required, use truncate when all records may be get rid of. When you want to be able to rollback then use delete.
If you want to get rid of the whole table, including the headers (columns with settings) then choose drop.
If you want to get rid of values and automatically the related objects (and cascading is defined in the table), use delete. (PS: in other dialects it seems there are ways to accomplish it even when the table is not designed with cascading, but as far as I know there isn't in t-sql/msss; but correct me if I'm wrong)
PS: if you want to alter or delete the preferences of a column, then use (in t-sql dialect):
Alter:
alter table tableX
alter columnX datatypeX
Delete:
alter table tableX
drop column columnX
--And here's some code to play with
--table to truncate, drop or delete
create table TableX(
[Name] [nchar](25) null,
[ID_Number] [int] not null)
--tables with PK and FK
create table Table_1(
[Name] [nchar](25) null,
[ID_Number] [int] not null primary key)
create table Table_2(
[ID_Number] int not null foreign key references Table_1(ID_Number) on delete cascade,
[Buys] [int] null)
--the on delete cascade make it happen that when a ID_Number is Table_1 is deleted, that row
is automatically deleted in Table_2 too. But not the other way around,
therefor alter the design of Table_1.
insert into Table_1 (Name,ID_Number) values ('A',1),('B',2),('C',3);
insert into Table_2 (ID_Number,Buys) values (1,10),(2,20),(3,30);
select * from Table_1
select * from Table_2
truncate table table_2
truncate table table_1
drop table table_2
drop table table_1
delete Table_1
delete from dbo.table_1 where name='A'
delete from Table_1 where name like '%'
delete from dbo.table_2 where ID_Number=2
I have 2 tables, A and B.
Table B has a foreign key pointing to primary key of table A. The foreign key on table B has ON DELETE CASCADE, so the corresponding records from B is deleted when a record from A is deleted.
My requirement is to track all added/updated/deleted records in history tables. I have trigger on each table to insert the records into history tables(AHistories and BHistories tables).
I do not like the order ON DELETE CASCADE deletes the records. Trigger A is executed after trigger B, so I have to work around to get the ID of AHistory into BHistory record.
I am wanting to get rid of ON DELETE CASCADE and perform Delete on the records of B in trigger A then insert the deleted record of B into BHistories there.
To demonstrate the idea, I made the case simple, but I have a few more tables that have a foreign key pointing to the primary key in table A. Personally, I would like if I can specify the order and what I do on delete cascade.
Does this stink as an approach? Any comments are appreciated.
As bad as triggers are but sometimes they are the only way to implement complex business requirements. I would do something as follows in the following example PK_ID refers to Primary Key Column.
CREATE TRIGGER tr_Table_A_InsteadOfDelete
ON dbo.TableA
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION;
-- Insert into History Table from TableB
INSERT INTO TABLE_B_History
SELECT B.*
FROM TableB B INNER JOIN deleted d
ON B.PK_ID = d.PK_ID
-- Delete rows from TableB
DELETE FROM TableB
WHERE EXISTS (SELECT 1
FROM deleted
WHERE PK_ID = TableB.PK_ID)
-- Insert into History Table from TableA
INSERT INTO TABLE_A_History
SELECT A.*
FROM TableA A INNER JOIN deleted d
ON A.PK_ID = d.PK_ID
-- Delete rows from TableA
DELETE FROM TableA
WHERE EXISTS (SELECT 1
FROM deleted
WHERE PK_ID = TableA.PK_ID)
COMMIT TRANSACTION;
END
Using triggers for audit purposes is as obvious as it's wrong. If you have sufficient permissions, they can be disabled too easily.
Nevertheless, what you want to do can be achieved with the combination of:
INSTEAD OF DELETE triggers, where you can delete or move rows around in any order;
Disabling nested triggers. This might has unpleasant side effects, if you use triggers for more than just the audit, because it's a database-wide option.
Just don't forget that in such a trigger, you have to manually delete rows from its underlying table, as well.
I got 2 tables. They have a foreign key relation. But the foreign key is not set to update cascade. Now I want to update the table's primary key. SQL Server always prevent me from doing that because of the FK. How could I do it in SQL command? I don't have the right to modify the FK.
Thanks.
Why would you want to do this? It strikes me as potentially hazardous.
Your SQL would need to update the related table first NULLing the links back to the PK table and storing the IDs of the records effected. Then you can update the PK in the PK table. Then go back and update the FKs in the related tables.
Create new rows based on the existing row's attribute values but using the new key value. Do the same for all referencing tables. Then, using the new old key value, delete rows from the referencing tables then the referenced table. Something like:
INSERT INTO Table1 (key_col, attrib_col1)
SELECT 'new_key_value', attrib_col1
FROM Table1
WHERE key_col = 'old_key_value';
INSERT INTO Table2 (key_col, attrib_col2)
SELECT 'new_key_value', attrib_col2
FROM Table2
WHERE key_col = 'old_key_value';
DELETE
FROM Table2
WHERE key_col = 'old_key_value';
DELETE
FROM Table1
WHERE key_col = 'old_key_value';
You should only need to INSERT new rows for the parent table and UPDATE the child rows after the fact...
INSERT INTO ParentTable (PKColumn, attribute1, attribute2)
SELECT 'NewPKValue', attribute1, attribute2
FROM ParentTable
WHERE PKColumn = 'OldPKValue'
;
UPDATE ChildTable
SET FKColumn = 'NewPKValue'
WHERE FKColumn = 'OldPKValue'
;
DELETE
FROM ParentTable
WHERE PKColumn = 'OldPKValue'
;
Now for the gotchas:
1.) The above code wont work if there are any unique indexes defined on non-PK columns in the parent table and you need to use the current records' non-PK data values without modification.
2.) Since you're asking this question, I'm assuming your Parent table is not using an IDENTITY column as the PK.
3.) The code is certainly less than efficient. If your db has to do this infrequently on a few rows, you should be fine. If you need to do this 80 times per second, then I would strongly suggest you talk to the programmer/DBA or vendor, if they're at all available, about updating the FK definition to include ON UPDATE CASCADE.
4.) Make sure that there aren't any triggers on either table that would lead to unintended side effects when you create the new parent records or update the child records.
First note that I have seen this question:TSQL delete with an inner join
I have a large table and several foreign key relations, each of which have data of a given age. We need to remove data older than a given data on a regular basis to stop the DB from growing without bound.
I'm writing a query that will delete from each point on the star if you will by the given parameters (unfortunately these are configurable and different between the tables).
After this first deletion, I have a central table that I'm worried that I'm doing twice the work attempting to delete, as on delete the database checks the conditionals. I have a set of:
AND NOT EXISTS
(SELECT key
FROM table
WHERE table.key = centretable.key)
which TSQL is making into a right anti semi join and doing it nicely on the indexes. The problem is it creates a list of stuff to delete and then does the same checks again as it performs the delete.
I guess my question is whether there is a try delete by row, (I'm not going to do that in a cursor as I know how slow it would be), but you would think that such a keyword would exist, I haven't had any luck finding it though.
In terms of a single command that only checks the relationships once (rather than twice in your example - once for the NOT EXISTS, once for the DELETE), then I expect the answer is a big fat no, sorry.
(off the wall idea):
If this is a major problem, you could try some kind of reference-counting implementation, using triggers to update the counter - but in reality I expect this will be a lot more overhead to maintain than simply checking the keys like you are already.
You could also investigate NOCHECK during the delete (since you are checking it yourself); but you can only do this at the table level (so probably OK for admin scripts, but not for production code) - i.e.:
-- disable
alter table ChildTableName nocheck constraint ForeignKeyName
-- enable
alter table ChildTableName check constraint ForeignKeyName
A quick test shows that with it enabled it does an extra Clustered Index Scan on the foreign key; with it disabled, this is omitted.
Here's a full example; you can look at the query plan of the two DELETE operations... (ideally in isolation from the rest of the code):
create table parent (id int primary key)
create table child (id int primary key, pid int)
alter table child add constraint fk_parent foreign key (pid)
references parent (id)
insert parent values (1)
insert parent values (2)
insert child values (1,1)
insert child values (2,1)
-- ******************* THIS ONE CHECKS THE FOREIGN KEY
delete from parent
where not exists (select 1 from child where pid = parent.id)
-- reset
delete from child
delete from parent
insert parent values (1)
insert parent values (2)
insert child values (1,1)
insert child values (2,1)
-- re-run with check disabled
alter table child nocheck constraint fk_parent
-- ******************* THIS ONE DOESN'T CHECK THE FOREIGN KEY
delete from parent
where not exists (select 1 from child where pid = parent.id)
-- re-enable
alter table child check constraint fk_parent
Again - I stress this should only be run from things like admin scripts.
You could create an Indexed view of your select sentence:
SELECT key FROM table WHERE table.key = centretable.key
The indexed view is a physical copy of the data it would therefore be very fast to check.
You do have the overhead of updating the view, so you would need to test this against your usage pattern.
If you're reusing the same list of stuff to delete then you could consider inserting the keys to delete into a temp table and then using this in the second query.
SELECT Key, ...
INTO #ToDelete
FROM Table T
WHERE ...
Then something like this
...
LEFT OUTER JOIN #ToDelete D
ON T.Key=D.Key
WHERE D.Key IS NULL
DROP #ToDelete
If you specified the foreign key as a constraint when creating the table in the database you can tell the database what to do in case of a delete, by setting the delete rule. This rule specifies what happens if a user tries to delete a row with data that is involved in a foreign key relationship. The "No action" setting tells the user that the deletion is not allowed and the DELETE is rolled back. Implementing it like that would keep you from checking it yourself before deleting it, and thus could be seen as some kind of try.
Well, at least it works like that in MS SQL. http://msdn.microsoft.com/en-us/library/ms177288.aspx
I did find one article that discusses using an outer join in a delete:
http://www.bennadel.com/blog/939-Using-A-SQL-JOIN-In-A-SQL-DELETE-Statement-Thanks-Pinal-Dave-.htm
I hope this works for you!
The short answer to your question is no, there is no standard RDBMS keyword for deleting a master record when all foreign key references to it go away (and certainly none that would account for foreign keys in multiple tables).
Your most efficient option is a second query that is run on an as-needed basis to delete from "centre" based on a series of NOT EXISTS() clauses for each of the tables with foreign keys.
This is based on two statements I believe are both true for your situation:
You will delete more "related" records than "centre" (parent) records. Thus, any operation that attempts to adjust "centre" every time you delete from one of the other tables will result in an instantaneous update to "centre", but will require much wasted querying to delete a "centre" record only occasionally.
Given that there are multiple points on the star from "centre," any "wasted effort" checking for a foreign key in one of them is minimal compared to the whole. For instance, if there are four foreign keys to check before deleting from "centre", you can only save, at best, 25% of the time.