My requirement is that the column accno has no null value and no duplicates. The name column has no nulls and accepts only A to Z (no other like number or * $). The acctype columns is a character that allows only ( 'S' , 'C' ,'R') and the balance column has no null values. If acctype is S then balance should be >= 5000, when C the balance should be > 10000 and when it's R >= 5000.
I am trying to apply this with:
create table kcb_acc_tab
(accno varchar2(20)
constraint kcb_acc_Pk
primary key,
name varchar2(20)
constraint kcb_name_NN
Not null
constraint kcb_name_CK
check((name =upper(name)) and (name like '[(A-Z)]')),
Acctype char
constraint kcb_acctype_ck
check (acctype in('S' ,'C' ,'R')) ,
Doo timestamp
default sysdate ,
bal number(7,2) kcb_bal_NN
constraint kcb_bal_ck
check((aacctype ='S' and bal >=5000) or
(acctype = 'C' and bal >=10000) or
(acctype ='R' and bal >=5000));
This sounds like a perfect use-case for regular expressions, which I think is your intention with the like in your constraint.
I've considerably cleaned up your statement, you were missing a comma in the definition of kcb_bal_ck, and I've put the constraints at the end and added whitespace. This makes it easier, for me, to see what's going on and where any mistakes might be.
create table kcb_acc_tab(
accno varchar2(20) not null
, name varchar2(20) not null
, acctype char(1) not null -- missing size of column
, doo timestamp default sysdate not null -- missing not null
, bal number(7,2) not null
, constraint kcb_acc_pk primary key (accno)
, constraint kcb_name_ck check ( regexp_like(name, '[A-Z]', 'c' ) )
, constraint kcb_acctype_ck check ( acctype in ( 'S' ,'C' ,'R' ) )
-- acctype was spelled incorrectly.
, constraint kcb_bal_ck check( ( acctype ='S' and bal >= 5000 )
or ( acctype = 'C' and bal >= 10000 )
or ( acctype ='R' and bal >= 5000 )
) -- this parenthesis was missing
)
Here's a SQL Fiddle to demonstrate.
The main difference between this and your own is regexp_like(name, '[A-Z]', 'c' ). This ensures that the characters in the column name are solely contained within the group A-Z, that is the set of the upper-case Latin alphabet. The match_parameter 'c', specifies that the matching should be case-sensitive. The default case-sensitivity is determined by your NLS_SORT parameter, so you may not need to specify this explicitly but it's wise to do so.
Related
I have a simple insert statement like below
Insert into table_X (id, validFrom, validTo, someValue) VALUES(?,?,?,?);
where validFrom and validTo have a DATE type.
I want to insert records to table only if validFrom <= validTo. How can I add some condition checking to the SQL statement to do so? For now I'm doing the cheking with java and I want to do it in the sql query if possible.
EDIT
CREATE TABLE XYZ.TABLE_X
(
ID VARCHAR2(20 BYTE) NOT NULL ,
VALIDFROM DATE NOT NULL ,
VALIDTO DATE NOT NULL ,
SOMEVALUE NUMBER(18, 0) NOT NULL ,
CONSTRAINT TABLE_X_PK_N2 PRIMARY KEY
(
ID ,
VALIDFROM ,
VALIDTO
)
ENABLE
CONSTRAINT chk_table_x_valids
(
check(VALIDFROM <= VALIDTO )
)
ENABLE
)
ORGANIZATION INDEX
LOGGING
TABLESPACE XYZ
...
You can add a check constraint:
alter table table_x add constraint chk_table_x_valids
check (validFrom <= validTo);
CREATE OR REPLACE TRIGGER EVALUATION
BEFORE INSERT OR UPDATE OR DELETE ON BOOKING
FOR EACH ROW
DECLARE
BEGIN
SELECT BOOKING_EVALUATION FROM BOOKING WHERE BOOKING_EVALUATION > 2;
SELECT BOOKING_EVALUATION INTO EVALUATIONAUDIT FROM BOOKING;
IF INSERTING THEN
INSERT INTO EVALUATIONAUDIT (VOYAGES_ID,CUSTOMER_NAME,START_DATE,SHIP_NAME,BOOKING_EVALUATION)
VALUES(:NEW.VOYAGES_ID,:NEW.CUSTOMER_NAME,:NEW.START_DATE,:NEW.SHIP_NAME,:NEW.BOOKING_EVALUATION);
END IF;
IF UPDATING THEN
INSERT INTO EVALUATIONAUDIT (VOYAGES_ID,CUSTOMER_NAME,START_DATE,SHIP_NAME,BOOKING_EVALUATION)
VALUES(:OLD.VOYAGES_ID,:OLD.CUSTOMER_NAME,:OLD.START_DATE,:OLD.SHIP_NAME,:OLD.BOOKING_EVALUATION);
END IF;
IF DELETING THEN
INSERT INTO EVALUATIONAUDIT (VOYAGES_ID,CUSTOMER_NAME,START_DATE,SHIP_NAME,BOOKING_EVALUATION)
VALUES(:OLD.VOYAGES_ID,:OLD.CUSTOMER_NAME,:OLD.START_DATE,:OLD.SHIP_NAME,:OLD.BOOKING_EVALUATION);
END IF;
END;
This is my audit table:
desc evaluationaudit;
Name Null? Type
----------------------------------------- -------- -----------------------
AUDITT_ID NOT NULL NUMBER(10)
VOYAGES_ID NOT NULL NUMBER(10)
CUSTOMER_NAME NOT NULL VARCHAR2(20)
START_DATE NOT NULL DATE
SHIP_NAME NOT NULL VARCHAR2(20)
BOOKING_EVALUATION NOT NULL NUMBER(20)
and this is my show error output:
SQL> SHOW ERRORS;
Errors for TRIGGER EVALUATION:
LINE/COL ERROR
-------- -------------------------------------------------------------
12/24 PLS-00049: bad bind variable 'NEW.CUSTOMER_NAME'
12/43 PLS-00049: bad bind variable 'NEW.START_DATE'
12/59 PLS-00049: bad bind variable 'NEW.SHIP_NAME'
18/24 PLS-00049: bad bind variable 'OLD.CUSTOMER_NAME'
18/43 PLS-00049: bad bind variable 'OLD.START_DATE'
18/59 PLS-00049: bad bind variable 'OLD.SHIP_NAME'
24/24 PLS-00049: bad bind variable 'OLD.CUSTOMER_NAME'
24/43 PLS-00049: bad bind variable 'OLD.START_DATE'
24/59 PLS-00049: bad bind variable 'OLD.SHIP_NAME'
I wanted to add to the audit table. If a customer gives a poor evaluation of 2 or less, the details of their voyage (customer_name, name and date of the cruise, ship name and evaluation) but I'm getting error
Warning: Trigger created with compilation errors.
And the audit table is empty, showing no rows selected. I can't seem to find the problem where I am going wrong.
Maybe the following code snippets will help you. Suppose we have 2 tables: BOOKING, and EVALUATION_AUDIT (everything written in uppercase letters is taken from the code in your question).
Tables
create table booking (
VOYAGES_ID number primary key
, CUSTOMER_NAME VARCHAR2(20) NOT NULL
, START_DATE DATE NOT NULL
, SHIP_NAME VARCHAR2(20) NOT NULL
, BOOKING_EVALUATION NUMBER(20) NOT NULL
) ;
create table evaluationaudit (
AUDITT_ID number generated always as identity start with 5000 primary key
, trg_cond_pred varchar2( 64 ) default 'Row not added by evaluation trigger!'
, VOYAGES_ID NUMBER(10) NOT NULL
, CUSTOMER_NAME VARCHAR2(20) NOT NULL
, START_DATE DATE NOT NULL
, SHIP_NAME VARCHAR2(20) NOT NULL
, BOOKING_EVALUATION NUMBER(20) NOT NULL
) ;
Trigger
-- "updating" and "deleting" code omitted for clarity
CREATE OR REPLACE TRIGGER EVALUATION_trigger
BEFORE INSERT OR UPDATE OR DELETE ON BOOKING
FOR EACH ROW
BEGIN
case
when INSERTING then
if :new.booking_evaluation <= 2 then
INSERT INTO EVALUATIONAUDIT
( trg_cond_pred,
VOYAGES_ID, CUSTOMER_NAME, START_DATE, SHIP_NAME, BOOKING_EVALUATION )
VALUES (
'INSERTING'
, :NEW.VOYAGES_ID
, :NEW.CUSTOMER_NAME
, :NEW.START_DATE
, :NEW.SHIP_NAME
, :NEW.BOOKING_EVALUATION
);
end if ;
end case ;
END ;
/
Testing
One of your requirements (in your question) is:
I wanted to add to the audit table. If a customer gives a poor
evaluation of 2 or less, the details of their voyage (customer_name,
name and date of the cruise, ship name and evaluation)
delete from evaluationaudit ;
delete from booking ;
-- booking_evaluation greater than 2 -> no entry in audit table
insert into booking
( VOYAGES_ID, CUSTOMER_NAME, START_DATE, SHIP_NAME, BOOKING_EVALUATION )
values ( 1111, 'customer1', date '2018-05-24', 'ship1', 9999 ) ;
select * from evaluationaudit ;
-- no rows selected
-- booking_evalution = 2 -> insert a row into the audit table
insert into booking
( VOYAGES_ID, CUSTOMER_NAME, START_DATE, SHIP_NAME, BOOKING_EVALUATION )
values ( 1112, 'customer1', date '2018-05-24', 'ship1', 2 ) ;
select * from evaluationaudit ;
AUDITT_ID TRG_COND_PRED VOYAGES_ID CUSTOMER_NAME START_DATE SHIP_NAME BOOKING_EVALUATION
5000 INSERTING 1112 customer1 24-MAY-18 ship1 2
__Update__
If - as you wrote in your comment - you need to pull in some more data from other tables, maybe you want to try the following approach: keep the trigger code rather brief, and use it to call a procedure for the more complicated stuff eg
Tables
create table evaluationaudit (
AUDITT_ID number generated always as identity start with 7000 primary key
, trg_cond_pred varchar2( 64 ) default 'Row not added by evaluation trigger!'
, VOYAGES_ID NUMBER NOT NULL
, CUSTOMER_NAME VARCHAR2(20) NOT NULL
, SHIP_NAME VARCHAR2(20) NOT NULL
, BOOKING_EVALUATION NUMBER(20) NOT NULL
) ;
create table ships ( name varchar2( 64 ), id number unique ) ;
create table customers ( name varchar2( 64 ), id number unique ) ;
insert into ships ( name, id ) values ( 'ship1', 501 );
insert into ships ( name, id ) values ( 'ship2', 502 );
insert into ships ( name, id ) values ( 'ship3', 503 );
insert into customers ( name, id ) values ( 'customer1', 771 ) ;
insert into customers ( name, id ) values ( 'customer2', 772 ) ;
insert into customers ( name, id ) values ( 'customer3', 773 ) ;
create table bookings (
id number generated always as identity start with 5000 primary key
, voyagesid number
, shipid number
, customerid number
, evaluation number
, bookingdate date
);
Trigger
create or replace trigger bookingeval
before insert on bookings
for each row
when ( new.evaluation <= 2 ) -- Use NEW without colon here! ( see documentation )
begin
auditproc( :new.voyagesid, :new.customerid, :new.shipid, :new.evaluation ) ;
end ;
/
Procedure
create or replace procedure auditproc (
voyagesid_ number
, customerid_ number
, shipid_ number
, evaluation_ number
)
as
customername varchar2( 64 ) := '' ;
shipname varchar2( 64 ) := '' ;
begin
-- need to find the customername and shipname before INSERT
select name into customername from customers where id = customerid_ ;
select name into shipname from ships where id = shipid_ ;
insert into evaluationaudit
( trg_cond_pred,
voyages_id, customer_name, ship_name, booking_evaluation )
values (
'INSERTING'
, voyagesid_
, customername
, shipname
, evaluation_
);
end ;
/
Testing
-- evaluation > 2 -> no INSERT into evaluationaudit
insert into bookings
( voyagesid, customerid, shipid, evaluation, bookingdate )
values ( 1111, 771, 501, 9999, sysdate ) ;
select * from evaluationaudit ;
-- no rows selected
-- evaluation = 2
-- -> trigger calling audit procedure -> inserts into evaluationaudit
insert into bookings
( voyagesid, customerid, shipid, evaluation, bookingdate )
values ( 1112, 772, 502, 2, sysdate ) ;
select * from evaluationaudit ;
AUDITT_ID TRG_COND_PRED VOYAGES_ID CUSTOMER_NAME SHIP_NAME BOOKING_EVALUATION
7000 INSERTING 1112 customer2 ship2 2
I am trying to insert rows into a table. I m using oracle database. The query looks as follows:
INSERT INTO bv_chglogentry
SELECT DISTINCT account_coid,
To_timestamp('2018-02-01-16.04.22.428161',
'YYYY-MM-DD-HH24.MI.SSXFF'),
1,
'HP15004',
'~HP15004',
' ',
' ',
Hextoraw('00257EAB')
|| Hextoraw('0001517F0804011B'),
Hextoraw('0000000F07650368'),
' ',
0,
' ',
Trunc(To_number(To_char(To_date('2015-05-02', 'YYYY-MM-DD'), 'J'
))),
134480155,
86399,
124060520
FROM inputaccounts;
The structure of the tables looks as follows;
BV_CHGLOGENTRY
---------------
COID_ NOT NULL CHAR(26 CHAR)
TIMESTAMP_ NOT NULL TIMESTAMP(9)
PRODUCT_ID NOT NULL NUMBER(10)
ADMIN_UNIT_ID NOT NULL CHAR(8 CHAR)
OPERATOR_ID NOT NULL CHAR(10 CHAR)
OVER_ADMIN_UNIT_ID NOT NULL CHAR(8 CHAR)
OVER_OPERATOR_ID NOT NULL CHAR(10 CHAR)
CHANGE_DATE NOT NULL RAW(12 BYTE)
DESCRIPTION_ NOT NULL RAW(220 BYTE)
BUSINESS_EVENT_ID NOT NULL CHAR(32 CHAR)
OWNER_TYPE NOT NULL NUMBER(10)
OWNER_ID NOT NULL CHAR(26 CHAR)
DATE_ NOT NULL NUMBER(10)
ZONE_ NOT NULL NUMBER(10)
TIME_ NOT NULL NUMBER(10)
CHG_LOG_TYPE NOT NULL NUMBER(10)
INPUTACCOUNTS
--------------
ACCOUNT_COID NOT NULL CHAR(26)
TXN_ID NOT NULL CHAR(26)
there is a column DATE_ which is of NUMBER type. I m having problem with inserting proper data in that column. The number of columns i'm inserting is 16 which is correct in BV_CHGLOGENTRY. Can you please let me know what might be the problem with the above query.
The value you are getting in the date column, after doing insert is not incorrect. It's just converted into Julian calendar format. Because you are converting the date into Julian calendar in your select query i.e:
Trunc(To_number(To_char(To_date('2015-05-02', 'YYYY-MM-DD'), 'J')))
The value that you are getting the date column is correct i.e (2457145).
To avoid this, you can use replace() function in your SELECT query where you are doing Trunc(To_number(To_char(To_date('2015-05-02', 'YYYY-MM-DD'), 'J')))to get readable date in number format.
Sample query:
select to_number(replace('2015-08-02', '-')) from dual;
Output: 20150802
i want to insert records from one table to another with common columns
desc fp_mast null? type
emp_id not null varchar2(4)
emp_nm not null varchar2(35)
typ not null varchar2(1)
flag not null varchar2(1)
st_dt not null DATE
end_dt DATE
wk_dt not null DATE
desc emp_mast null? type
emp_id not null varchar2(4)
emp_nm not null varchar2(35)
grd_cd not null varchar2(3)
desg_cd not null varchar2(2)
cost_sl not null varchar2(2)
flag not null varchar2(1)
join_dt not null DATE
resig_dt DATE
wk_dt not null DATE
i tried this query to insert the records but did not work
insert into fp_mast(emp_id,emp_nm,st_dt,end_dt,wk_dt)
select(emp_no,emp_nm,join_dt,resig_dt,wk_dt)
from emp_mast
where emp_id in ('7996','7942','5251','7999','8249','6464','8220',
'8221')
it kept showing me the following error
missing right parenthesis
thank you.
missing right parenthesis
Because you have a syntax error in the SELECT statement.
Change this:
select(emp_no,emp_nm,join_dt,resig_dt,wk_dt)
to this:
select emp_no,emp_nm,join_dt,resig_dt,wk_dt
You don't need the parenthesis for the column names in the select list. I think you got confused with the way VALUES keyword is used.
How can i restrict field in a table to 15 or 16 digits. I have this table:
create table Person(
,UserID varchar(30)
,Password varchar(30) not null
,CCtype varchar(8)
,CCNumber numeric
,primary key(UserID)
,constraint CK_CCvalidity check
(
(CCType is null or CCNumber is null)
or
(
(CCType = 'Amex' or CCType = 'Discover' or CCType = 'MC' or CCType = 'VISA')
and
(CCNumber >= 15 and CCNumber <= 16)
)
)
);
But this actually checks for the values 15 an 16, not for the number of digits. Also, we can assume that the numeric may hold 000... as the first digits.
Thanks for the help
CCNumber should never be numeric. That will lead to a world of pain.
It should be varchar(X) where X is 13 - 24 digits.
Credit card numbers are usually represented by groups of 4 or 5
digits separated by spaces or dashes or simply all together with no separators.
[note: American Express: 15 digits; Visa: 13 or 16 digits]
In response to your comment:
ALTER TABLE dbo.Person
ADD CONSTRAINT CK_Person_CCNumber
CHECK (LEN(CCNumber) = 16 OR LEN(CCNumber) = 15);
But probably better as:
ALTER TABLE dbo.Person
ADD CONSTRAINT CK_Person_CCNumber
CHECK (LEN(CCNumber) >= 13 AND LEN(CCNumber) <= 15);
AND add a constraint to ensure it is a valid credit card number perhaps (there are plenty of examples online).
Bank Card Number
You can create a function to remove the Non-Numeric characters from a varchar, like this one:
CREATE Function [fnRemoveNonNumericCharacters](#strText VARCHAR(1000))
RETURNS VARCHAR(1000)
AS
BEGIN
WHILE PATINDEX('%[^0-9]%', #strText) > 0
BEGIN
SET #strText = STUFF(#strText, PATINDEX('%[^0-9]%', #strText), 1, '')
END
RETURN #strText
END
Now, if you want to allow only digits and want to check the length, you could add two Check Constraints like this:
Create Table Person
(
Id int not null primary key,
CCNumber varchar(30),
CONSTRAINT CK_Person_CCNumber_Length CHECK (LEN(CCNumber) BETWEEN 15 AND 16),
CONSTRAINT CK_Person_CCNumber_IsNumeric CHECK (LEN(dbo.[fnRemoveNonNumericCharacters](CCNumber)) = LEN(CCNumber))
)
First Constraint will check the length of the field to be 15 or 16.
Second one will check that the field is numeric (length of field removing non-numeric is equal to length of the original field)
You can also do it in just one ANDed Check Constraint.
Storing a credit card number as a...number is guaranteed to shoot you in the foot some day. Such as the day you start encountering credit card numbers with leading zeroes. They may consist of decimal digits, but they're not numbers. They're text.
Plan for the future: what happens when somebody start issuing credit card numbers with letters?
So, try this:
create table dbo.some_table
(
...
credit_card_type varchar(8) null ,
credit_card_number varchar(32) null ,
constraint some_table_ck01 check (
( credit_card_type is not null
and credit_card_number is not null
)
OR ( credit_card_type is null
and credit_card_number is null
)
) ,
constraint some_table_ck02 check (
credit_card_type in ( 'amex' , 'discover' , 'mc' , 'visa' )
) ,
constraint some_table_ck03 check (
credit_card_number not like '%[^0-9]%'
) ,
constraint some_table_ck04 check (
len(credit_card_number) = case credit_card_type
when 'amex' then 15
when 'discover' then 16
when 'mc' then 16
when 'visa' then 16
else -1 -- coerce failure on invalid/unknown type
end
) ,
)
go
insert some_table values( null , null ) -- succeeds
insert some_table values( 'amex' , null ) -- violates check constraint #1
insert some_table values( null , '1' ) -- violates check constraint #1
insert some_table values( 'acme' , '1' ) -- violates check constraint #2
insert some_table values( 'amex' , 'A1B2' ) -- violates check constraint #3
insert some_table values( 'amex' , '12345' ) -- violates check constraint #4
insert some_table values( 'amex' , '123456789012345' ) -- success!
go
But as noted by others, you need to fix your data model. A credit card is a separate entity from a customer. It has a dependent relationship upon the customer (the card's existence is predicated upon the existence of the customer who own it). You can a data model like the following. This
create table credit_card_type
(
int id not null primary key clustered ,
description varchar(32) not null unique ,
... -- other columns describing validation rules here
)
create table credit_card
(
customer_id int not null ,
type int not null ,
number varchar(32) not null ,
expiry_date date not null ,
primary key ( customer_id , number , type , expiry_date ) ,
unique ( number , customer_id , type , expiry_date ) ,
foreign key customer references customer(id) ,
foreign key type references credit_card_type(id) ,
)
Further: you are encrypting card numbers using strong encryption, aren't you?