H2 - Create unique index with two columns - sql

currently I am using a postgres query to create two unique indexes.
Each index consists of two columns, where the value of one column is checked for null/not null:
CREATE UNIQUE INDEX deleted_not_null_idx
ON user (ADDRESS, DELETED)
WHERE DELETED IS NOT NULL;
CREATE UNIQUE INDEX deleted_null_idx
ON user (ADDRESS)
WHERE DELETED IS NULL;
I am attempting to do the same on H2 but I am having issues understanding the syntax and structure of H2.
How would this expression be formed if written using H2 syntax?

A workaround to ensure "uniqueness of the columns on a subset of rows" can be worked out if you are willing to add an artificial extra column to the table, just for this purpose. Not sure it's the best idea, but can do the job.
For example:
create table t (
address varchar(20),
deleted int,
extra_column varchar(20) as
case when deleted is null then null else address end,
constraint uq1 unique (extra_column)
);
insert into t (address, deleted) values ('123 Maple', 20);
insert into t (address, deleted) values ('456 Oak', 25);
insert into t (address, deleted) values ('456 Oak', null); -- succeeds
insert into t (address, deleted) values ('456 Oak', 28); -- fails
Result:
select * from t;
ADDRESS DELETED EXTRA_COLUMN
--------- ------- ------------
123 Maple 20 123 Maple
456 Oak 25 456 Oak
456 Oak <null> <null>

Related

I need to validate data in a Table where 1 column has multiple values but 1 value can only be present in 1 row by unique key

Example:
A table Keyed by Unique Name and Email Address with a column for Type
The Type column can have Original, Work, Personal
where you can have multiple work and personal emails but only 1 Original email
I am using DB2 for i SQL and I want to constrain the data using UNIQUE or CHECK constraints but not sure how I can do this data set.
Scott scott#hotmail.com Original
Scott scott#gmail.com Personal
Scott scott#live.com Personal
Scott scott#NBC.com Work
Scott scott#ABC.com Work
Scott scott#yahoo.com Original
I want to identify that I cant have yahoo as Original if I already have hotmail as original.
the rest are valid.
Let me know if I need to add more.
If you have Db2 for IBM i, then you may create a UNIQUE INDEX with the corresponding WHERE clause.
CREATE TABLE TEST_IND_EXPR
(
NAME VARCHAR (20) NOT NULL
, EMAIL VARCHAR (20) NOT NULL
, TYPE VARCHAR (20) NOT NULL
);
CREATE UNIQUE INDEX TEST_IND_EXPR1 ON TEST_IND_EXPR (NAME, EMAIL);
CREATE UNIQUE INDEX TEST_IND_EXPR2 ON TEST_IND_EXPR (NAME, TYPE) WHERE TYPE = 'Original';
INSERT INTO TEST_IND_EXPR VALUES ('Scott', 'scott#hotmail.com', 'Original');
INSERT INTO TEST_IND_EXPR VALUES ('Scott', 'scott#gmail.com', 'Personal');
INSERT INTO TEST_IND_EXPR VALUES ('Scott', 'scott#live.com', 'Personal');
INSERT INTO TEST_IND_EXPR VALUES ('Scott', 'scott#yahoo.com', 'Original');
The last statement returns SQL0803 as this row violates uniqueness of the TEST_IND_EXPR2 index.

PostgreSQL to find the next available value

I have followed the example here to find the next available value on a table column: the generated value will be used by an application to insert data in another table. But, if multiple concurrent application instances run the same query, some of these instances could get the same value. How could I avoid these collisions without change the application? Is it possible write a PostreSQL function to handle this task?
You can use an IDENTITY column or a SEQUENCE.
Identity Column Example
create table t (
id int primary key not null generated always as identity,
name varchar(10)
);
insert into t (name) values ('New York');
insert into t (name) values ('Chicago');
Result:
id name
--- --------
1 New York
2 Chicago
Each INSERT statement will produce a different value for the id column, even when they are executed on separate simultaneous threads.
Sequence Example
create table u (
id int primary key not null,
name varchar(10)
);
create sequence sequ;
insert into u (id, name) values (nextval('sequ'), 'New York');
insert into u (id, name) values (nextval('sequ'), 'Chicago');
Result:
id name
--- --------
1 New York
2 Chicago
Again, each INSERT statement will produce a different value for the id column, even when they are executed on separate simultaneous threads.
See running example for both cases at DB Fiddle.

