SQL check constraint on multiple tables - sql

So If the "Type" is 0, i should be able to add my person in Table B, else not, but the "Type" column is not and shouldn't be in Table B.

You can do this with a foreign key constraint and some trickery.
First, set up a unique constraint on TableA for both type and person:
alter table TableA add constraint unq_TableA_type_person on TableA(type, person);
This allows you set to set up a foreign key constraint. However, you need a type column. For that, you can use a computed column:
alter table TableB add type_for_a as (0); -- it is always 0
Now, just use a foreign key constraint:
alter table TableB add constraint fk_tableA_type_person
foreign key (type_for_a, person) references tableA(type, person);
Voila! You have the constraint in place without having to write any code.

CREATE TABLE T1 (TypeID INT NOT NULL, people VARCHAR(50));
GO
CREATE TABLE T2 ( people VARCHAR(50));
GO
-- creating trigger to insert on the behalf when there is a particular type
CREATE TRIGGER dbo.AfterInsertTrigger
ON T1
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
declare #id int,
#someval char(1)
insert into dbo.T2
select i.people FROM Inserted i
where i.TypeID=0 -- checks only when the id is 0
END
GO
-- inserting people with different id s into Table1
INSERT T1 (TypeID, people) SELECT 1, 'A';
INSERT T1 (TypeID, people) SELECT 0, 'B';
GO
--selecting from tables see what got affected.
select * from T1
select *from T2
--Clean up
DROP TABLE T2;
DROP TABLE T1;
GO

Related

MERGE Constraint Error on self referenced table

MERGE is raising an Error
ORA-02291: integrity constraint violated-parent key not found
on inserting into a self referenced table.
If I change query a little bit with no matter, it will work.
Am I do something wrong.
Execution order:
Preparement stuff
First MERGE query (will not work)
Second MERGE query (will work)
The order of the columns in the MERGE query does not matter. You also can run the second MERGE before first.
If you would like to restart your test, start at DROP TABLE TGR.
First some preparement:
DROP TABLE SRC;
CREATE TABLE SRC (
ID VARCHAR(20) PRIMARY KEY,
PARENT VARCHAR(20)
);
INSERT INTO SRC SELECT '1', null FROM DUAL;
INSERT INTO SRC SELECT '2', '1' FROM DUAL;
DROP TABLE TRG;
CREATE TABLE TRG (
ID VARCHAR(20) PRIMARY KEY,
PARENT VARCHAR(20),
CONSTRAINT FK2 FOREIGN KEY(PARENT) REFERENCES TRG(ID)
);
This will not work:
MERGE INTO TRG t USING(
SELECT PARENT, ID FROM SRC
) s ON (t.ID=s.ID)
WHEN NOT MATCHED THEN INSERT(ID,PARENT) VALUES (s.ID,s.PARENT);
But this will work.
MERGE INTO TRG t USING(
SELECT ID, PARENT FROM SRC
) s ON (t.ID=s.ID)
WHEN NOT MATCHED THEN INSERT(ID,PARENT) VALUES (s.ID,s.PARENT);
If you change the datatype of these columns to NUMBER, it will work.
DROP TABLE SRC;
CREATE TABLE SRC (
ID NUMBER PRIMARY KEY,
PARENT NUMBER
);
INSERT INTO SRC SELECT 1, null FROM DUAL;
INSERT INTO SRC SELECT 2, 1 FROM DUAL;
DROP TABLE TRG;
CREATE TABLE TRG (
ID NUMBER PRIMARY KEY,
PARENT NUMBER,
CONSTRAINT FK2 FOREIGN KEY(PARENT) REFERENCES TRG(ID)
);
MERGE INTO TRG t USING(
SELECT PARENT, ID FROM SRC
) s ON (t.ID=s.ID)
WHEN NOT MATCHED THEN INSERT(ID,PARENT) VALUES (s.ID,s.PARENT);
Edit:
Forgot to tell: I am using 12c

Delete from 2 tables with foreign key constraints

