SQL (oracle) Check Constraint difficulty - Not sure how to implement - sql

I am creating a small database for a telecom system.
One of the tables (calls) requires that a if a phone number's area code is not contained in a predefined list, then the number should not be added to the table.
The way I have thought about doing this is to put a check constraint within the calls table to not accept numbers that are not a part of this mentioned list. However, this list is quite long and I am not too sure if there would be a better implementation method.
Here is the list:
01 or 02: local/national number. Ex.: 01612 338866.
075, 077, 078, 079: mobile phone number. Ex.: 07747 556647.
0800: free number. Ex.: 08002 223344.
0845, 0870: special service. Ex.: 08451 423456.
08442 to 08449: 5p special service. Ex.: 08444 404404.
08712 to 08719: 10p special service. Ex.: 08713 457893.
090, 091, 098: premium rate special service. Ex.: 09119 229595.
The only way I could think of to do this is as follows:
ALTER TABLE calls ADD (CONSTRAINT area_ck
CHECK area_code ("01" or "02" or "075" or "077" or "078" or "079" or "0800" or
"0845" or "0870" or (BETWEEN ("08442" AND "08449")) or
(BETWEEN ("08712" AND "08719")) or
"090" or "091" or "098")
) ;
My two main issues with this are:
It gives an error as it is implemented incorrectly
If I were to modify it slightly until it did work, would it still be a long way about trying to solve my task?

The more common approach would be to define a table of valid area codes
CREATE TABLE area_code (
area_code VARCHAR2(5) PRIMARY KEY
);
Fill the Area_Code table with the set of valid values
INSERT INTO area_code( area_code ) VALUES( '01' );
INSERT INTO area_code( area_code ) VALUES( '02' );
INSERT INTO area_code( area_code ) VALUES( '075' );
...
or
BEGIN
FOR i IN 1000 .. 2999
LOOP
INSERT INTO area_code( area_code )
VALUES( to_char( i, '00000' ) );
END LOOP;
END;
And then define a foreign key constraint from your Call table to the Area_Code table
CREATE TABLE call (
call_id NUMBER PRIMARY KEY,
area_code VARCHAR2(5) REFERENCES area_code( area_code ),
<<other columns>>
);
That's going to be more efficient to enforce than a CHECK constraint and it will be easier to list the valid area codes.

The first problem is about using = and between together. Do it like:
area_code in ('01', '02', '03') or area_code between ('1000' and '1500') or ....

You could either write area_code='01' OR area_code='02' ... or you can use area_code in ('01','02', ...). You also need to add area_code before between keywords.
But I would suggest you to store the area codes in a table instead of the check constraint and use the area codes as foreign keys. This way the list of area codes can easily be modified.

Related

Oracle SQL Developer regex expression error

In the script file, I inserted the following code.
drop table Test;
create table Test(
name char(2) unique not null,
constraint name_c check(
regexp_like(name, '^[A-Z]{1,2}$', 'c')
)
);
insert into Test values ('B');
The developer never budge. It keeps on saying violating the name_c constraint and I don't understand why. The regular expression looks fine for me.
Some variants, however, succeeded, for example, dropping the dollar sign
drop table Test;
create table Test(
name char(2) unique not null,
constraint name_c check(
regexp_like(name, '^[A-Z]{1,2}', 'c')
)
);
insert into Test values ('B');
And I don't understand either.
Why?
Edit: This is the problem: logically the regex is right. But somehow Oracle SQL Developer doesn't budge.
if you went it to start with 1 or 2 capital letter and doesn't matter what coming next you should do it like this :
^[A-Z]{1,2}.*
.* mean 1 or more character exept \n (nex line)

No row selected

SQL> create table artwork
2 (
artwork_id number(7) NOT NULL,
barcode char(20),
title char(20),
description char(50),
PRIMARY KEY (artwork_id)
);
Table created.
SQL> select * from artwork;
no rows selected
I created the table but it showing me this error dont know. Why table it not showing?
I would expect the create to look like this:
create table artwork (
artwork_id number primary key,
barcode char(20),
title varchar2(20),
description varchar2(50)
);
Notes:
There is no need to have number(7). You can specify the length, but it is not necessary.
For title and description you definitely want varchar2(). There is no reason to store trailing spaces at the end of a name.
That may be the same for barcode, but because it might always be exactly 20 characters or trailing spaces might be significant, you might leave it as char().
The primary key constraint can be expressed in-line. There is no need for a separate declaration.
You probably simply want something like
create table artwork
(
artwork_id number(7) NOT NULL,
barcode varchar2(20),
title varchar2(20),
description varchar2(50),
PRIMARY KEY (artwork_id)
);
insert into artwork values (0, 'barcode', 'fancytitle', 'somedescription');
insert into artwork values (1, 'barcode1', 'fancytitle1', 'somedescription1');
select * from artwork;
This creates a table "ARTWORK", inserts 2 rows in it and then selects all rows currently in the table.
An empty table contains no data, with the create table-statement you only define the bucket of data, you have to fill the bucket as well with items.
I'd also recommend a auto increment column (oracle 12c) or a trigger/sequence to increment the id automatically. But that's something to read later :)