Is there any way to reference column with different datatype?

I have 2 schemas/tables as shown:
CREATE TABLE schema1.code_tbl
( code CHAR(6) PRIMARY KEY,
description CHAR(30)
);
CREATE TABLE schema2.record_tbl
( rec_id VARCHAR(10) PRIMARY KEY,
curr_code VARCHAR(6),
remarks VARCHAR(30)
);
I need to create a foreign key reference from curr_code in RECORD_TBL to code in CODE_TBL.
ALTER TABLE schema2.record_tbl
ADD CONSTRAINT record_code_fk
FOREIGN KEY (curr_code)
REFERENCES schema1.code_tbl (code);
This obviously gives me an ORA-02267 (column type incompatible with referenced column) error.
I cannot alter the code column in CODE_TBL because I do not own or control schema1. I cannot alter the curr_code column in RECORD_TBL because it would break many functions in my application because we don't account for trailing whitespaces.
Is there any other way to enforce referential integrity between the 2 columns?
If schema2 is on Oracle 11g and above, using virtual column may solve your problem, but that will change the structure of your table which you are trying to avoid. If you can manage it, here is how it can be done
SQL> CREATE TABLE code_tbl
2 ( code CHAR(6) PRIMARY KEY,
3 description CHAR(30)
4 );
Table created
SQL>
SQL> CREATE TABLE record_tbl
2 ( rec_id VARCHAR2(10) PRIMARY KEY,
3 curr_code VARCHAR2(6),
4 remarks VARCHAR2(30)
5 );
Table created
SQL> INSERT INTO code_tbl(code, description) VALUES ('ABC', 'Test Data');
1 row inserted
SQL> INSERT INTO record_tbl(rec_id, curr_code, remarks) VALUES ('1', 'ABC', 'Test Row');
1 row inserted
SQL> SELECT * FROM record_tbl;
REC_ID CURR_CODE REMARKS
---------- --------- ------------------------------
1 ABC Test Row
SQL> SELECT * FROM code_tbl;
CODE DESCRIPTION
------ ------------------------------
ABC Test Data
SQL> ALTER TABLE record_tbl ADD curr_code_v CHAR(6) AS (trim(curr_code));
Table altered
SQL> SELECT * FROM record_tbl;
REC_ID CURR_CODE REMARKS CURR_CODE_V
---------- --------- ------------------------------ -----------
1 ABC Test Row ABC
SQL>
SQL> ALTER TABLE record_tbl
2 ADD CONSTRAINT record_code_fk
3 FOREIGN KEY (curr_code_v)
4 REFERENCES code_tbl (CODE);
Table altered
SQL> INSERT INTO record_tbl(rec_id, curr_code, remarks) VALUES ('2', 'ABC', 'Test Row 2');
1 row inserted
SQL> INSERT INTO record_tbl(rec_id, curr_code, remarks) VALUES ('3', 'XYZ', 'Test Row 2');
INSERT INTO record_tbl(rec_id, curr_code, remarks) VALUES ('3', 'XYZ', 'Test Row 2')
ORA-02291: integrity constraint (USER_X.RECORD_CODE_FK) violated - parent key not found
Here are the words from Tom Kyte regarding virtual columns:
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:676611400346196844
So the situation is this. You have an existing table record_tbl which is well established ( obviously, because changing it "break many functions"). Belatedly somebody has decided to enforce relational integrity on this table but has chosen to do so referencing a table in a different schema with a column of a different datatype.
Hmmmm.
Your options are:
Do nothing. Always an option; your application has survived with the current state for some length of time, maybe you can continue to live with the accrued technical debt.
Refactor one of the schemas. If you need to enforce the foreign key - and let's face it, relational integrity is a good thing - then you are going to have to change the datatype of one of the columns. Which one you choose is a project decision: changing a column in a schema you don't own is a political problem (initially), and political problems are usually harder than technical problems. Refactoring a schema by changing a column type is a matter of testing, testing, testing.
Replication. Build a materialized view in schema2 which copies the data from schema1.code_tbl. Crucially, define the MView code column match the datatype of schema2.record_tbl.curr_code i.e. varchar2(6). You will now be able to enforce a foreign key against schema2.mv_code_tbl.code. Note: the data in the MView column will be formatted as CHAR i.e. with trailing spaces.
You can not create relation in different type column.
Why you must create relation in database? You can connect the table using only the code.

