Oracle SQL, trigger inserting row into table X as FK for currently inserted row in table Y - sql

So in Oracle 11g I have the following:
CREATE TABLE OBJECT(
ID NUMBER(8) NOT NULL,
CATEGORY_ENUM_ID NUMBER(8) NOT NULL
);
CREATE SEQUENCE OBJECT_SEQ
START WITH 1
INCREMENT BY 1
NOCACHE
NOCYCLE;
/
CREATE TABLE TREE(
ID NUMBER(8) NOT NULL,
OBJECT_ID NUMBER(8) NOT NULL,
NAME NVARCHAR2(128)
);
CREATE TABLE CATEGORY_ENUM(
ID NUMBER(8) NOT NULL,
NAME NVARCHAR2(64)
);
-- PK's
ALTER TABLE TREE
ADD CONSTRAINT TREE_PK PRIMARY KEY (ID);
ALTER TABLE OBJECT
ADD CONSTRAINT OBJECT_PK PRIMARY KEY (ID);
ALTER TABLE CATEGORY_ENUM
ADD CONSTRAINT CATEGORY_ENUM_PK PRIMARY KEY (ID)
--- FK's
ALTER TABLE TREE
ADD CONSTRAINT TREE_OBJECT_FK FOREIGN KEY (OBJECT_ID)
REFERENCES OBJECT (ID);
ALTER TABLE OBJECT
ADD CONSTRAINT OBJECT_CATEGORY_FK FOREIGN KEY (CATEGORY_ENUM_ID)
REFERENCES CATEGORY_ENUM (ID);
-- Closed dictionary sample data
INSERT INTO CATEGORY_ENUM (ID, NAME) VALUES (1, 'TREE');
INSERT INTO CATEGORY_ENUM (ID, NAME) VALUES (2, 'HERB');
INSERT INTO CATEGORY_ENUM (ID, NAME) VALUES (3, 'SHROOM');
-- Triggers
CREATE OR REPLACE TRIGGER TREE_before_insert
BEFORE INSERT
ON TREE
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
CATEGORY_ID NUMBER;
BEGIN
SELECT ID INTO CATEGORY_ID FROM CATEGORY_ENUM WHERE NAME = 'TREE' AND ROWNUM <= 1;
if :NEW.OBJECT_ID is null then
:NEW.OBJECT_ID := OBJECT_SEQ.nextval;
INSERT INTO OBJECT (ID, CATEGORY_ENUM_ID) VALUES (:NEW.OBJECT_ID, CATEGORY_ID);
end if;
END;
/
If next I run:
INSERT INTO TREE (ID, NAME) VALUES (1, 'Tree1');
INSERT INTO TREE (ID, NAME) VALUES (2, 'Tree2');
I get an error:
...
TRIGGER TREE_BEFORE_INSERT compiled
Error starting at line 91 in command:
INSERT INTO TREE (ID, NAME) VALUES (1, 'Tree1')
Error report:
SQL Error: ORA-02291: integrity constraint (HR.TREE_OBJECT_FK) violated - parent key not found
02291. 00000 - "integrity constraint (%s.%s) violated - parent key not found"
*Cause: A foreign key value has no matching primary key value.
*Action: Delete the foreign key or add a matching primary key.
1 rows inserted.
But if before the above, I insert anything into FEATURES table (like below), it works fine.
INSERT INTO OBJECT (ID, CATEGORY_ENUM_ID) VALUES (0, 1);
INSERT INTO TREE (ID, NAME) VALUES (1, 'Tree1');
INSERT INTO TREE (ID, NAME) VALUES (2, 'Tree2');
So the problem occurs only for the very first insert, the others work fine, IDs assigned from OBJECT_SEQ.nextval are proper.
What am I doing wrong ?
Jah bless ya for help.
EDIT I have removed some unnecessary code, so now it's more clear and shorter.

