Can I use select statement with check constraint - sql

My table is parent-child table where it includes two columns. The first column is ID which is auto number. The other column is ParentID which has the special conditions. If the row is parent the ParentID = 0 otherwise it should be related to a ParentID row. For example:
**ID** | **ParentID**
---------------------
1 0
2 0
3 2
4 [it can be 0 or 1 or 2 NOT 3]
The ParentID depends on the ID column, if the ID includes ParentID = 0 then the ParentID can be that ID.
The question is can i do something like this??
constraint ParentID_ck ckeck(ParentID in(0, select ID from table where ParentID = 0;))
Or I should use other solution such as PL/SQL:
DECLARE
--Somthing..
BEGIN
--Somthing..
END;
And put it inside the check constraint.

You need to implement this constraint with combination of foreign key constraint and a trigger
You may use a null-able foreign key from the table to itself.(parentID == null shows the root nodes).
For the part of select ID from table where ParentID = 0 (null) of the constraint you may use a after insert or update DML trigger, some trigger examples could be found here

You can do something like:
insert into TABLE_NAME(PARENT_ID,ID)
(select <THE_PARENT_ID>, <ID> from DUAL where
exists (select 1 from TABLE_NAME where PARENT_ID = <THE_PARENT_ID>))
So that those PARENT_ID's which are actually not in the TABLE_NAME would not be inserted.

You can do this by fixing your data model first. Your table has two kinds of entities - Parents and Children. Create a separate table for your Parents, then you can use ordinary referential constraints.

Related

Postgresql: Unique constraint over Union of 2 columns

I have the following tables:
TRANSACTIONS
id | amount
------------------
1 | 100
2 | -100
3 | 250
4 | -250
TRANSACTION_LINKS
id | send_tx | receive_tx
---------------------------
1 | 2 | 1
2 | 4 | 2
The send_tx and receive_tx columns in the transaction links table use foreign keys pointing to the ID of the transactions table.
This is how I create the transaction links table
CREATE TABLE IF NOT EXISTS transaction_links
(
id BIGSERIAL PRIMARY KEY,
send_id INT NOT NULL UNIQUE REFERENCES transactions(id) ON DELETE
RESTRICT,
receive_id INT NOT NULL UNIQUE REFERENCES transactions(id) ON DELETE
RESTRICT
);
I want to create a unique constraint over both send_tx and receive_tx, meaning that if transaction id 1 is found in the receive_tx column, then
no other transaction link can have the receiving_tx = 1
no other transaction link can have the sending_tx = 1
I know that I can have a unique constraint on each column separately, but that only solves my first problem
EDIT:
essentially, if I insert (1,2) into transaction links, then inserting (1,3) or (3,1) or (4,2) or (2,4) should all be rejected
Also, in my design, the transactions table contains many more columns than what is shown here, I've only included the amount for simplicity's sake.
You can use an exclusion constraint which only requires a single index:
alter table transaction_links
add constraint check_tx
exclude using gist ( (array[send_id, receive_id]) with &&);
The && operator is the "overlaps" operator for arrays - which means "have elements in common, regardless of the order of the elements in the array. In this case the constraint prevents to insert any row where any value of (send_id, receive_id) appears in some other row of the table (regardless of the column).
However, you need the intarray extension for that.
Online example: https://rextester.com/QOYS23482

Insert data in table, update other table with FK in one query

