I have a varchar column in my table for url value. I have to make it unique across the records case-insensitively.
I found 2 ways to achieve it.
Create an unique index on the field.
create unique index <index_name> on <tablename>(lower(<column_name>))
Add a unique constraint on the field as
ALTER TABLE person ADD CONSTRAINT person_name_unique
UNIQUE(LOWER(first_name),LOWER(last_name));
What is the efficient way to adopt from the above choices ?
The more efficient approach is the first approach. It's more efficient, though, only because the latter syntax doesn't work. You cannot, unfortunately, create a function-based constraint in the same way that you can create a unique index.
A unique constraint doesn't work
SQL> create table person (
2 first_name varchar2(10),
3 last_name varchar2(10)
4 );
Table created.
SQL> ALTER TABLE person ADD CONSTRAINT person_name_unique
2 UNIQUE(LOWER(first_name),LOWER(last_name));
UNIQUE(LOWER(first_name),LOWER(last_name))
*
ERROR at line 2:
ORA-00904: : invalid identifier
A unique function-based index, however, does work
SQL> create unique index idx_uniq_name
2 on person( lower(first_name), lower(last_name) );
Index created.
1 is possible and gives error for duplicate.
2 is not possible. (function is not possible in constraint)
Related
I have three tables named as :
Staff(id,name,address);
Members(id,name,address);
Plans(id,Userid,type);
I want to create Userid from Plans foreign key for both Staff and Members table
How can i do it?
Umm ... not like that.
Staff and members should be stored in the same table, with additional column (and table to be referenced) which says whether someone is "staff" or "member". Then plans table doesn't have a problem any more.
SQL> create table type_sm
2 (id_sm number constraint pk_tsm primary key,
3 name varchar2(20) constraint ch_typ check (name in ('staff', 'member'))
4 );
Table created.
SQL> create table staff_members
2 (id_sta_mem number constraint pk_stamem primary key,
3 id_sm number constraint fk_sm_typ references type_sm (id_sm),
4 name varchar2(20),
5 address varchar2(30)
6 );
Table created.
SQL> create table plans
2 (id_pla number constraint pk_pla primary key,
3 id_sta_mem number constraint fk_plastamem references staff_members (id_sta_mem)
4 );
Table created.
SQL>
I've been struggling for some time now to figure out a way to create a composite foreign key in liquibase.
I have a table A which has a composite PK, let's say (id1, id2). I'm trying to make another table B, in which the A.PK is mapped as a FK.
I'm using liquibase with YAML and something doesn't seem to add up.
I've tried adding the FK when creating the table (so in the column tag)
- column:
name: id1_id2
type: int
constraints:
nullable: false
foreignKeyName: fk_id1_id2
references: A(id1, id2)
Unfortunately this syntax returns an error:
Caused by: java.sql.SQLSyntaxErrorException: ORA-02256: number of referencing columns must match referenced columns
Another thing that I've tried is creating the table first, with the column for the desired FK and try to add a FK constraint on that column. This doesn't throw any error but it does nothing (also the log for LB says "empty" in the description)
changes:
- addForeignKeyContraint:
baseColumnNames: id1, id2
baseTableName: B
constraintName: fb_id1_id2
referencedColumnNames: id1, id2
referencedTableName: A
Any help would be much appreciated.
Thanks
Have you tried addForeignKeyConstraint? Something like this:
- changeSet:
id: 1
author: you
changes:
- addForeignKeyConstraint:
baseColumnNames: id1, id2
baseTableName: tableB
constraintName: FK_tableB_tableA
referencedColumnNames: id1, id2
referencedTableName: tableA
I don't use Liquibase, but here's how it is supposed to look like, as far as Oracle is concerned: if you want to create a composite foreign key (in the detail table), then it has to reference a composite primary key (in the master table).
Have a look at this example:
SQL> create table master
2 (id_1 number,
3 id_2 number,
4 constraint pk_mas primary key (id_1, id_2));
Table created.
SQL> create table detail
2 (id_det number constraint pk_det primary key,
3 --
4 id_1 number,
5 id_2 number,
6 constraint fk_det_mas foreign key (id_1, id_2) references master (id_1, id_2));
Table created.
SQL>
It just wouldn't work otherwise; that's why you got the error
ORA-02256: number of referencing columns must match referenced columns
because your detail table contained a single column (id1_id2) and tried to reference two columns in table A (id1, id2).
I have a table and this table has relationship with itself as many-to-many. So i am create another table (second table) that stores two id for composite primary key which comes from original (first) table. But if in second table there is id1=1 and id2=2, then second table shouldn't have id1=2 and id2=1.
So how can i do that, should i write a trigger for that or is there a simple way for oracle.
I use Oracle11g and pl/sql developer.
You can try defining a unique function-based index that automatically defines the index in numerical order. This would ensure that only one of the 2 combinations is ever allowed. Something like:
create unique index your_index on your_table(
least(id1, id2),
greatest(id1, id2)
);
If it matters, there is a slight difference between this approach and MT0's answer that uses the check constraint.
With the check constraint approach, only (id1=1, id2=2) is valid.
With the function-based index approach, both (id1=1, id2=2) and (id1=2, id2=1) are valid, but they can't both be present in the table at any given time.
CREATE TABLE table_name (
id INT PRIMARY KEY
);
CREATE TABLE table_name_many_to_many(
id1 INT REFERENCES table_name( id ),
id2 INT REFERENCES table_name( id ),
CONSTRAINT tnmtm__id1__id2__pk PRIMARY KEY ( id1, id2 ),
CONSTRAINT tnmtm__id1__id2__chk CHECK ( id1 <= id2 )
);
I have two tables (master-detail) I use to record orders, I need to create a trigger that allows me to update the "TOTAL_GENERAL" field that is in the master table with the sum of subtotals in the "SUBTOTAL" field the detail table that are related to the foreign key "ID_ORDEN" but I get an error with the trigger.
tables:
CREATE TABLE "ENCABEZADO_ORDEN"
("ID_ENCABEZADO" NUMBER(10,0),
"NUMERO_ORDEN" NUMBER(10,0),
"FECHA" DATE,
"NOMBRE_CLIENTE" VARCHAR2(50),
"DIRECCION" VARCHAR2(50),
"TOTAL_GENERAL" NUMBER(10,0),
"LUGAR_VENTA" VARCHAR2(50),
CONSTRAINT "ENCABEZADO_ORDEN_PK" PRIMARY KEY ("ID_ENCABEZADO")
USING INDEX ENABLE
)
CREATE TABLE "DETALLE_ORDEN"
("ID_DETALLE" NUMBER(10,0),
"PRODUCTO" VARCHAR2(50),
"PRECIO_UNITARIO" NUMBER(10,2),
"CANTIDAD" NUMBER(10,0),
"SUBTOTAL" NUMBER(10,2),
"ID_ENCABEZADO" NUMBER(10,0),
CONSTRAINT "DETALLE_ORDEN_PK" PRIMARY KEY ("ID_DETALLE")
USING INDEX ENABLE
)
/
ALTER TABLE "DETALLE_ORDEN" ADD CONSTRAINT "DETALLE_ORDEN_FK" FOREIGN KEY ("ID_ENCABEZADO")
REFERENCES "ENCABEZADO_ORDEN" ("ID_ENCABEZADO") ENABLE
/
trigger:
create or replace TRIGGER "CALCULAR_TOTAL_GENERAL"
BEFORE INSERT OR UPDATE ON "DETALLE_ORDEN"
FOR EACH ROW
DECLARE
V_ID_ENCABEZADO NUMBER(10,0);
BEGIN
SELECT "ID_ENCABEZADO"
INTO V_ID_ENCABEZADO
FROM "ENCABEZADO_ORDEN"
WHERE "ID_ENCABEZADO" = :NEW."ID_ENCABEZADO";
UPDATE "ENCABEZADO_ORDEN"
SET "TOTAL_GENERAL" = (SELECT SUM("SUBTOTAL") FROM "DETALLE_ORDEN"
WHERE "ID_ENCABEZADO" = V_ID_ENCABEZADO)
WHERE "ID_ENCABEZADO" = V_ID_ENCABEZADO;
END;
This is the error message I get when I insert or update the table "DETALLE_ORDEN":
1 error has occurred
ORA-04091: table CARLOSM.DETALLE_ORDEN is mutating, trigger/function may not see it
ORA-06512: at "CARLOSM.CALCULAR_TOTAL_GENERAL", line 9
ORA-04088: error during execution of trigger 'CARLOSM.CALCULAR_TOTAL_GENERAL'
Don't use triggers for this kind of logic (for that matter, don't use triggers ever; there's almost always a better way). Also, avoid storing redundant information in base tables whenever possible.
Far better design, with minimal impact to existing code is to
1) rename table "ENCABEZADO_ORDEN" (i.e. to "ENCABEZADO_ORDEN_TAB") and 2) disable/drop "TOTAL_GENERAL" field, and then 3) create a view with original name "ENCABEZADO_ORDEN" as:
CREATE OR REPLACE VIEW ENCABEZADO_ORDEN AS
SELECT O.*, (SELECT SUM(D.SUBTOTAL) FROM DETALLE_ORDEN D
WHERE D.ID_ENCABEZADO = O.ID_ENCABEZADO) TOTAL_GENERAL
FROM ENCABEZADO_ORDEN_TAB O;
This will ensure TOTAL_GENERAL is always correct (in fact, any efforts to set it directly to some other value via update of ENCABEZADO_ORDEN will result in immediate syntax error).
If performance is an issue (i.e. users frequently query TOTAL_GENERAL field in ENCABEZADO_ORDEN table for orders with large numbers of detail records in DETALLE_ORDEN, causing Oracle to repeatedly fetch&sum multitudes of SUBTOTALS) then use a materialized view instead of a basic view.
table 1
ID - name - main_number - random1 - random2
1* -aaaa-blalablabla*- *** - *
2 -vvvv-blublubluuu*- *** - *
3 -aaaa-blalablabla*- *** - **
ID , name and main number are primary key
My problem that I have noticed coulmn name and main number has duplicate values, i dont want to ADD ANY OTHER DUPLICATE VALUES ( I should keep the old duplicat because in my real table there are a lot of duplicated data and its hard to remove them )
what I want when I TRY ( BEFORE TO COMMIT) to know that this name I am trying to insert is duplicate.
I can do that with in a procedure or triger, but i have heard constraint checking is simpler and easier(if there a simpler way then procedure or triger ill be glad to learn it)
CONSTRAINT check_name
CHECK (name = (A_name))
can the constaraint have more then 1 column in such way?
CONSTRAINT check_name
CHECK (name = (A_name) , main_number=( A_number))
can I a write a constaraint in such way?
CONSTRAINT check_name
CHECK (name = ( select case where there is an column has the same value of column name))
So my question : Is there a way simelar to check constraint to help me to know if there is a duplicate column or I have to use a trigger ?
Since your database is Oracle you could also use NOVALIDATE constraints. Meaning: "doesn't matter how the data is, just validate from now on".
create table tb1
(field1 number);
insert into tb1 values (1);
insert into tb1 values (1);
insert into tb1 values (1);
insert into tb1 values (2);
insert into tb1 values (2);
commit;
-- There should be an non-unique index first
create index idx_t1 on tb1 (field1);
alter table tb1 add constraint pk_t1 primary key(field1) novalidate;
-- If you try to insert another 1 or 2 you would get an error
insert into tb1 values (1);
Yes, you can use constraints on many columns.
But in this case constraint is not applicable, because all table rows must satisfy constraints. Use a trigger.
Constraints cannot contain subqueries.
Alternatively use unique index, that will enforce unique constraint
create unique index index1 on table1
(case when ID <= XXX then null else ID end,
case when ID <= XXX then null else name end);
Replace 'XXX' with your current max(ID).
I assume that you want to prevent duplicate records as defined by the combination of name and main_number.
Then the way to go is to cleanup your database, and create a unique index:
create unique index <index_name> on <table> (name, main_number)
This both checks, and speed's it up.
In theory, if you really wanted to keep the old duplicate records, you could get along by using a trigger, but then you will have a hard time trying to get sense out of this data.
Update
If you used the trigger, you would end up with two partitions of data in one table - one is checked, the other is not. So all of your queries must pay attention to it. You just delay your problem.
So either clean it up (by deleting or merging) or move the old data in a separate table.
You can use SQL select ... group by to find your duplicates, so you can delete/move them in one turn.