Postgres SERIAL add value after error INSERT command

I created a table
CREATE TABLE street (
id SERIAL PRIMARY KEY NOT NULL,
street_name CHAR (30) NOT NULL,
city_id INTEGER REFERENCES city,
building_number CHAR(10));
after that I insert some data:
INSERT INTO street (street_name, city_id) VALUES ('Sumskaya', 1);
The data was added with id=1. Then I insert next data
INSERT INTO street (street_name, city_id) VALUES ('Sumskaya', 10);
and get the error
Key (city_id)=(10) is not present in table "city".
I changed my insert data
INSERT INTO street (street_name, city_id) VALUES ('Sumskaya', 2);
and get a row in the table with id = 3. Id = 2 is missing. Why serial assigned the value 3, not 2 and how to change it?
Serials internally use sequences. For concurrency reasons, sequences do not roll back. They just move forward. Imagine if you had two clients inserting at the same time.
Client 1 inserts a row, goes on to do some other work.
Client 2 inserts a row, goes on to do some other work
client 1 commits.
Client 1 inserts another row, goes on to do some other work
client 2 errors and rolls back.
We would expect values with an id of 1 and 3, and 2 just gets omitted.Anything else and we have problems.
If you truly need gapless nubering then you have to use a separate table and row locks, but then you cannot have concurrent clients inserting....

SQL set UNIQUE only for two columns

I would like for example that user can add name JHONE and age 25, so next time he can add JHONE, 26 or ALEX 25, BUT not JHONE, 25 again.
So I'm looking for two column unique NOT separately.
P.S. I'm sorry if same question was mentioned before.
EDIT:
This is my example:
Would like to make userIdG and doWithCar will be like this
102163096246025413003 View
102163096246025413003 Buy
102163096246025413003 Let
102163096246025413003 Sell
And for Id = 102163096246025413003 you can't add any more values, BECAUSE column doWithCar will have only 4 possible choice view, buy, rent and sell
You could specify more than one column in UNIQUE:
CREATE TABLE tab(ID INT IDENTITY(1,1) PRIMARY KEY, name VARCHAR(100), age INT
,UNIQUE(name, age));
INSERT INTO tab(name, age) VALUES ('John', 25);
INSERT INTO tab(name, age) VALUES ('John', 26);
-- INSERT INTO tab(name,age) VALUES ('John', 25);
-- Violation of UNIQUE KEY constraint 'UQ__tab__CF0426FD76D3370A'.
-- Cannot insert duplicate key in object 'dbo.tab'.
-- The duplicate key value is (John, 25).
-- The statement has been terminated.
SELECT * FROM tab;
LiveDemo
Note:
You should store date of birth and not age itself (or make age calculated column and set UNIQUE(name, dob)).
this is what I do not understand) how database will know that it should be two columns as unique and not each column is unique
These are different concepts. DB "knows" it from UNIQUE constraint definition:
UNIQUE(userIdG,doWithCar) -- pair of column is unique
!=
UNIQUE(userIdG),UNIQUE(doWithCar) -- each column is unique