I have one Table A with 3 attributes:
ID (PK), UNIT(varchar2), VALUE(number)
And I have another Table B that references the PK of Table A via a foreign key.
ID, FK_TABLE_A
In Table B are already records, but the value of the attribute FK_TABLE_A is null. What I want is, that all records in Table B get an own unique reference to Table A's PK by inserting new data into Table A and referencing the newly created primary key as foreign key into Table B.
What I have done so far:
I can now insert new data via following SQL:
INSERT INTO TABLE_A(ID, UNIT, VALUE) VALUES (TABKE_A_SEQ.nextval, 'SOME_STRING', 1);
And I can update by hand the refernce to Table B
UPDATE TABLE_B SET FK_TABLE_A = 123; //123 is just an example PK
But I do not want to query for each record everytime the DB, but instead want to combine somehow the first insert with the second update in one query.
So that the update takes the newly created PK in Table A as reference.
Is this possible?
I am using Oracle as DB.
Once you've used the sequence in your session, you can use the currval pseudocolumn to get the last sequence value issued in your session, by the last nextval call:
INSERT INTO TABLE_A(ID, UNIT, VALUE) VALUES (TABKE_A_SEQ.nextval, 'SOME_STRING', 1);
UPDATE TABLE_B SET FK_TABLE_A = TABKE_A_SEQ.currval;
(Although as you don't have a filter, that will update all rows in table B to the same FK value; presumably you're doing something more complicated to identify the relevant rows...)
If you want a one-to-one relationship and don't care which row gets which PK value, and you can make the constraint deferrable, you could approach it the other way around; update all the table B rows using the sequence, then create the table A rows using those.
With a quick demo table:
create table table_a (id number primary key, unit varchar2(20), value number);
create table table_b (id number,
fk_table_a number references table_a(id) initially deferred deferrable);
create sequence table_a_seq;
create sequence table_b_seq start with 50;
insert into table_b (id) select table_b_seq.nextval from dual connect by level <= 5;
Then, in a single transaction, update all the rows and do a single insert:
update table_b set fk_table_a = table_a_seq.nextval;
insert into table_a (id, unit, value)
select fk_table_a, 'SOME_STRING', 1
from table_b;
The constraint has to be deferrable to allow the update to happen first; otherwise you'd get ORA-02291.
If the unit/value are coming from table_a you can include those in the query instead of using fixed literals. It's hard to tell what you actually need for those, and you said they could even be left null.
Now you have:
select * from table_a;
ID UNIT VALUE
---------- -------------------- ----------
1 SOME_STRING 1
2 SOME_STRING 1
3 SOME_STRING 1
4 SOME_STRING 1
5 SOME_STRING 1
select * from table_b;
ID FK_TABLE_A
---------- ----------
50 1
51 2
52 3
53 4
54 5
Use a trigger (on TABLE_A) to create the row in TABLE_B.
For example (assuming you are using a SEQUENCE to generate the PK)
CREATE OR REPLACE TRIGGER TR_TABLE_A_AI
AFTER INSERT
ON TABLE_A
FOR EACH ROW
BEGIN
INSERT INTO TABLE_B VALUES (SEQ_TABLE_B.NEXTVAL, :NEW.ID);
END;

How to delete nested child using Trigger in SQLite

I need to delete nested items in my table using triggers. I have a folder table, table have a Id and parrentID. For example
ID FolderName ParentID
--------------------------------
1 A Null
2 B 1
3 C 2
4 D 3
If i delete ID 1 then it will be delete all the record 2,3,4 because 1 is a top parent. There is any way?
The documentation says that
[recursion] cannot be used within a CREATE TRIGGER.
However, if you declare the relationship correctly, you can tell the database to delete the children automatically:
CREATE TABLE MyTable(
ID INTEGER PRIMARY KEY,
FolderName TEXT,
ParentID INTEGER REFERENCES MyTable(ID) ON DELETE CASCADE
);

How to make an index with an optional FK?