The problem here is that you have a primary key constraint placed on the "TREE" table and a foreign key constraint placed on the "OBJECT" table. So any new record that you try inserting into the table with foreign key constraint, make sure it is already available in the table that has the primary key mapping.
Example shown below:
**Tables:**
create table tab1
(id number,
name varchar2(100)
)
create table tab2
(id number,
name varchar2(100)
)
**constraints added:**
alter table tab1 add constraint pk_tab1_id primary key (id)
alter table tab2 add constraint fk_tab2_id foreign key(id) references tab1(id)
**Insert statement on the second table:**
insert into tab2(values(1,'abc')
**Error:**
SQL Error: ORA-02291: integrity constraint (PRAVE.fk_tab2_id) violated - parent key not found
02291. 00000 - "integrity constraint (%s.%s) violated - parent key not found"
*Cause: A foreign key value has no matching primary key value.
*Action: Delete the foreign key or add a matching primary key.
**Solution:**
**Insert into the table with PK first as below:**
insert into tab1 values(1,'abc')
1 rows inserted.
insert into tab2 values(1,'abc')
1 rows inserted.

Related

Even with complete and readable statements, an error occurs: SQL Error [900] [42000]: ORA-00900: invalid SQL statement

I have two tables created in my Oracle database: the table 'responsible' (which has the id and description of the responsible) and the table 'ident' (which has id's of three types of expenses).
Below, I am trying to create a script in which:
Add values to the columns of the 'responsible';
create a 'type_responsible' table, and insert values into it;
create a relationship table called 'responsible_by_type', which, using the data from the table 'responsible', 'ident' and 'responsible_type' establishes one relationship between them.
These changes in data type to primary key or foreign key are attempts to get the bank to recognize the fields as relatable.
I get the error: 'SQL Error [900] [42000]: ORA-00900: invalid SQL statement'. Even if I execute one instruction at a time, which makes no sense.
I really don't know where my mistake is. Since I can't run the script, I can't know if the script allows me to reach my goal. Could someone help me?
<--Table RESPONSIBLE-->
INSERT INTO RESPONSIBLE(RESPONSIBLE_ID, RESPONSIBLE_DESC) VALUES (1, 'payer1');
INSERT INTO RESPONSIBLE(RESPONSIBLE_ID, RESPONSIBLE_DESC) VALUES (2, 'payer2');
ALTER TABLE RESPONSIBLE ADD CONSTRAINT PK_RESPONSIBLE_ID PRIMARY KEY (RESPONSIBLE_ID);
ALTER TABLE RESPONSIBLE ADD CONSTRAINT UNIQUE_RESPONSIBLE_DESC UNIQUE (RESPONSIBLE_DESC);
<--Table IDENT-->
<--OPTION ONE-->
ALTER TABLE IDENT ADD CONSTRAINT pk_RESPONSIBLES_TYPE_IDS primary key(RESPONSIBLE_DEBTS_ID, RESPONSIBLE_ASSETS_ID, RESPONSIBLE_EXPENSES_ID);
<--OPTION TWO-->
ALTER TABLE IDENT ADD CONSTRAINT FK_RESPONSIBLE_DEBTS_ID FOREIGN KEY (RESPONSIBLE_DEBTS_ID);
ALTER TABLE IDENT ADD CONSTRAINT FK_RESPONSIBLE_ASSETS_ID FOREIGN KEY (RESPONSIBLE_ASSETS_ID);
ALTER TABLE IDENT ADD CONSTRAINT FK_RESPONSIBLE_EXPENSES_ID FOREIGN KEY (RESPONSIBLE_EXPENSES_ID);
<--Table RESPONSIBLE_TYPE-->
CREATE TABLE RESPONSIBLE_TYPE(RESPONSIBLE_TYPE_ID NUMBER(10), RESPONSIBLE_TYPE_DESC VARCHAR(100));
INSERT INTO RESPONSIBLE_TYPE(RESPONSIBLE_TYPE_ID, RESPONSIBLE_TYPE_DESC) VALUES (1, 'Assets');
INSERT INTO RESPONSIBLE_TYPE(RESPONSIBLE_TYPE_ID, RESPONSIBLE_TYPE_DESC) VALUES (2, 'Expenses');
INSERT INTO RESPONSIBLE_TYPE(RESPONSIBLE_TYPE_ID, RESPONSIBLE_TYPE_DESC) VALUES (3, 'Debts');
ALTER TABLE RESPONSIBLE_TYPE ADD CONSTRAINT PK_RESPONSIBLE_TYPE_ID PRIMARY KEY (RESPONSIBLE_TYPE_ID);
<--Table RESPONSIBLE_BY_TYPE-->
CREATE TABLE RESPONSIBLE_BY_TYPE(RESPONSIBLE_ID NUMBER(10) NOT NULL, RESPONSIBLE_EXPENSES_ID NUMBER(10) NOT NULL,
RESPONSIBLE_ASSETS_ID NUMBER(10) NOT NULL, RESPONSIBLE_DEBTS_ID NUMBER(10) NOT NULL, RESPONSIBLE_DESC VARCHAR(100), RESPONSIBLE_TYPE VARCHAR(100),
CONSTRAINT FK_RESPONSIBLE_BY_TYPE FOREIGN KEY(RESPONSIBLE_ID) REFERENCES RESPONSIBLE(RESPONSIBLE_ID),
CONSTRAINT FK_RESPONSIBLE_BY_TYPE FOREIGN KEY(RESPONSIBLE_EXPENSES_ID) REFERENCES IDENT(RESPONSIBLE_EXPENSES_ID),
CONSTRAINT FK_RESPONSIBLE_BY_TYPE FOREIGN KEY(RESPONSIBLE_ASSETS_ID) REFERENCES IDENT(RESPONSIBLE_ASSETS_ID),
CONSTRAINT FK_RESPONSIBLE_BY_TYPE FOREIGN KEY(RESPONSIBLE_DEBTS_ID) REFERENCES IDENT(RESPONSIBLE_DEBTS_ID),
CONSTRAINT FK_RESPONSIBLE_BY_TYPE FOREIGN KEY(RESPONSIBLE_TYPE) REFERENCES RESPONSIBLE_TYPE(RESPONSIBLE_TYPE),
CONSTRAINT FK_RESPONSIBLE_BY_TYPE FOREIGN KEY(RESPONSIBLE_DESC) REFERENCES RESPONSIBLE(RESPONSIBLE_DESC);
In the first steps it was running. From the moment I created the table 'responsible_by_type' and started changing the data types of the other tables, I couldn't do anything anymore.
Now, even when I execute a simple insert instruction, I get the error I mentioned above.
This works from Oracle SQL Developer
-- ******************* Existing Table RESPONSIBLE - you don't need to create it ***************************
--CREATE TABLE RESPONSIBLE
-- (
-- RESPONSIBLE_ID NUMBER(10),
-- RESPONSIBLE_DESC VARCHAR2(100)
-- );
-- -------------- Alter table --------------------
ALTER TABLE RESPONSIBLE ADD CONSTRAINT pk_responsible_id PRIMARY KEY (RESPONSIBLE_ID);
ALTER TABLE RESPONSIBLE ADD CONSTRAINT unique_responsible_desc UNIQUE (RESPONSIBLE_DESC);
-- ---------- I n s e r t s ----------------
INSERT INTO RESPONSIBLE(RESPONSIBLE_ID, RESPONSIBLE_DESC) VALUES (1, 'payer1');
INSERT INTO RESPONSIBLE(RESPONSIBLE_ID, RESPONSIBLE_DESC) VALUES (2, 'payer2');
-- ******************* Existing Table IDENT - you don't need to create it ***************************
-- CREATE TABLE IDENT
-- (
-- RESPONSIBLE_DEBTS_ID NUMBER,
-- RESPONSIBLE_ASSETS_ID NUMBER,
-- RESPONSIBLE_EXPENSES_ID NUMBER
-- );
-- -------------- Alter table --------------------
ALTER TABLE IDENT ADD CONSTRAINT pk_responsibles_type_ids
PRIMARY KEY(RESPONSIBLE_DEBTS_ID, RESPONSIBLE_ASSETS_ID, RESPONSIBLE_EXPENSES_ID);
-- *************** New Table RESPONSIBLE_TYPE ***************************
CREATE TABLE RESPONSIBLE_TYPE
(
RESPONSIBLE_TYPE_ID NUMBER(10),
RESPONSIBLE_TYPE_DESC VARCHAR(100),
CONSTRAINT pk_responsible_type_id PRIMARY KEY (RESPONSIBLE_TYPE_ID)
-- option 2 -- PK(RESPONSIBLE_TYPE_ID, RESPONSIBLE_TYPE_DESC)
);
-- ---------- I n s e r t s ----------------
INSERT INTO RESPONSIBLE_TYPE(RESPONSIBLE_TYPE_ID, RESPONSIBLE_TYPE_DESC) VALUES (1, 'Assets');
INSERT INTO RESPONSIBLE_TYPE(RESPONSIBLE_TYPE_ID, RESPONSIBLE_TYPE_DESC) VALUES (2, 'Expenses');
INSERT INTO RESPONSIBLE_TYPE(RESPONSIBLE_TYPE_ID, RESPONSIBLE_TYPE_DESC) VALUES (3, 'Debts');
-- *************** New Table RESPONSIBLE_BY_TYPE ***************************
CREATE TABLE RESPONSIBLE_BY_TYPE
(
RESPONSIBLE_ID NUMBER(10) NOT NULL,
RESPONSIBLE_EXPENSES_ID NUMBER(10) NOT NULL,
RESPONSIBLE_ASSETS_ID NUMBER(10) NOT NULL,
RESPONSIBLE_DEBTS_ID NUMBER(10) NOT NULL,
RESPONSIBLE_DESC VARCHAR(100),
RESPONSIBLE_TYPE_ID NUMBER(10),
-- NOTE *** FK Constraints References PKs in referenced tables *** ----------
CONSTRAINT fk_resp_by_type_resp
FOREIGN KEY(RESPONSIBLE_ID) REFERENCES RESPONSIBLE(RESPONSIBLE_ID),
CONSTRAINT fk_resp_by_type_idnt
FOREIGN KEY(RESPONSIBLE_EXPENSES_ID, RESPONSIBLE_ASSETS_ID, RESPONSIBLE_DEBTS_ID)
REFERENCES IDENT(RESPONSIBLE_EXPENSES_ID, RESPONSIBLE_ASSETS_ID, RESPONSIBLE_DEBTS_ID),
CONSTRAINT fk_resp_by_type_resptype
FOREIGN KEY(RESPONSIBLE_TYPE_ID) REFERENCES RESPONSIBLE_TYPE(RESPONSIBLE_TYPE_ID)
-- option 2 -- FK(RESPONSIBLE_TYPE_ID, RESPONSIBLE_TYPE_DESC) references RESPONSIBLE_TYPE(RESPONSIBLE_TYPE_ID, RESPONSIBLE_TYPE_DESC)
);
-- R e s u l t
table RESPONSIBLE created.
table RESPONSIBLE altered.
table RESPONSIBLE altered.
1 rows inserted.
1 rows inserted.
table IDENT created.
table IDENT altered.
table RESPONSIBLE_TYPE created.
1 rows inserted.
1 rows inserted.
1 rows inserted.
table RESPONSIBLE_BY_TYPE created

Integrity constraint violated - Parent key not found

I am creating two tables each having one primary key column and another column that is used to link both tables using a foreign key.
create table t1(a number not null primary key,
b number);
create table t2 ( c number ,
d number not null primary key);
alter table t1 add foreign key (b) references t2(d);
alter table t2 add foreign key (c) references t1(a);
Now when I try to insert values in any one of the table I get the error as below
ORA-02291: integrity constraint (SQL_KVQVOPFDDGLIGJGJSPOQZZIPN.SYS_C0049615414) violated - parent key not found ORA-06512: at "SYS.DBMS_SQL", line 1721
How can I insert the data on both columns of table t1 at once?
Constraints should be created as deferrable initially deferred, otherwise it won't work because one table references another and vice versa. In Oracle database versions which didn't support deferred constraints, you should have dropped foreign key constraints, insert rows (paying attention that foreign keys won't be violated) and then reinforce those constraints.
So:
SQL> create table t1(a number not null primary key,
2 b number);
Table created.
SQL> create table t2 ( c number ,
2 d number not null primary key);
Table created.
SQL> alter table t1 add constraint fk12 foreign key (b)
2 references t2(d) deferrable initially deferred;
Table altered.
SQL> alter table t2 add constraint fk21 foreign key (c)
2 references t1(a) deferrable initially deferred;
Table altered.
SQL>
Testing: referential integrity is checked when you COMMIT:
SQL> insert into t1 (a, b) values (1, 2);
1 row created.
SQL> insert into t2 (c, d) values (1, 2);
1 row created.
SQL> commit;
Commit complete.
SQL>
If you enter invalid combination (values that violate referential integrity), you won't be stopped at the moment on insert but COMMIT (as I've already said):
SQL> insert into t1 (a, b) values (5, 6);
1 row created.
SQL> insert into t2 (c, d) values (9, 9);
1 row created.
SQL> commit;
commit
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-02291: integrity constraint (SCOTT.FK12) violated - parent key not found
SQL>

Ensure combination of two columns existing in another table

How could one ensure the combination of two columns existing in another table?
CREATE TABLE a (
aid INTEGER PRIMARY KEY
);
CREATE TABLE b (
aid INTEGER REFERENCES a(aid),
bid INTEGER,
PRIMARY KEY (aid, bid)
);
CREATE TABLE c (
aid INTEGER REFERENCES a(aid),
cid INTEGER,
PRIMARY KEY (aid, cid)
);
-- combination of (aid, bid) must come from b
-- combination of (aid, cid) must come from c
CREATE TABLE d (
aid INTEGER REFERENCES a(aid),
bid INTEGER REFERENCES b(bid),
cid INTEGER REFERENCES c(bid),
PRIMARY KEY (aid, bid)
);
INSERT INTO a values(1);
INSERT INTO a values(2);
INSERT INTO b values(1, 1);
INSERT INTO b values(2, 2);
INSERT INTO c values(1, 1);
INSERT INTO c values(2, 2);
INSERT INTO d values(1, 2, 2);
Obviously the above "CREATE TABLE d" coding failed to ensure
combination of (aid, bid) must come from b
combination of (aid, cid) must come from c
Thanks.
In addition to Sticky bit and Vladimir Baranov's answers about using FOREIGN KEY and REFERENCES, for sqlite3 users:
"Foreign key constraints are disabled by default (for backwards compatibility), so must be enabled separately for each database connection." That means, the user has to open a database first, then run command "PRAGMA foreign_keys = ON;".
The pragma will not work if "the version of SQLite you are using does not support foreign keys (either because it is older than 3.6.19 or because it was compiled with SQLITE_OMIT_FOREIGN_KEY or SQLITE_OMIT_TRIGGER defined)".
You cannot define multicolumn foreign key constraints inline. Define them on their own.
CREATE TABLE d
(aid integer,
bid integer,
cid integer,
PRIMARY KEY (aid,
bid),
FOREIGN KEY (aid,
bid) REFERENCES b (aid,
bid),
FOREIGN KEY (aid,
cid) REFERENCES c (aid,
cid));
You didn't specify what RDBMS you are using. In SQL Server you can create a foreign key using more than one column. The key should point to columns that are in a unique or primary key and you do have the required primary keys in tables B and C.
The definitions of tables A, B and C can remain as they are in the question.
The definition of table D would look like this in SQL Server syntax. Here the foreign key constraints are created using separate ALTER TABLE statements. Different DBMS may use somewhat different syntax.
CREATE TABLE [dbo].[d](
[aid] [int] NULL,
[bid] [int] NULL,
[cid] [int] NULL
)
GO
ALTER TABLE [dbo].[d] WITH CHECK ADD CONSTRAINT [FK_d_b] FOREIGN KEY([aid], [bid])
REFERENCES [dbo].[b] ([aid], [bid])
GO
ALTER TABLE [dbo].[d] CHECK CONSTRAINT [FK_d_b]
GO
ALTER TABLE [dbo].[d] WITH CHECK ADD CONSTRAINT [FK_d_c] FOREIGN KEY([aid], [cid])
REFERENCES [dbo].[c] ([aid], [cid])
GO
ALTER TABLE [dbo].[d] CHECK CONSTRAINT [FK_d_c]
GO
It works as expected in my test:
INSERT INTO a values(1);
INSERT INTO a values(2);
INSERT INTO b values(1, 1);
INSERT INTO b values(2, 2);
INSERT INTO c values(1, 1);
INSERT INTO c values(2, 2);
(1 row affected)
(1 row affected)
(1 row affected)
(1 row affected)
(1 row affected)
(1 row affected)
Now let's try to insert into D:
INSERT INTO d values(1, 2, 2);
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_d_b".
The conflict occurred in database "TestDB", table "dbo.b".
The statement has been terminated.
These inserts go through, as expected:
INSERT INTO d values(1, 1, 1);
INSERT INTO d values(2, 2, 2);
(1 row affected)
(1 row affected)
OR you can use this syntax:
CREATE TABLE d2 (
aid INTEGER,
bid INTEGER,
cid INTEGER,
FOREIGN KEY (aid,bid) REFERENCES b (aid,bid),
FOREIGN KEY (aid,cid) REFERENCES c (aid,cid)
);
In this case the foreign keys would get some autogenerated names, but they will work the same:
INSERT INTO d2 values(1, 2, 2);
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__d2__351DDF8C".
The conflict occurred in database "TestDB", table "dbo.b".
The statement has been terminated.
INSERT INTO d2 values(1, 1, 2);
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__d2__361203C5".
The conflict occurred in database "TestDB", table "dbo.c".
The statement has been terminated.
These inserts work, as expected:
INSERT INTO d2 values(1, 1, 1);
INSERT INTO d2 values(2, 2, 2);
(1 row affected)
(1 row affected)
OR, you can use this syntax to give the constraints some meaningful names:
CREATE TABLE d3 (
aid INTEGER,
bid INTEGER,
cid INTEGER,
CONSTRAINT FK_d3_b FOREIGN KEY (aid,bid) REFERENCES b (aid,bid),
CONSTRAINT FK_d3_c FOREIGN KEY (aid,cid) REFERENCES c (aid,cid)
);

Unique constraint on two database columns with reverse direction

I'm stuck upon a following problem: Let's say I have a table with relations to itself:
CREATE TABLE ITEM (
ID NUMBER(18) PRIMARY KEY,
NAME VARCHAR2(100 CHAR) NOT NULL
);
ALTER TABLE ITEM ADD CONSTRAINT PK_ITEM PRIMARY KEY (ID);
CREATE TABLE ITEM_RELATION (
FIRST_ITEM_ID NUMBER(18) NOT NULL,
SECOND_ITEM_ID NUMBER(18) NOT NULL,
RELATION_TYPE VARCHAR2(1) NOT NULL
);
ALTER TABLE ITEM_RELATION ADD CONSTRAINT PK_ITEM_RELATION PRIMARY KEY (FIRST_ITEM_ID, SECOND_ITEM_ID, RELATION_TYPE);
--ALTER TABLE ITEM_RELATION ADD CONSTRAINT UK_ITEMS UNIQUE (FIRST_ITEM_ID, SECOND_ITEM_ID, RELATION_TYPE);
ALTER TABLE ITEM_RELATION ADD CONSTRAINT FK_FIRST_ITEM FOREIGN KEY (FIRST_ITEM_ID) REFERENCES ITEM(ID);
ALTER TABLE ITEM_RELATION ADD CONSTRAINT FK_SECOND_ITEM FOREIGN KEY (SECOND_ITEM_ID) REFERENCES ITEM(ID);
Now let's say, I wan't the relation to be directional, that is if item 1 has relation to item 2 of certain type, the item 2 shouldn't have the same relation to item 1.
That is, the following should not be permitted:
INSERT INTO ITEM (ID, NAME) VALUES (1, 'Item 1');
INSERT INTO ITEM (ID, NAME) VALUES (2, 'Item 2');
INSERT INTO ITEM_RELATION(FIRST_ITEM_ID, SECOND_ITEM_ID, RELATION_TYPE) VALUES (1, 2, 'R');
INSERT INTO ITEM_RELATION(FIRST_ITEM_ID, SECOND_ITEM_ID, RELATION_TYPE) VALUES (2, 1, 'R');
It means that the table ITEM_RELATION defines a direction of this connection, and it shouldn't be allowed to add a reversed relation.
Is it possible with Oracle DB?
You can do this with a unique index. In Oracle, you can use functions in indexes, so this will work:
create unique index unq_item_relation_3 on
item_relation(RELATION_TYPE ,
least(FIRST_ITEM_ID, SECOND_ITEM_ID),
greatest(FIRST_ITEM_ID, SECOND_ITEM_ID)
);
You could get the same effect with a check constraint, by requiring that FIRST_ITEM_ID be less than SECOND_ITEM_ID:
alter table item_relation add constraint chk_item_relation_2
check ((FIRST_ITEM_ID < SECOND_ITEM_ID);
However, this requires that the items be inserted in a particular order.

Relational table that is constrained to a subset of its referee

Say I have the following table:
create table foo (
id integer primary key,
mode integer not null check (mode in (1, 2))
);
Now I want to store additional information about records from this table, but only those that have, say, mode = 1. The way I thought I could do this is as follows:
create table foo_bar (
id integer primary key,
foo_id integer,
_mode integer not null default (1) check (_mode = 1),
foreign key (foo_id, _mode) references foo(id, mode)
);
That is, we have a dummy column in foo_bar that is forcibly always equal to 1 and include that in the foreign key constraint with foo.
However, not only does this not work (with pragma foreign_keys = ON;), but that foreign key constraint can be violated when you insert multiple values into foo!
sqlite> insert into foo(mode) values (1);
sqlite> insert into foo(mode) values (2);
sqlite> select * from foo;
1|1
2|2
sqlite> insert into foo_bar(foo_id) values (1);
Error: foreign key mismatch - "foo_bar" referencing "foo"
sqlite> insert into foo_bar(foo_id) values (2);
Error: foreign key mismatch - "foo_bar" referencing "foo"
sqlite> insert into foo(mode) values (1), (2);
Error: foreign key mismatch - "foo_bar" referencing "foo"
Is this a bug in SQLite (I'm using 3.17, it it matters), or am I doing it wrong?
An alternative option, without the foreign key, might be to use a trigger:
create trigger bad_mode
before insert on foo_bar when (select mode from foo where id = NEW.foo_id) <> 1
begin
select raise(fail, "Invalid mode");
end;
...but this seems a bit gross!
The documentation says:
Usually, the parent key of a foreign key constraint is the primary key of the parent table. If they are not the primary key, then the parent key columns must be collectively subject to a UNIQUE constraint or have a UNIQUE index.
That missing UNIQUE constraint is what causes the "foreign key mismatch" error.
It is not possible to use a subquery in a CHECK constraint, so the only way to enforce the mode = 1 constraint is to make it part of the foreign key constraint, or to use triggers.
I think you could improve the design, and no constraints other than FK are needed:
First of all foo.mode could be a foreign key to another table, say modes, no need to use a check constraint. You would store the values 1 and 2 there.
A second table, say bar_modes, could have a field mode, a FK to modes. You would store the value 1 in this table.
Finally, foo_bar.foo_id would be FK to foo.id, and foo_bar.mode a FK to bar_modes.mode.
Let me know what you think!
SQL Server example for M = 3, N = 5
create table modes (
id int primary key
)
create table foo (
id int primary key,
mode int not null foreign key references modes(id)
);
create table bar_modes (
id int primary key foreign key references modes(id)
)
create table foo_bar (
id int primary key,
foo_id int foreign key references foo(id),
other_data varchar(20)
)
insert modes (id) values(1)
insert modes (id) values(2)
insert modes (id) values(3)
insert modes (id) values(4)
insert modes (id) values(5)
insert bar_modes (id) values (1)
insert bar_modes (id) values (2)
insert bar_modes (id) values (3)
insert foo (id, mode) values (1000, 1)
insert foo (id, mode) values (2000, 2)
insert foo (id, mode) values (2500, 2)
insert foo (id, mode) values (5000, 5)
insert foo_bar (id, foo_id, other_data) values (100, 1000, 'data for foo 1000')
insert foo_bar (id, foo_id, other_data) values (200, 2000, 'data for foo 2000')
insert foo_bar (id, foo_id, other_data) values (250, 2500, 'data for foo 2500')