Execution plan oddity after re-enabling foreign key constraint - sql

I have a weird problem where after setting nocheck on a foreign constraint and re-enabling it,
I am getting a same out-dated execution plan that was used with nocheck on.
Why would SQL server generate an execution plan as if foreign constraint FKBtoA is disabled even after adding the check again with following statement?
alter table B check constraint FKBtoA
[UPDATE1]
So far dropping foreign constraint and readding it worked.
alter table B drop constraint FKBtoA
alter table B add constraint FKBtoA foreign key (AID) references A(ID)
But for really big tables, this seems like an overkill - Is there a better way?
[ANSWER]
I had to add WITH CHECK in alter statement like following to get the old execution plan
alter table B WITH CHECK add constraint FKBtoA foreign key (AID) references A(ID)
Here is a full SQL statement
create table A ( ID int identity primary key )
create table B (
ID int identity primary key,
AID int not null constraint FKBtoA references A (ID)
)
select *
from B
where exists (select 1 from A where A.ID = B.AID)
alter table B nocheck constraint FKBtoA
GO
select *
from B
where exists (select 1 from A where A.ID = B.AID)
alter table B check constraint FKBtoA
GO
select *
from B
where exists (select 1 from A where A.ID = B.AID)
Here is the screenshot of execution plans per each SELECT statement
Before disabling foreign key constraint
After disabling foreign key constraint
After re-enabling foreign key constraint

Most likely your constraint is enabled but not trusted, so there can be orphan rows in your child table. Read this great post by Hugo Kornelis:Can you trust your constraints?

There doesn't seem to be any data in those tables, judging from both the scripts you posted and from the width of the connectors in the plan. Analyzing query plans on empty tables is largely irrelevant: at one single page read, the optimizer will almost certainly choose a full scan.
I assume you're doing this as some sort of experiment, in real world you should join those tables not use inner EXIST.

Personally I do not know, but I know how to rebuild statistics...

Related

Phantom Table being created in Teradata

I'm using Teradata 16.20.05.01 to run the following script:
create table t1(v int not null);
create table t2(w int null);
alter table t1 add constraint pk primary key (v);
alter table t2 add constraint t2_fk foreign key (w) references t1 (v);
After adding the foreign key, I suddenly get one excess table in my schema:
select TableName, RequestText
from "DBC".Tables
where DatabaseName = 'test'
and (TableName like 't1%' or TableName like 't2%')
Output:
TableName |RequestText |
----------|----------------------------------------------------------------------|
t1 |alter table t1 add constraint pk primary key (v) |
t2 |create table t2(w int null) |
T2_0 |alter table t2 add constraint t2_fk foreign key (w) references t1 (v) |
This is especially annoying when re-creating that foreign key:
alter table t2 drop constraint t2_fk;
alter table t2 add constraint t2_fk foreign key (w) references t1 (v);
Which isn't possible because of:
SQL Error [5303] [HY000]: [Teradata Database] [TeraJDBC 15.00.00.33] [Error 5303] [SQLState HY000] Error table 'TEST.t2_0' already exists.
Workaround:
The problem does not appear when using inline constraint definitions
create table t1(v int not null, constraint pk primary key (v));
create table t2(w int null, constraint t2_fk foreign key (w) references t1 (v));
Is this a known issue? Is there a reliable workaround?
This is documented behaviour, when you add a Foreign Key to an existing table there's an error table created and all all rows violating the constraint are copied into it. And it's not dropped automatically after the ALTER.
The workaround is simple: Don't use Standard Foreign Keys, you will hardly find any site using it. Switch to Batch FKs, i.e. REFERENCES WITH CHECK OPTION, which applies the check on a request level (not row by row), or to a Soft/Dummy FK, REFERENCES WITH NO CHECK OPTION, which simply defined the constraint without enforcing it (you must check for PK/FK violations in your load scripts anyway).

CASCADE behaviour on the drop of a foreign key

I am not clear about what happens when a "foreign key constraint" is deleted specifying the option CASCADE.
For instance, consider this command
ALTER TABLE table1 DROP CONSTRAINT foreignKeyToTable2 CASCADE.
What the option CASCADE is supposed to do in this case? What would happen if I omitted it? And if I wrote RESTRICT instead of CASCADE?
Note: this example of query is excerpted from "Ramez Elmasri, Shamkant B. Navathe - Fundamentals of database systems, end of chapter 5".
The cascade option to drop a constraint is only needed when dropping primary keys, not when dropping a foreign key.
Consider this example in Postgres:
create table t1 (id integer, constraint pk_one primary key (id));
create table t2 (id integer primary key, id1 integer references t1);
When you try to run:
alter table t1 drop constraint pk_one;
You get:
ERROR: cannot drop constraint pk_one on table t1 because other objects depend on it
Detail: constraint t2_id1_fkey on table t2 depends on index pk_one
Hint: Use DROP ... CASCADE to drop the dependent objects too.
If you run:
alter table t1 drop constraint pk_one cascade;
you get:
NOTICE: drop cascades to constraint t2_id1_fkey on table t2
Telling you that the foreign key that needed the primary key was dropped as well.
Note that not all DBMS support a cascading drop. Postgres and Oracle do.
MySQL, SQL Server or Firebird do not. You need to drop the foreign keys manually in those DBMS.

SQL Update error. FK Conflict