##Original question##
So the business model, which I didn't create, have an optional relationship (as in ER model). It's been a while since I've worked DB so I might be forgetting something. Currently the FK (Foreign Key) of the 1st table point to the PK (Primary Key) of the 2nd table, which is an ID ; I don't recall the term but it's the "fake" one, not the "real" one used by the RDBMS (Relational Database Management System). For simplicity, let's imagine there's only 2 tables.
Currently I'm having nulls in the FK column/attribute when there's no need for the optional relation. When there is an item in that column, I want the full advantages, checking if there's a matching item at the other side of the relationship, where the FK point to (2nd table), also triggers (although there are currently none) and other validations. I was satisfied up to not long ago when I realized I didn't want a duplicate on the important parts of the 1st table, so I wanted to create an unique key but it seems a key cannot be created which include a column/attribute that might contain null. So far there's 2 solutions proposed to me although I understand neither.
The 1st was that I'd put defaults, 0 for digit-based types and an empty string ('') for character-based types. What I don't get for that is that the 2nd table already has a row/tuple with a corresponding value (0). If I was to shift the current rows to not have a row with the default, I assume I then would put in the corresponding content a default too, in my case it's a character-based type. So the "cost" of enabling an index would be to make a multitude of useless joins then a multitude of useless merges by the software, in my case the database section of an office suite, Apache OpenOffice Base. This seem like a lot of added processing and it seem to me some kind of trigger, along with my current design, would be better.
The 2nd was to make a "linked" table (his/her term), a many-to-many relationship but I thought those were only for entries that had more than 1 possible relationship ; that having 0-1 relationship would not use it. And anyway, I'd still be confronted with the same problem, where there would not need to have an entry in that "linked" table. IIRC, the 2 "sides" of such table must contain valid candidate keys.
So the 1-1 relationship is already implemented for the cases where the business model do have the need for that option, with the current non-null entries in the FK. Now I just have to implement a method for the cases when the business model do not need the optional part, to allow for a 0-1 relationship, for the current null entries in the FK while not allowing duplicates.
##fredt request##
This now contain the 3rd example.
The followind sub-section contain a semi-SQL export from Apache OpenOffice Base using the command SCRIPT 'PATH\TO\NAME.sql'. The original file, along with its export, and its non-exported queries, are on How to make an index with an optional FK? example 3.
I'd like a unique key on the 3 columns/attributes ID_to_part1, model_number & ID_to_part2 ; however the original question, in the previous section, show HSQLDB version 1.8.0.10 won't allow a null to be contained in a column which is part of a unique key.
###HSQLDB export###
Producing some kind of SQL ; including non-standard statements.
SET DATABASE COLLATION "Latin1_General"
CREATE SCHEMA PUBLIC AUTHORIZATION DBA
CREATE CACHED TABLE "Table1"("ID" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,"ID_to_part1" INTEGER NOT NULL,"model_number" VARCHAR_IGNORECASE(3) NOT NULL,"ID_to_part2" INTEGER)
CREATE CACHED TABLE "Table2"("ID" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,"content" VARCHAR_IGNORECASE(1) NOT NULL)
CREATE CACHED TABLE "Table3"("ID" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,"content" VARCHAR_IGNORECASE(1) NOT NULL)
ALTER TABLE "Table1" ADD CONSTRAINT SYS_FK_87 FOREIGN KEY("ID_to_part1") REFERENCES "Table3"("ID") ON DELETE CASCADE ON UPDATE CASCADE
ALTER TABLE "Table1" ADD CONSTRAINT SYS_FK_90 FOREIGN KEY("ID_to_part2") REFERENCES "Table2"("ID") ON DELETE SET NULL ON UPDATE CASCADE
ALTER TABLE "Table1" ALTER COLUMN "ID" RESTART WITH 15
ALTER TABLE "Table2" ALTER COLUMN "ID" RESTART WITH 2
ALTER TABLE "Table3" ALTER COLUMN "ID" RESTART WITH 4
CREATE USER SA PASSWORD ""
GRANT DBA TO SA
SET WRITE_DELAY 0 MILLIS
SET SCHEMA PUBLIC
INSERT INTO "Table1" VALUES(0,0,'123',0)
INSERT INTO "Table1" VALUES(1,1,'456',NULL)
INSERT INTO "Table1" VALUES(2,2,'789',0)
INSERT INTO "Table1" VALUES(3,0,'012',1)
INSERT INTO "Table1" VALUES(6,3,'345',NULL)
INSERT INTO "Table1" VALUES(7,1,'678',1)
INSERT INTO "Table1" VALUES(8,0,'123',NULL)
INSERT INTO "Table1" VALUES(9,0,'123',1)
INSERT INTO "Table1" VALUES(10,1,'456',0)
INSERT INTO "Table1" VALUES(11,1,'456',1)
INSERT INTO "Table1" VALUES(12,1,'456',0)
INSERT INTO "Table1" VALUES(13,1,'123',NULL)
INSERT INTO "Table1" VALUES(14,1,'123',0)
INSERT INTO "Table2" VALUES(0,'B')
INSERT INTO "Table2" VALUES(1,'E')
INSERT INTO "Table3" VALUES(0,'A')
INSERT INTO "Table3" VALUES(1,'C')
INSERT INTO "Table3" VALUES(2,'D')
INSERT INTO "Table3" VALUES(3,'F')
It seem queries weren't exported, here they are followed by their results
###Query1###
Joined main table:
SELECT "Table1"."ID", "Table3"."content" AS "Table3_content", "Table1"."model_number", "Table2"."content" AS "Table2_content"
FROM "Table1"
LEFT OUTER JOIN "Table2" ON "Table1"."ID_to_part2" = "Table2"."ID"
LEFT OUTER JOIN "Table3" ON "Table1"."ID_to_part1" = "Table3"."ID"
ORDER BY "ID" ASC
Result in:
ID Table3_content model_number Table2_content
0 A 123 B
1 C 456
2 D 789 B
3 A 012 E
6 F 345
7 C 678 E
8 A 123
9 A 123 E
10 C 456 B
11 C 456 E
12 C 456 B
13 C 123
14 C 123 B
###Query2###
The rows/tuples which 2 first part of the unique index could "break" the desired unique index should the 3rd also match. In other words, other rows aren't a threat (Query1 minus Query2).
SELECT *
FROM "Table1"
-- It seem HSQLDB won't support tuples as in WHERE (col1, col2) IN ( SELECT col1, col2 FROM
WHERE "ID_to_part1" IN (
SELECT "ID_to_part1"
FROM "Table1"
GROUP BY "ID_to_part1", "model_number"
HAVING COUNT(*) > 1
) AND "model_number" IN (
SELECT "model_number"
FROM "Table1"
GROUP BY "ID_to_part1", "model_number"
HAVING COUNT(*) > 1
)
ORDER BY "ID_to_part1" ASC, "model_number" ASC, "ID_to_part2" ASC, "ID" ASC
Result in:
ID ID_to_part1 model_number ID_to_part2
8 0 123
0 0 123 0
9 0 123 1
13 1 123
14 1 123 0
1 1 456
10 1 456 0
12 1 456 0
11 1 456 1
###Query3###
The rows/tuples which would "break" the desired unique index.
SELECT "Table1".*
FROM "Table1"
JOIN (
SELECT "ID_to_part1", "model_number", "ID_to_part2"
FROM "Table1"
GROUP BY "ID_to_part1", "model_number", "ID_to_part2"
HAVING COUNT(*) > 1
) AS "non_unique_model"
ON "Table1"."ID_to_part1"="non_unique_model"."ID_to_part1"
AND "Table1"."model_number"="non_unique_model"."model_number"
AND "Table1"."ID_to_part2"="non_unique_model"."ID_to_part2"
ORDER BY "ID_to_part1" ASC, "model_number" ASC, "ID_to_part2" ASC, "ID" ASC
Result in:
ID ID_to_part1 model_number ID_to_part2
10 1 456 0
12 1 456 0
###Beautified important tables schema###
CREATE CACHED TABLE "Table1"(
"ID" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,
"ID_to_part1" INTEGER NOT NULL,
"model_number" VARCHAR_IGNORECASE(3) NOT NULL,
"ID_to_part2" INTEGER
)
CREATE CACHED TABLE "Table2"(
"ID" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,
"content" VARCHAR_IGNORECASE(1) NOT NULL
)
Welcome to SO! I find your question a little hard to read.
EDIT:
CREATE TABLE table1 (
id INTEGER NOT NULL PRIMARY KEY,
data1 INTEGER NOT NULL
);
CREATE TABLE table2 (
id INTEGER NOT NULL PRIMARY KEY REFERENCES table1(id),
data2 INTEGER NOT NULL
);
There are records in table1. For each record in table1, there is zero or one corresponding record in table2.
This pattern is similar to table inheritance.
Further explanation:
This would allow you to have to following data.
id data1 id data2
---------- ---------
0 1234 0 42
1 5678 2 57
2 9012
See that the records in table1 with ids 0 and 2 have corresponding records in table2. The record with id 1 does not.
P.S.
Note that you also could combine things into one table. Whether this is advisable depends on your situation.
CREATE TABLE table1 (
id INTEGER NOT NULL PRIMARY KEY,
data1 INTEGER NOT NULL,
data2 INTEGER NULL
);
I wanted to create an unique key but it seems a key cannot be created
which include a column that might contain null.
My understanding is that you have a FK on which you want to build an index on to enhance performance and that FK may contain nulls (as in #Paul Draper's solution).
I am no expert in HSQLDB, but the user guide, under the Constraints section says:
"Since version 1.7.2 the behaviour of UNIQUE constraints and indexes with respect to NULL values has changed to conform to SQL standards. A row, in which the value for any of the UNIQUE constraint columns is NULL, can always be added to the table. So multiple rows can contain the same values for the UNIQUE columns if one of the values is NULL."
I understand this to mean that you can build an index on the FK in version 1.7.2 of the database even if the column conain rows with the FK value is Null.
Your question was:
I didn't want a
duplicate on the important parts of the 1st table, so I wanted to
create an unique key but it seems a key cannot be created which
include a column that might contain null.
You don't want a duplicate on the "important parts" in Table1 but it is not clear which parts must be unique. Assuming the "important parts" are some of these three columns:
"ID_to_part1" INTEGER,"model_number" VARCHAR_IGNORECASE(3) NOT NULL,"ID_to_part2" INTEGER
A) If you create a unique constraint on "model_number", which is by definition NOT NULL:
CONSTRAINT UNIQUE ("model_number")
Then model_number values are unique but two different models can have the same ID_to_part1
B) In addition to (A) you can have this constraint:
CONSTRAINT UNIQUE ("model_number", "ID_to_part1")
Then each model_number will correspond to a unique ID_to_part1. If you don't have NOT NULL on ID_to_part1, then it can contain NULL for those model_number values that do not have an extra part.
C) In addition to (A) you can have this:
CONSTRAINT UNIQUE ("model_number", "ID_to_part2")
Which has the same effect as (B) but for the ID_to_part2 column.
Your SELECT statement is correct. It shows all models with any optional information they may have.
In short, you can have a UNIQUE constraint on columns that can have NULL in them. But the UNIQUE constraint on model_number is also required.
Edit:
The OP has edited the question again with the requirement that "model_number" is not unique, only the three columns together are unique while some of them can store NULL and the NULL cannot be repeated. This is not possible to achieve with HSQLDB 1.8. In HSQLDB 2.x there is a setting for SET DATABASE SQL UNIQUE NULLS which can be changed to FALSE to allow this. In this case only one UNIQUE constraints on the three columns is needed.