I have 2 tables tlbinvoice and tblRel_Inv_Course in which InvoiceID is the foreign key. When I tried to delete a row from the Invoice table, I get an error
Cannot delete foreign key constraint
Below are the 2 queries and data:
select * from invoice where InvoiceID=19
InvoiceID invimagetype location
-----------------------------------
19 image/jpeg network
select * from Rel_Inv_Course where CourseID=4262
Rel_I_C_ID CourseID InvoiceID
----------------------------------
2255 4262 19
What I tried:
delete from [TAP].[dbo].Invoice
where InvoiceID = (select InvoiceID
from Rel_Inv_Course
where CourseID = 4262)
delete from Rel_Inv_Course
where CourseID = 4262
But I can't do this. I need to delete from both the rows of the tables with invoice id as 19. Please help.
As the comments said all you need to do is flip your delete statements and you should be good:
You may consider wraping them in a begin tran so you can check that your deletes only delete the data you want as well:
Begin Tran
DECLARE #INVOICEID INT
SET #INVOICE = (select InvoiceID from Rel_Inv_Course where CourseID=4262)
delete from Rel_Inv_Course where CourseID=4262
delete from [TAP].[dbo].Invoice where InvoiceID =(#INVOICEID)
--Select * from Rel_Inv_Course
--Select * from [dbo].Invoice
--If satisfied with deletes finally commit tran
--If not satisfied --> Rollback Tran
Commit Tran
This might be easier to explain with some sample data and DDL:
USE Sandbox;
GO
CREATE TABLE dbo.Parent (ID int NOT NULL,
SomeString varchar(100) NOT NULL);
GO
CREATE TABLE dbo.Child (ID int NOT NULL,
ParentID int NOT NULL,
AnotherString varchar(100) NOT NULL);
GO
ALTER TABLE dbo.Parent ADD CONSTRAINT PK_PID PRIMARY KEY CLUSTERED (ID);
ALTER TABLE dbo.Child ADD CONSTRAINT PK_CID PRIMARY KEY CLUSTERED (ID);
ALTER TABLE dbo.Child
ADD CONSTRAINT FK_PID
FOREIGN KEY (ParentID)
REFERENCES dbo.Parent (ID);
GO
INSERT INTO dbo.Parent (ID,
SomeString)
VALUES (1, 'sdfkgjbhasdfg'),
(2, 'sdfkjsdbhkf');
GO
INSERT INTO dbo.Child (ID,
ParentID,
AnotherString)
VALUES (1, 1, 'asdfkiashjbd'),
(2, 1, '#asldjasbhdk,'),
(3, 2, 'asfjasdfj');
GO
--Try to delete a row in Parent:
DELETE FROM dbo.Parent
WHERE ID = 2;
--No surprise it failed
GO
--Try to delete a row in child
DELETE FROM dbo.Child
WHERE ID = 2;
--This worked fine.
GO
--
--If we check, however, ParentID 1 and 2 are still in the table:
SELECT *
FROM dbo.Child;
--We want to delete ID 1 in parent, so we need to delete the other row
DELETE FROM dbo.Child
WHERE ParentID = 1;
--Now delete in Parent
DELETE FROM dbo.Parent
WHERE ID = 1;
GO
DROP TABLE dbo.Child;
DROP TABLE dbo.Parent;
You'll notice that the first delete on Parent failed, as it conflicts with the foreign key constraint. After, however, deleting all the rows in child for that ID, you can delete the parent row.
The same logic applies with your data. Delete the relevant rows in the child table first, and then you delete the data in your parent table. Alternatively, implement cascading, and then you simply need to delete the row in the parent, and the deletes will cascade down.

inserting into A errors because of a foreign key contraint issue

Can someone help explain this to me and resolve it?
http://sqlfiddle.com/#!6/2adc7/9
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_tblMobileForms_tblForms". The conflict occurred in database "db_6_2adc7", table "dbo.tblForms", column 'fm_id'.: insert into tblMobileForms(fm_name) values ('lol')
My schema has the ID from tblMobileForms be a foreign key to tblForms.fm_id
To do what you are trying to do you cannot set up the FK on tblMobileForms as an identity. See my fiddle below for more information.
http://sqlfiddle.com/#!6/be6f7/2
Alternatively what you could do is to have tblMobileForms have it's own separate surrogate key and have a different FK column to the tblForms table.
The PK on the tblMobileForms table has the same name as the FK on the same table. Seeing the PK is an IDENTITY column, you can end up with non-matching values.
In my fiddle, the tblForms table contained IDs in the upper 60s. Running the INSERT in the child table would add a record with id 1, which does not exist in the parent table.
I'd create a new row in the tblMobileForms table, and reference that to the parent table.
You could use an INSTEAD OF trigger to apply a random ID to each mobile form as it is inserted:
CREATE TRIGGER dbo.tblMobileForms_Insert
ON dbo.tblMobileForms
INSTEAD OF INSERT
AS
BEGIN
DECLARE #Inserted TABLE (fm_ID INT, fm_html_file VARBINARY(MAX), fm_name NVARCHAR(50));
INSERT #Inserted (fm_ID, fm_html_File, fm_Name)
SELECT fm_ID, fm_html_File, fm_Name
FROM inserted;
IF EXISTS (SELECT 1 FROM #Inserted WHERE fm_ID IS NULL)
BEGIN
WITH NewRows AS
( SELECT fm_ID, fm_html_File, fm_Name, RowNumber = ROW_NUMBER() OVER (ORDER BY fm_name)
FROM #Inserted
WHERE fm_ID IS NULL
), AvailableIDs AS
( SELECT fm_ID, RowNumber = ROW_NUMBER() OVER (ORDER BY fm_ID)
FROM tblForms f
WHERE NOT EXISTS
( SELECT 1
FROM tblMobileForms m
WHERE f.Fm_ID = m.fm_ID
)
AND NOT EXISTS
( SELECT 1
FROM inserted i
WHERE f.fm_ID = i.fm_ID
)
)
UPDATE NewRows
SET fm_ID = a.fm_ID
FROM NewRows n
INNER JOIN AvailableIDs a
ON a.RowNumber = n.RowNumber
IF EXISTS (SELECT 1 FROM #Inserted WHERE fm_ID IS NULL)
BEGIN
RAISERROR ('Not enough free Form IDs to allocate an ID to the inserted rows', 16, 1);
RETURN;
END
END
INSERT dbo.tblMobileForms (fm_ID, fm_html_File, fm_Name)
SELECT fm_ID, fm_html_file, fm_name
FROM #Inserted
END
When each row is inserted the trigger will check for the next available ID in tblForms and apply it sequentially to the inserted rows where fm_id is not specified. If there are no free ID's in tblForms then the trigger will throw an error so a 1 to 1 relationship is maintained (The error would be thrown anyway since tblMobileForms.fm_id is also a PK).
N.b. this requires tblForms.fm_ID to just be an int column, and not identity.

How to prevent an entry in one table from existing in another table

I have 3 tables in SQL Server 2008
Clubs with a PK of ID and a Name.
Products which has a PK of ID, a FK of ClubID , a Name, a ShortCode and a Keyword.
There is a UK to enforce that there are no duplicate keywords for combinations of ShortCode/Keyword.
ProductAdditionalShortCodes. This has a PK of ID, a FK of ProductID and a Keyword
The idea is to prevent any shortcode/keyword combination of products to point to different clubs and also to prevent the creation of duplicate short/code keyword combinations
I have a solution that works, but feels clunky, and could under certain circumstances fail if multiple users happened to update multiple entries at the same time. (Hypothetically)
How can I add some form of constraint to the DB to prevent the Keyword in the Main table from being the same as in the Additional table and the other way round?
Following is a sample script to create the scenario and some of the examples I want to prevent. I am not opposed to changing the DB design if the impact of the change would not disrupt too many other aspects of the solution. (I realize this is subjective)
use Tinker
if exists (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'dbo.ProductAdditionalKeywords') AND type in (N'U'))
drop table dbo.ProductAdditionalKeywords
go
if exists (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'dbo.Products') AND type in (N'U'))
drop table dbo.Products
go
if exists (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'dbo.Clubs') AND type in (N'U'))
drop table dbo.Clubs
go
create table dbo.Clubs (
ID int not null identity(1,1)
,Name varchar(50) not null
,constraint PK_Clubs primary key clustered ( ID )
)
go
alter table dbo.Clubs add constraint UK_Clubs__Name unique ( Name )
go
create table dbo.Products (
ID int not null identity(1,1)
,ClubID int not null
,Name varchar(50) not null
,ShortCode varchar(50) not null
,Keyword varchar(50) not null
,constraint PK_Products primary key clustered ( ID )
)
go
alter table dbo.Products add constraint UK_Products__ShortCode_Keyword unique ( ShortCode , Keyword )
go
alter table dbo.Products add constraint UK_Products__Name unique ( Name )
go
alter table dbo.Products add constraint FK_Products_ClubID foreign key ( ClubID ) references dbo.Clubs ( ID )
go
create table dbo.ProductAdditionalKeywords (
ID int not null identity(1,1)
,ProductID int not null
,Keyword varchar(50) not null
,constraint PK_ProductAdditionalKeywords primary key clustered ( ID )
)
go
alter table dbo.ProductAdditionalKeywords add constraint FK_ProductAdditionalKeywords_ProductID foreign key ( ProductID ) references dbo.Products ( ID )
go
alter table dbo.ProductAdditionalKeywords add constraint UK_ProductAdditionalKeywords__Keyword unique ( Keyword )
go
insert into dbo.Clubs ( Name )
select 'Club 1'
union all select 'Club 2'
insert into dbo.Products (ClubID,Name,Shortcode,Keyword)
select 1,'Product 1','001','P1'
union all select 1,'Product 2','001','P2'
union all select 1,'Product 3','001','P3'
union all select 2,'Product 4','002','P4'
union all select 2,'Product 5','002','P5'
union all select 2,'Product 6','002','P6'
insert into dbo.ProductAdditionalKeywords (ProductID,Keyword)
select 1,'P1A'
union all select 1,'P1B'
union all select 2,'P2A'
union all select 2,'P2B'
/*
What can be done to prevent the following statements from beeing allowed based on the reason in the comments?
*/
--insert into dbo.ProductAdditionalKeywords (ProductID,Keyword) values ( 1 , 'P2' ) -- Main keyword for product 2
--update dbo.Products set Keyword = 'P1A' where ID = 2 -- Additional keyword for product 1
--insert into dbo.ProductAdditionalKeywords (ProductID,Keyword) values ( 3 , 'P1' ) -- Main ShortCode/Keyword combination for product 1
/*
At the moment I look at the following view to see if the proposed(new/updated) Keyword/Shortcode combination already exists
If it already exists I pevent the insert/update
Is there any way to do it in the DB via constraints rather than in the BLL?
*/
select ShortCode,Keyword,count([ClubID]) as ClubCount from
(
select p.ClubID,p.ShortCode,p.Keyword,p.ID
from dbo.Products p
union all
select p.ClubID,p.ShortCode,PAK.Keyword,PAK.ID * -1
from dbo.ProductAdditionalKeywords as PAK
inner join dbo.Products P on PAK.ProductID = P.ID
) as FullList
group by Shortcode,Keyword
order by Shortcode,Keyword
How I would normally do this would be to place all of the keywords in a separate table (e.g. what is currently your additional table). If all keywords must be distinct within a ShortCode, then I'd include ShortCode in this table also, so that a unique constraint can be applied across both columns.
If all keywords for a product must be in the same ShortCode, then I'd keep ShortCode in Products also. I'd apply a unique constraint on (ID,ShortCode) in that table, and an additional foreign key from the keywords table, referencing both columns on both sides.
What we're left with now are two potential issues not included in your original design, but I don't know if they're a concern in practice:
1) Is the Keyword in Products more important, or special, than the additional keywords? If so, we need to add a column to keywords table to mark which one is important. To ensure only one is set, you can search for plenty of other SO questions which involve unique constraints with additional conditions. (Let me know if you can't find one and need it, I'm sure I can add a link if necessary)
2) Should a Product be allowed to have no keywords? If not, then I'd create a view that mimics your original Products table. In this circumstance, it would be easier if 1) above is true, in which case we always join to the "important" keyword. Otherwise, we need to have some way to limit it to a single row per product. We deny insert/update/delete on the table, and only allow them through the view. 3 relatively simple triggers will then maintain the underlying table structure.
on your design, I do not understand the the use of productAdditionalShortCodes having no field of ShortCode.
However, you can add Unique key constraint with ShortCode & Keyword (composite key). This will eliminate duplicate entry in product table.