I am trying to change the name of a group from ASSY to Manufacturing but am running into some dificulties. It is on a sql server database. I ran the query below.
Update groups
set group_code= 'Manufacturing'
where site_code = 'TMMBC' and group_code = 'ASSY' and group_description = 'Manufacturing'
But it returned with this error - "The UPDATE statement conflicted with the REFERENCE constraint "user_groups_FK_2". The conflict occurred in database "eci", table "dbo.user_groups"."
Is there a way I can update both tables at the same time to bypass this error?
Is there a way I can update both tables at the same time to bypass
this error?
Yes. You can define the foreign key to cascade on update.
I would consider restructuring it so that groups has an integer surrogate key though and have the textual description as a separate column.
This avoids having to repeat the relatively long string Manufacturing possibly many times in the child table.
Assuming your definition for table user_groups looks something like:
create table dbo.user_groups (
group_code varchar(100),
-- other fields
constraint user_groups_fk_2 foreign key (group_code) references dbo.groups (group_code)
);
You would change the table definition to have the foreign key cascade, like:
create table dbo.user_groups (
group_code varchar(100),
-- other fields
constraint user_groups_fk_2 foreign key (group_code) references dbo.groups (group_code) on delete cascade on update cascade
);
Or through ALTER TABLE statements:
alter table dbo.user_groups drop constraint user_groups_fk_2;
alter table dbo.user_groups add constraint user_groups_fk_2 foreign key (group_code) references dbo.groups (group_code) on delete cascade on update cascade;

Enforce a foreign-key constraint to columns of same table

How to enforce a constraint of foreign key on columns of same table in SQL while entering values in the following table:
employee:
empid number,
manager number (must be an existing employee)
Oracle call this a self-referential integrity constraint. The documentation is here for a description,
You create a self-referential constraint in the same manner you would a normal one:
alter table employees
add constraint employees_emp_man_fk
foreign key ( manager_no )
references employees ( emp_id )
on delete set null
;
I'm assuming that your manager_no is nullable. I've added set null here as a delete cascade would probably wipe out a significant amount of your table.
I can't think of a better way of doing this. Deleting a manager should not result in the deletion of all their employees so you have to set null and have a trigger on the table to alert you to anyone with no manager.
I always like this site, which is good for simple references. and don't forget to have an index on the FK as well or Tom will yell at you :-).
One can also utilise standard Oracle syntax to create a self-referential FK in the create table statement, which would look like the following.
create table employees
( emp_id number
, other_columns ...
, manager_no number
, constraint employees_pk
primary key (emp_id)
, constraint employees_man_emp_fk
foreign key ( manager_no )
references employees ( emp_id )
on delete set null
);
EDIT:
In answer to #popstack's comment below:
Whilst you can do this in one statement not being able to alter a table is a fairly ridiculous state of affairs. You should definitely analyze a table that you're going to be selecting from and you will still want an index on the foreign key ( and possibly more columns and / or more indexes ) otherwise whenever you use the foreign key you're going to do a full table scan. See my link to asktom above.
If you're unable to alter a table then you should, in descending order of importance.
Find out how you can.
Change your DB design as a FK should have an index and if you can't have one then FKs are probably not the way to go. Maybe have a table of managers and a table of employees?
SELF REFERENCES QUERY...
Alter table table_name ADD constraints constraints_name foreign key(column_name1,column_name2..) references table_name(column_name1,column_name2...) ON DELETE CASCADE;
EX- ALTER TABLE Employee ADD CONSTRAINTS Fr_key( mgr_no) references employee(Emp_no) ON DELETE CASCADE;
CREATE TABLE TABLE_NAME (
`empid_number` int ( 11) NOT NULL auto_increment,
`employee` varchar ( 100) NOT NULL ,
`manager_number` int ( 11) NOT NULL ,
PRIMARY KEY (`empid_number`),
CONSTRAINT `manager_references_employee`
FOREIGN KEY (`manager_number`) REFERENCES (`empid_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Hope it helps!

Oracle unique constraint error when inserting

INSERT INTO SS_ALERT_EVENTS ( ALERT_ID, EVENT_ID, TIME_DURATION, ALERT_EVENT_EFFECT, DATASET_ASSIGN_RULE, KEY_FIELDS_ASSIGN_RULE, SIDE, ALERT_VALIDATION_RULE, UNIQUE_ID ) VALUES ( 'test1', 7 , 0, 1 , NULL, '5b414c4552545f494e535452554d454e542e496e737472756d656e742049445d203a3d205b54524144455f5245504f52542e496e737472756d656e742049445d3b', -1, '5b414c4552542e416374696f6e5d203a3d20313b', 1)
*
ERROR at line 1:
ORA-00001: unique constraint (ESV31SURV.PK_SS_ALERT_EVENTS) violated
The EVENT_ID field is the problem. But I want to insert it anyway. However, when I try to drop the constraint of that name, it says there is no such constraint. Further, no such constraint is shown in USER_CONSTRAINTS table. What should I do?
The unique constraint might be in fact be a primary key constraint - at least that's what the name suggests.
Dropping the primary key of a table will potentially have very bad side effect it might break applications that rely on this primary key (and you will also have to drop all foreign keys that reference that table before you can drop the primary key)
That primary key was created with a purposes, so before blindly dropping it you should consult whoever created that schema and make sure that primary key is not needed (or should be redefined).
Having said all this: try to drop the PK using
ALTER TABLE SS_ALERT_EVENTS
DROP PRIMARY KEY
But please double check if this is really a wise decision!