Placeholder while retrieving data from table

I have a table that contains common subjects text, that requires insertion of some string at run time. For example below:
CREATE TABLE certification.cert_email_dtls (
cert_eid_id numeric(10,0),
cert_eid_email_body character varying(8000),
);
insert into certification.cert_email_dtls(1,'hello world <blank-value1>);
insert into certification.cert_email_dtls(2,'hello guys <blank-other-value2>);
insert into certification.cert_email_dtls(3,'hello <blank-value3> india <some-other-value4>);
and so on. <some-value>,<some-other-value> etc come at run time.
SELECT cert_eid_id ,
cert_eid_email_body,
INTO v_id,
v_email_body_end
FROM certification.cert_email_dtls where cert_eid_id = in_id;
My requirement is to insert that some-values that are coming in between and generate the final email body.
format() might be the perfect tool for this. Read details in the manual.
If you can save cert_eid_email_body in a compatible format, like:
CREATE TABLE cert_email_dtls (
cert_eid_id serial PRIMARY KEY
, cert_eid_email_body text
);
INSERT INTO cert_email_dtls (cert_eid_email_body)
VALUES ('hello %1$s india %2$s');
The whole operation can be as simple as:
SELECT format(cert_eid_email_body, 'foo', 'bar', 'baz') AS cert_eid_email_body
FROM cert_email_dtls
WHERE cert_eid_id = 1;
Result:
cert_eid_email_body
-------------------
hello foo india bar
Note that the 3rd parameter 'baz' is silently ignored, since it is not referenced in the string.
SQL Fiddle.
Aside: Don't use numeric(10,0) for an ID or primary key. integer or bigint typically serve you better. Using serial PRIMARY KEY above as it probably should be.

Confused with Oracle Procedure with sequence, linking errors and filling null fields

I am trying to make a procedure that takes makes potential empty "received" fields use the current date. I made a sequence called Order_number_seq that populates the order number (Ono) column. I don't know how to link errors in the orders table to a entry in the Orders_errors table.
this is what i have so far:
CREATE PROCEDURE Add_Order
AS BEGIN
UPDATE Orders
CREATE Sequence Order_number_seq
Start with 1,
Increment by 1;
UPDATE Orders SET received = GETDATE WHERE received = null;
These are the tables I am working with:
Orders table
(
Ono Number Not Null,
Cno Number Not Null,
Eno Number Not Null,
Received Date Null,
Shipped_Date Date Null,
Creation_Date Date Not Null,
Created_By VARCHAR2(10) Not Null,
Last_Update_Date Date Not Null,
Last_Updated_By VARCHAR2(10) Not Null,
CONSTRAINT Ono_PK PRIMARY KEY (Ono),
CONSTRAINT Cno_FK FOREIGN KEY (Cno)
REFERENCES Customers_Proj2 (Cno)
);
and
Order_Errors table
(
Ono Number Not Null,
Transaction_Date Date Not Null,
Message VARCHAR(100) Not Null
);
Any help is appreciated, especially on linking the orders table errors to create a new entry in OrderErrors table.
Thanks in advance.
Contrary to Martin Drautzburg's answer, there is no foreign key for the order number on the Order_Errors table. There is an Ono column which appears to serve that purpose, but it is not a foreign as far as Oracle is concerned. To make it a foreign key, you need to add a constraint much like the Cno_FK on Orders. An example:
CREATE TABLE Order_Errors
(
Ono Number Not Null,
Transaction_Date Date Not Null,
Message VARCHAR(100) Not Null,
CONSTRAINT Order_Errors_Orders_FK FOREIGN KEY (Ono) REFERENCES Orders (Ono)
);
Or, if your Order_Errors table already exists and you don't want to drop it, you can use an ALTER TABLE statement:
ALTER TABLE Order_Errors
ADD CONSTRAINT Order_Errors_Orders_FK FOREIGN KEY (Ono) REFERENCES Orders (Ono)
;
As for the procedure, I'm inclined to say what you're trying to do does not lend itself well to a PROCEDURE. If your intention is that you want the row to use default values when inserted, a trigger is better suited for this purpose. (There is some performance hit to using a trigger, so that's a consideration.)
-- Create sequence to be used
CREATE SEQUENCE Order_Number_Sequence
START WITH 1
INCREMENT BY 1
/
-- Create trigger for insert
CREATE TRIGGER Orders_Insert_Trigger
BEFORE INSERT ON Orders
FOR EACH ROW
DECLARE
BEGIN
IF :NEW.Ono IS NULL
THEN
SELECT Order_Number_Sequence.NEXTVAL INTO :NEW.Ono FROM DUAL;
END IF;
IF :NEW.Received IS NULL
THEN
SELECT CURRENT_DATE INTO :NEW.O_Received FROM DUAL;
END IF;
END;
/
This trigger will then be executed on every single row inserted into the Orders table. It checks if the Ono column was NULL and replaces it with an ID from the sequence if so. (Be careful that you don't ever provide an ID that will later be generated by the sequence; it will get a primary key conflict error.) It then checks if the received date is NULL and sets it to the current date, using the CURRENT_DATE function (which I believe was one of the things you were trying to figure out), if so.
(Side note: Other databases may not require a trigger to do this and instead could use a default value. I believe PostgreSQL, for instance, allows the use of function calls in its DEFAULT clauses, and that is how its SERIAL auto-increment type is implemented.)
If you are merely trying to update existing data, I would think the UPDATE statements by themselves would suffice. Is there a reason this needs to be a PROCEDURE?
One other note. Order_Errors has no primary key. You probably want to have an auto-incrementating surrogate key column, or at least create an index on its Ono column if you only ever intend to select off that column.
There are a number of confusing things in your question:
(1) You are creating a sequence inside a procedure. Does this even compile?
(2) Your procedure does not have any parameters. It just updates the RECEIVED column of all rows.
(3) You are not telling us what you want in the MESSAGE column.
My impression is that you should first go "back to the books" before you ask questions here.
As for your original question
how to link errors in the orders table to a entry in the Orders_errors
table.
This is aleady (correctly) done. The Orders_error table contains an ONO foreign key which points to an order.

Constraint Check for 10 Digit Character use for Postal Code

I have a table with a Char(10) column type, named postal Code and I need a Constraint check for all values just be 10 digits like 1234567890 and nothing else, I use the following:
CONSTRAINT [CH_PCDigit] CHECK ( [PostalCode] LIKE '%[^0-9]%'),
CONSTRAINT [CH_PCLength] CHECK ( LEN([PostalCode])=10)
but not worked correctly, why? and what is your suggestion? is there any way to merge this 2 constraint with one?
And what about if I want a Postal Code like this: 12345-54321 mean: 5digit-5digit? (Also Type must be Char(11)).
Does any one know any good source for Rgex or Constraint Check in SQl?
SQL Server TSQL does not support full blown RegEx's. You can do what you want in a single constraint like so:
CONSTRAINT [CH_PCDigit]
CHECK ([PostalCode] LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]')
or better:
CONSTRAINT [CH_PCDigit]
CHECK ([PostalCode] LIKE REPLICATE('[0-9]', 10))
If you want to allow dashes:
CREATE table ChkTest
(
PostalCode char(10) not null
CONSTRAINT [CH_PCDigit]
CHECK ([PostalCode] LIKE REPLICATE('[0-9,-]', 10))
)
-- Test Code...
insert into ChkTest
select '1234567890'
insert into ChkTest
select '123456780'
insert into ChkTest
select '12345678y0'
insert into ChkTest
select '12345678901'
select * from ChkTest
insert into ChkTest
select '12345-8901'
Here is one that accepts both U.S. Zip Code and Canada Postal Code.
CONSTRAINT CH_PCDigit
CHECK (PostalCode LIKE '[0-9][0-9][0-9][0-9][0-9]' OR
PostalCode LIKE '[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]' OR
PostalCode LIKE '[A-Y][0-9][A-Z][0-9][A-Z][0-9]')
YOu can use isnumeric, split big number:
CREATE TABLE a (
pc CHAR(10),
CONSTRAINT pc_c CHECK (
LEN(pc) = 10 AND
ISNUMERIC(SUBSTRING(pc,1,5))=1 AND
ISNUMERIC(SUBSTRING(pc,6,5))=1)
)