How can I make a copy of a table with a PK?

In an Oracle 10g database, I would like to make a copy of an existing table. I would like it to have the same data and rows as the original table. The original table uses a PK though, so I'm not sure how to copy it and keep them unique.
oracle maintains the pk as a column constraint. you have to copy the table and subsequently create this constraint for the new table.
the following code illustrates how to get your job done.
-- setting up table t1 - this is just for the sake of demonstration
create table t1 (
t_id integer
, t_data varchar2(40)
);
alter table t1 modify ( t_id constraint t1_pk primary key );
insert into t1 values ( 1, 'test');
insert into t1 values ( 2, 'another test');
insert into t1 values ( 3, 'final test');
commit;
-- copying table t1 (definition + contents) and defining the pk
create table t2 as ( select * from t1 );
alter table t2 modify ( t_id constraint t2_pk primary key );
hope this helps,
best regards,
carsten
You can make the copy using
CREATE TABLE dummy_copy as SELECT * FROM dummy//Structure and data
Also you could use dbms_metadata.get_ddl to get the associated constraints of the table
and create it with all the checks
SELECT dbms_metadata.get_ddl( 'TABLE', 'dummy' ) FROM DUAL;
Or you can just do it all in one statement:
create table mike_temp_1 (
col1,
col2,
col3,
col4,
col5,
constraint xpk_mike_temp_1 primary key (col1)
)
as select *
from OLD_POLICY_TERM;
I think the format of specifying column names when using create table as select is a bit fiddly in that I don't believe that you can specify data types (sort of obvious really) but you can specify constraints such as not null, primary key and foreign key.