Constrain a table such that each account can have one of another table

I have a table which has these columns:
Id (Primary Key): the id.
OwnerId (Foreign Key): the id of the owner, which resides in another table.
TypeId (Foreign Key): the type of thing this record represents. There are a finite number of types, which are represented in another table. This links to that table.
TypeCreatorId (ForeignKey): the owner of the type represented by TypeId.
SourceId (Foreign Key): this isn't important to this question.
I need to constrain this table such that for each Id, there can be only one of each TypeCreatorId. I hope that makes sense!
For SQL Server, you have two options:
create a UNIQUE CONSTRAINT
ALTER TABLE dbo.YourTable
ADD CONSTRAINT UNIQ_Id_TypeCreator UNIQUE(Id, TypeCreatorId)
create a UNIQUE INDEX:
CREATE UNIQUE INDEX UIX_YourTable_ID_TypeCreator
ON dbo.YourTable(Id, TypeCreatorId)
Basically, both things achieve the same thing - you cannot have two rows with the same (Id, TypeCreatorId) values.
Simply create a unique index on OwnerId and TypeCreatorId.
An example using MySQL (sorry, I don't use SQL Server):
alter table yourTable
add unique index idx_newIndex(OwnerId, TypeCreatorId);
Example. I'll just put here what would happen with this new unique index:
OwnerId | TypeCreatorId
--------+--------------
1 | 1
1 | 2 -- This is Ok
2 | 1 -- Ok too
2 | 2 -- Ok again
1 | 2 -- THIS WON'T BE ALLOWED because it would be a duplicate