Related
CREATE TABLE projeto.Person (
Person_Code INT IDENTITY(1,1) NOT NULL,
birthDate DATE NOT NULL,
Name VARCHAR(50) NOT NULL,
PRIMARY KEY (Person_Code)
)
CREATE TABLE projeto.Student (
Student_Code INT REFERENCES projeto.Person (Person_Code),
payment INT NOT NULL,
PRIMARY KEY (Student_Code),
)
CREATE TABLE projeto.teacher (
payment INT NOT NULL,
teacher_Code INT,
PRIMARY KEY (teacher_Code),
CHECK (payment > 350)
)
How do I insert values in student, paying attention that a student has all person attributes? e.g. student has name, birth_date etc.
I tried this:
INSERT INTO projeto.Person VALUES
('1961-03-26', John Adam')
but this only adds in a person, and I can't tell if its a student or not.
I guess its how to get the recently inserted Person_Code that you are asking? In which case use scope_identity().
declare #BirthDate date, #Name varchar(50), #Payment int, #IsStudent bit, #IsTeacher bit, #NewPersonCode int;
-- SET THE RELEVANT VARIABLE VALUES
-- Insert person
insert into projeto.Person (BirthDate, [Name])
select #BirthDate, #Name;
-- Get the Person_Code of the new person record
set #NewPersonCode = scope_identity();
-- Insert Student record if a student
insert into projeto.Student (Student_Code, Payment)
select #NewPersonCode, #Payment
where #IsStudent = 1;
-- Insert Teacher record if a teacher
insert into projeto.Teacher (Teacher_Code, Payment)
select #NewPersonCode, #Payment
where #IsTeacher = 1;
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'm designing a database where I have a number of products, each of which belongs to one and only one category.
Products are meant to be tagged, but only with tags allowed for the category they belong to.
This is what I've got so far:
Let's say I have these 2 categories:
Smartphones
Laptops
Tags for the "Smartphones" category:
Dual SIM
GPS
Tags for the "Laptop" category:
Backlit keyboard
HDMI
The problem with this design is that the database doesn't prevent a product from being tagged with a tag from another category: a bug in my app's code could easily cause a laptop to be tagged with the "Dual SIM" tag, which obviously is not what I want.
I would like to prevent this situation at database level with foreign keys and without using triggers. Is this possible at all?
I was able to do the following in Oracle. Note how the last insert fails, I believe this is what you are after.
CREATE TABLE product (
id INTEGER NOT NULL,
category_id INTEGER NOT NULL,
PRIMARY KEY ( id ),
CONSTRAINT uq_prod_cat UNIQUE ( id,category_id )
);
INSERT INTO product (
id,
category_id
) VALUES (
1,
1
);
CREATE TABLE tags (
id INTEGER NOT NULL,
category_id INTEGER NOT NULL,
PRIMARY KEY ( id ),
CONSTRAINT uq_tag_cat UNIQUE ( id,category_id )
);
INSERT INTO tags (
id,
category_id
) VALUES (
1,
1
);
INSERT INTO tags (
id,
category_id
) VALUES (
2,
1
);
INSERT INTO tags (
id,
category_id
) VALUES (
3,
2
);
CREATE TABLE product_tags (
id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
category_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
PRIMARY KEY ( id ),
FOREIGN KEY ( product_id,category_id )
REFERENCES product ( id,category_id ),
FOREIGN KEY ( tag_id,category_id )
REFERENCES tags ( id,category_id )
);
INSERT INTO product_tags (
id,
product_id,
category_id,
tag_id
) VALUES (
1,
1,
1,
1
);
1 row inserted.
INSERT INTO product_tags (
id,
product_id,
category_id,
tag_id
) VALUES (
2,
1,
1,
2
);
1 row inserted.
INSERT INTO product_tags (
id,
product_id,
category_id,
tag_id
) VALUES (
3,
1,
1,
3
);
Error starting at line : 35 in command -
INSERT INTO product_tags (id, product_id, category_id, tag_id) VALUES (3, 1, 1, 3)
Error report -
ORA-02291: integrity constraint (SYS_C008023) violated - parent key not found
My answer is pretty similar to that of Ashuntosh A, except that I would create a separate table for associating tags with categories, so that the schema allows for a tag to apply to more one category (e.g. both a tablet and phone could have a DualSim):
--TSQL
create table ProductCategory
(
id int primary key identity,
name varchar(50) not null
)
create table ProductTag
(
id int primary key identity,
name varchar(50) not null
)
create table TagCategory
(
tag_id int foreign key references ProductTag,
category int foreign key references ProductCategory,
primary key (tag_id, category)
)
create table Product
(
id int primary key identity,
type int foreign key references ProductCategory,
unique (id, type)
)
create table TaggedProduct
(
product int,
tag int,
type int,
primary key (product, tag),
foreign key (product, type) references Product (id, type),
foreign key (tag, type) references TagCategory (tag_id, category)
)
insert ProductCategory
select 'Laptop' union
select 'Phone'
insert ProductTag
select 'HDMI' union
select 'Backlit Keyboard' union
select 'Dual Sim' union
select 'GPS'
insert TagCategory
select 1, 1 union -- HDMI/LAPTOP
select 2, 1 union -- Backlit/LAPTOP
select 3, 2 union -- DualSim/PHONE
select 4, 2 -- GPS/PHONE
insert Product
select 1 --a laptop
insert TaggedProduct
select 1, 1, 1 --laptop has hdmi
union select 1, 2, 1 --laptop has backlit keyboard
insert TaggedProduct select 1, 3, 1
--fails because 'DualSim/Laptop' is not a valid category
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?
I want to automatically generate unique id with per-defined code attach to it.
ex:
UID12345678
CUSID5000
I tried uniqueidentifier data type but it generate a id which is not suitable for a user id.
Any one have suggestions?
The only viable solution in my opinion is to use
an ID INT IDENTITY(1,1) column to get SQL Server to handle the automatic increment of your numeric value
a computed, persisted column to convert that numeric value to the value you need
So try this:
CREATE TABLE dbo.tblUsers
(ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
UserID AS 'UID' + RIGHT('00000000' + CAST(ID AS VARCHAR(8)), 8) PERSISTED,
.... your other columns here....
)
Now, every time you insert a row into tblUsers without specifying values for ID or UserID:
INSERT INTO dbo.tblUsersCol1, Col2, ..., ColN)
VALUES (Val1, Val2, ....., ValN)
then SQL Server will automatically and safely increase your ID value, and UserID will contain values like UID00000001, UID00000002,...... and so on - automatically, safely, reliably, no duplicates.
Update: the column UserID is computed - but it still OF COURSE has a data type, as a quick peek into the Object Explorer reveals:
CREATE TABLE dbo.tblUsers
(
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
UserID AS 'UID' + RIGHT('00000000' + CAST(ID AS VARCHAR(8)), 8) PERSISTED,
[Name] VARCHAR(50) NOT NULL,
)
marc_s's Answer Snap
Reference:https://learn.microsoft.com/en-us/sql/t-sql/functions/newid-transact-sql?view=sql-server-2017
-- Creating a table using NEWID for uniqueidentifier data type.
CREATE TABLE cust
(
CustomerID uniqueidentifier NOT NULL
DEFAULT newid(),
Company varchar(30) NOT NULL,
ContactName varchar(60) NOT NULL,
Address varchar(30) NOT NULL,
City varchar(30) NOT NULL,
StateProvince varchar(10) NULL,
PostalCode varchar(10) NOT NULL,
CountryRegion varchar(20) NOT NULL,
Telephone varchar(15) NOT NULL,
Fax varchar(15) NULL
);
GO
-- Inserting 5 rows into cust table.
INSERT cust
(CustomerID, Company, ContactName, Address, City, StateProvince,
PostalCode, CountryRegion, Telephone, Fax)
VALUES
(NEWID(), 'Wartian Herkku', 'Pirkko Koskitalo', 'Torikatu 38', 'Oulu', NULL,
'90110', 'Finland', '981-443655', '981-443655')
,(NEWID(), 'Wellington Importadora', 'Paula Parente', 'Rua do Mercado, 12', 'Resende', 'SP',
'08737-363', 'Brasil', '(14) 555-8122', '')
,(NEWID(), 'Cactus Comidas para Ilevar', 'Patricio Simpson', 'Cerrito 333', 'Buenos Aires', NULL,
'1010', 'Argentina', '(1) 135-5555', '(1) 135-4892')
,(NEWID(), 'Ernst Handel', 'Roland Mendel', 'Kirchgasse 6', 'Graz', NULL,
'8010', 'Austria', '7675-3425', '7675-3426')
,(NEWID(), 'Maison Dewey', 'Catherine Dewey', 'Rue Joseph-Bens 532', 'Bruxelles', NULL,
'B-1180', 'Belgium', '(02) 201 24 67', '(02) 201 24 68');
GO
If you want to add the id manually you can use,
PadLeft() or String.Format() method.
string id;
char x='0';
id=id.PadLeft(6, x);
//Six character string id with left 0s e.g 000012
int id;
id=String.Format("{0:000000}",id);
//Integer length of 6 with the id. e.g 000012
Then you can append this with UID.
The 'newid()' method unique id generate for per record.
AddColumn("dbo.Foo", "Key", c => c.String(nullable: false, maxLength: 250, defaultValueSql: "newid()"));
Table Creating
create table emp(eno int identity(100001,1),ename varchar(50))
Values inserting
insert into emp(ename)values('narendra'),('ajay'),('anil'),('raju')
Select Table
select * from emp
Output
eno ename
100001 narendra
100002 rama
100003 ajay
100004 anil
100005 raju