SQL trigger for tracking history of changes - ORACLE DB - sql

I have a table cust_info and need to track the update and delete on it. I created 2 new tables ( archive_customers is the same as cust_info but customers_hist contains two additional instances (changed_by and change_date)).
CREATE TABLE CUST_INFO
(
CUST_ID NUMBER(15),
CUST_F_NAME VARCHAR(20),
CUST_L_NAME VARCHAR(20),
CUST_ADDRESS VARCHAR(40),
CITY VARCHAR(30),
STATE VARCHAR(30),
ZIP NUMBER,
PHONE VARCHAR(12),
PRIMARY KEY (CUST_ID)
);
I was searching for similar problems and this is what I got so far:
CREATE OR REPLACE TRIGGER TRIGGER_UPDATE
BEFORE UPDATE ON CUST_INFO
FOR EACH ROW
BEGIN
INSERT INTO CUSTOMERS_HIST
(
CUST_ID,
CUST_F_NAME,
CUST_L_NAME,
CUST_ADDRESS,
CITY,
STATE,
ZIP,
PHONE,
SYSDATE
)
SELECT
OLD.CUST_ID,
OLD.CUST_F_NAME,
OLD.CUST_L_NAME,
OLD.CUST_ADDRESS,
OLD.CITY,
OLD.STATE,
OLD.ZIP,
OLD.PHONE
FROM CUST_INFO
END;
CREATE OR REPLACE TRIGGER TRIGGER_DELETE
BEFORE DELETE ON CUST_INFO
FOR EACH ROW
BEGIN
INSERT INTO ARCHIVE_CUSTOMERS
(
CUST_ID,
CUST_F_NAME,
CUST_L_NAME,
CUST_ADDRESS,
CITY,
STATE,
ZIP,
PHONE
)
SELECT
OLD.CUST_ID,
OLD.CUST_F_NAME,
OLD.CUST_L_NAME,
OLD.CUST_ADDRESS,
OLD.CITY,
OLD.STATE,
OLD.ZIP,
OLD.PHONE
FROM CUST_INFO
END;
In addition, I am not sure how to get the change_by and change_date populated??
If you need more info, please make a comment.
Thank you

Perhaps you could use the user and sysdate built-in functions for chnage_by and change_date respectively. The code I would write is completely different if I want to store the change history in a table. Yet, for your example, the code is as follows:
CREATE OR REPLACE TRIGGER TRIGGER_UPDATE
BEFORE UPDATE
ON CUST_INFO
FOR EACH ROW
BEGIN
INSERT INTO CUSTOMERS_HIST (CUST_ID,
CUST_F_NAME,
CUST_L_NAME,
CUST_ADDRESS,
CITY,
STATE,
ZIP,
PHONE,
SYSDATE,
change_by,
change_date)
SELECT OLD.CUST_ID,
OLD.CUST_F_NAME,
OLD.CUST_L_NAME,
OLD.CUST_ADDRESS,
OLD.CITY,
OLD.STATE,
OLD.ZIP,
OLD.PHONE,
user,
sysdate
FROM CUST_INFO;
END;
/
CREATE OR REPLACE TRIGGER TRIGGER_DELETE
BEFORE DELETE
ON CUST_INFO
FOR EACH ROW
BEGIN
INSERT INTO ARCHIVE_CUSTOMERS (CUST_ID,
CUST_F_NAME,
CUST_L_NAME,
CUST_ADDRESS,
CITY,
STATE,
ZIP,
PHONE,
change_by,
change_date)
SELECT OLD.CUST_ID,
OLD.CUST_F_NAME,
OLD.CUST_L_NAME,
OLD.CUST_ADDRESS,
OLD.CITY,
OLD.STATE,
OLD.ZIP,
OLD.PHONE,
user,
sysdate
FROM CUST_INFO;
END;
/
That was the answer for your question.
Now, if I were you, I would build an audit table as follows:
TABLE audit_entry
(
audit_Entry_Id INTEGER,
change_By VARCHAR2 (30), -- User who made the change
change_TS TIMESTAMP, -- TS at which the change was made
table_Name VARCHAR2 (30), -- Name of table on which change was made
column_Name VARCHAR2 (30), -- Name of column in that table
primary_key_id INTEGER, -- The PK or ID of the row which was changed
old_clob CLOB,
new_clob CLOB,
old_TS TIMESTAMP,
new_TS TIMESTAMP,
old_number NUMBER (36, 2),
new_number NUMBER (36, 2)
)
The last old and new columns would store the old and new values of appropriate data type. You could also improve the table as per your needs. Then, build a trigger on your tables to track changes and insert values into audit_entry table accordingly.

Related

How to automaticaly fill a foreign key when a row is inserted in Postgresql

I am currently working under postgres 14.
I got two tables customers and contacts, and i want to automatically find the customer_id from the customer name when i add a new contact in the table contacts.
CREATE TABLE customers(
customer_id INT GENERATED ALWAYS AS IDENTITY,
customer_name VARCHAR(255) NOT NULL UNIQUE,
PRIMARY KEY(customer_id)
);
CREATE TABLE contacts(
contact_id INT GENERATED ALWAYS AS IDENTITY,
customer_id INT,
contact_name VARCHAR(255) NOT NULL UNIQUE,
PRIMARY KEY(contact_id),
CONSTRAINT fk_customer
FOREIGN KEY(customer_id)
REFERENCES customers(customer_id)
);
INSERT INTO customers(customer_name) VALUES('my_first_customer')
So from here i want to do something like that :
INSERT INTO contacts(customer_id, contact_name ) VALUES('my_first_customer',"thomas")
and i want to get this result in contacts table :
contact_id
customer_id
contact_name
1
1
"thomas"
i tried to make a function to change the value from name to id but get an error of type.
Because the the type error is catch before the trigger. Here is my function
CREATE FUNCTION get_id_fct()
RETURNS TRIGGER AS $get_id_fct$
DECLARE val_id INTEGER;
BEGIN
SELECT customer_id INTO val_id FROM customers WHERE customers.customer_id = NEW.customer_id;
NEW.customer_id := val_id;
RETURN NEW
END
$get_id_fct$ LANGUAGE plpgsql;
CREATE TRIGGER get_id
BEFORE INSERT ON contacts
FOR EACH ROW EXECUTE PROCEDURE get_id_fct();'
Is their a way around that ? or specific method to do this task that i don't know about ?
I am quite a beginner at SQL.
You can make a subquery in insert statement
INSERT INTO contacts(customer_id, contact_name ) VALUES (
(select customer_id from customers where customer_name = 'my_first_customer') ,'thomas');
This query will fail if there is more than one customer with given name, and insert a null value for customer_id if there is no customers with given name
Different approach (proposed by #wildplasser)
INSERT INTO contacts(customer_id, contact_name )
select customer_id,'thomas' from customers where customer_name = 'my_first_customer';
In this case, when there is no customer with given name, no row will be created. When there is more than one customer with given name, for each of them record will be created.
Or you can create view with INSTEAD OF trigger.
create view v_contacts as
select customer_name, contact_name from customers
join contacts on customers.customer_id = contacts.customer_id;
CREATE FUNCTION emp () RETURNS trigger AS $$
BEGIN
INSERT INTO contacts(customer_id, contact_name )
VALUES((select customer_id from customers where customer_name =
NEW.customer_NAME),NEW.contact_NAME);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER v_contactst
INSTEAD OF INSERT ON v_contacts
FOR EACH ROW
EXECUTE FUNCTION emp();
Insert statement
INSERT INTO v_contacts(customer_name, contact_name ) VALUES('my_first_customer','thomas')
And just a note, that columns customer_name and contact_name are not unique so this code is not safe and throw error if customer_name didn't exists or there is more than one record with this customer_name

Oracle SQL: what is the cleanest way to refactor WITH...SELECT... into a function

So I have been trying to find the best way to re-write a large chunk of SQL in plan script, in form of
WITH
A AS (...<SUB_QA>...),
B AS (...<SUB_QB>...),
C AS (...<SUB_QC>...),
...
SELECT ... FROM
A
LEFT JOIN B
LEFT JOIN C
LEFT JOIN ...
ON ....
into a FUNCTION. This is mainly to facilitate the reuse the same logic represented by that big chunk in multiple places.
Contraint 1: can only use RECORD instead of creating customized TYPE;
Contraint 2: have to keep the content of the those subqueries (e.g.
, etc) under WITH clause as they are, since each is
considerably complex.
So far, I only came up with the following, as a simplified example.
It involves putting the WITH clause in the cursor loop
But does it run the WITH clause in each loop, which would be a big worry in term of performance? When written in function form, and when I run it with SqlDeveloper's 'Explain Plan' function, it doesn't reveal much helpful information at all.
Is there better/cleaner/more performant way to do this?
SQL to create data:
--------PERSON table------------
DROP TABLE Test_Persons;
CREATE TABLE Test_Persons (
PersonID int,
LastName varchar2(255),
FirstName varchar2(255)
);
INSERT INTO Test_Persons
(PersonID,LastName,FirstName)
values(1,'LN_1','FN_1');
INSERT INTO Test_Persons
(PersonID,LastName,FirstName)
values(2,'LN_2','FN_2');
INSERT INTO Test_Persons
(PersonID,LastName,FirstName)
values(3,'LN_21','FN_2');
--------Salary table------------
DROP TABLE TEST_SALARY_A;
CREATE TABLE TEST_SALARY_A ( -- no 'OR REPLACE' for ORACLE
SalaryID int,
PersonID int,
Amount int,
Tax int,
Bank varchar2(20)
);
INSERT INTO TEST_SALARY_A
(SalaryID, PersonID, Amount, Tax, Bank)
VALUES
(1, 1, 1000, 300, 'BOA1');
INSERT INTO TEST_SALARY_A
(SalaryID, PersonID, Amount, Tax, Bank)
VALUES
(2, 2, 2000, 600, 'JP1');
INSERT INTO TEST_SALARY_A
(SalaryID, PersonID, Amount, Tax, Bank)
VALUES
(3, 3, 3000, 900, 'TD1');
--------Address table------------
DROP TABLE TEST_ADDRESS_A;
CREATE TABLE TEST_ADDRESS_A (
AddressID int,
PersonID int,
Address varchar2(255)
);
INSERT INTO TEST_ADDRESS_A
(AddressID, PersonID, Address)
VALUES
(1, 1, 'address1');
INSERT INTO TEST_ADDRESS_A
(AddressID, PersonID, Address)
VALUES
(2, 2, 'address2');
INSERT INTO TEST_ADDRESS_A
(AddressID, PersonID, Address)
VALUES
(3, 3, 'address3');
commit;
Original SQL in Chunk:
------------------Original--------------------
WITH
TEST_JOINED_1 AS (
SELECT
tps.PERSONID,
tps.LASTNAME,
tsd.ADDRESS
FROM TEST_PERSONS tps
LEFT JOIN TEST_ADDRESS_A tsd ON tps.personid = tsd.personid WHERE tps.LASTNAME = 'LN_1'
),
TEST_JOINED_2 AS (
SELECT
tps.PERSONID,
tsl.BANK,
tsl.TAX
FROM TEST_PERSONS tps
LEFT JOIN TEST_SALARY_A tsl ON tps.personid = tsl.personid WHERE tps.LASTNAME = 'LN_1'
)
SELECT tj1.PERSONID as tj1_ID, tj1.LASTNAME, tj1.ADDRESS, tj2.PERSONID as tj2_ID, tj2.BANK, tj2.TAX
FROM TEST_JOINED_1 tj1
LEFT JOIN TEST_JOINED_2 tj2 ON tj1.PERSONID = tj2.PERSONID
WHERE tj1.LASTNAME = 'LN_1';
Rewrite in FUNCTION:
------------------Rewritten in functions with ------------------
------------------Contraint 1: can only use RECORD instead of creating customized TYPE;------------
------------------Contraint 2: have to keep the content of the two subqueries under WITH clause exactly as it is --------------
CREATE OR REPLACE PACKAGE MY_JOIN_TEST_SP_PACKAGE_3 AS
TYPE join_record_type IS RECORD(
PersonID1 int,
LastName varchar2(255),
Address varchar2(255),
PersonID2 int,
Bank varchar2(20),
Tax int
);
TYPE join_record_table_type IS TABLE OF join_record_type;
FUNCTION get_joined_data(last_name VARCHAR2)
RETURN join_record_table_type
PIPELINED;
END;
/
CREATE OR REPLACE PACKAGE BODY MY_JOIN_TEST_SP_PACKAGE_3 AS
FUNCTION get_joined_data(last_name VARCHAR2)
RETURN join_record_table_type
PIPELINED
AS
join_record join_record_type;
BEGIN
FOR x IN (
-------------------start - WITH clause -- does this run for every RECORD x in the loop??? -----------------------
WITH
TEST_JOINED_1 AS (
SELECT
tps.PERSONID,
tps.LASTNAME,
tsd.ADDRESS
FROM TEST_PERSONS tps
LEFT JOIN TEST_ADDRESS_A tsd ON tps.personid = tsd.personid WHERE tps.LASTNAME = last_name
),
TEST_JOINED_2 AS (
SELECT
tps.PERSONID,
tsl.BANK,
tsl.TAX
FROM TEST_PERSONS tps
LEFT JOIN TEST_SALARY_A tsl ON tps.personid = tsl.personid WHERE tps.LASTNAME = last_name
)
-------------------end - WITH clause -------------------
-------------------start - main select-----------------------
SELECT tj1.PERSONID as tj1_ID, tj1.LASTNAME, tj1.ADDRESS, tj2.PERSONID as tj2_ID, tj2.BANK, tj2.TAX
FROM TEST_JOINED_1 tj1
LEFT JOIN TEST_JOINED_2 tj2 ON tj1.PERSONID = tj2.PERSONID
WHERE tj1.LASTNAME = last_name
-------------------end - main select--------------------------
)
LOOP
SELECT x.tj1_ID, x.LASTNAME, x.ADDRESS, x.tj2_ID, x.BANK, x.TAX
INTO join_record
FROM DUAL;
PIPE ROW (join_record);
END LOOP;
END;
END; -- END of CREATE
/
select * from table(MY_JOIN_TEST_SP_PACKAGE_3.get_joined_data('LN_1'));
EDITED: modified example code to have variable in the WITH clause
----------------------Create GLOBAL TEMPORARY TABLE -------------------------
DROP TABLE my_global_temp_table;
CREATE GLOBAL TEMPORARY TABLE my_global_temp_table (
PersonID int,
LastName varchar2(255),
Address varchar2(255),
Bank varchar2(20)
)
ON COMMIT DELETE ROWS;
----------------------Create PACKAGE AND FUNCTION -------------------------
CREATE OR REPLACE PACKAGE MY_JOIN_TEST_SP_PACKAGE_3 AS
TYPE join_record_type IS RECORD(
PersonID int,
LastName varchar2(255),
Address varchar2(255),
Bank varchar2(20)
);
TYPE join_record_table_type IS TABLE OF join_record_type;
FUNCTION get_joined_data(last_name VARCHAR2)
RETURN join_record_table_type
PIPELINED;
END;
/
CREATE OR REPLACE PACKAGE BODY MY_JOIN_TEST_SP_PACKAGE_3 AS
FUNCTION get_joined_data(last_name VARCHAR2)
RETURN join_record_table_type
PIPELINED
AS
join_record join_record_type;
BEGIN
--------------------use GLOBAL TEMPORARY TABLE-------------------------
INSERT INTO my_global_temp_table
-------------------start - WITH ... SELECT ... clause -- does this run for every RECORD x in the loop??? -----------------------
WITH
TEST_JOINED_1 AS (
SELECT
tps.PERSONID,
tps.LASTNAME,
tsd.ADDRESS
FROM TEST_PERSONS tps
LEFT JOIN TEST_ADDRESS_A tsd ON tps.personid = tsd.personid
WHERE tps.LASTNAME = last_name
)
SELECT tj1.PERSONID, tj1.LASTNAME, tj1.ADDRESS, ts.BANK
FROM TEST_JOINED_1 tj1
LEFT JOIN TEST_SALARY_A ts ON tj1.PERSONID = ts.PERSONID
WHERE tj1.LASTNAME = last_name;
-------------------end - WITH ... SELECT ... clause --
FOR x IN (
SELECT * FROM my_global_temp_table
)
LOOP
SELECT x.PERSONID, x.LASTNAME, x.ADDRESS, x.BANK
INTO join_record
FROM DUAL;
PIPE ROW (join_record);
END LOOP;
END;
END; -- END of CREATE
/
--------------------Call the FUNCTION-------------------------
select * from table(MY_JOIN_TEST_SP_PACKAGE_3.get_joined_data('LN_1'));
EDITED: following #Littefoot suggestion, try out using CREATE GLOBAL TEMP table but giving out '17/21 PL/SQL: ORA-00936: missing expression'. I am not sure why?
EDITED: Corrected the Insert syntax, but will get error 'ORA-14551: cannot perform a DML operation inside a query', I believe that this is because I called the function that contains that Insert from a SELECT
If you are not using variables and other pl sql constructs, I would suggest you break with clauses in the form of a table or materialize view. In this way you don't need to take risk of rewriting the logic of query in pl sql block and missing something.
I would suggest using materialize view advantage of materialize view over the table is that you don't need to drop table next time you load data and also you can use nologging with materialized view.No logging makes it very fast.
It will be very fast and have minimum risk.
Thanks
Bhanu Yadav
As there's nothing dynamic in the WITH factoring clause (i.e. you don't use variables - at least, I didn't notice any), I'd suggest you to create a view (based on that WITH) and use it whenever needed.
If the real query is really complex and takes time to execute, you could create a global temporary table (GTT), most probably choosing to keep its data during the session (ON COMMIT PRESERVE ROWS) properly index it and store view (or WITH's) contents in there. Then you'd use the GTT in your code
Although, Oracle will keep date returned by a query in memory so you might even have to really "execute" it once, but the memory isn't unlimited so ... test it, compare results you get, pick the one that seems to be the best.
To me, the GTT idea sounds promising, but without actual information it is difficult to decide.
[EDIT, about GTT]
Oracle's "Global temporary table" is, actually, "local" from your point if view (note that, if you're on 18c (I don't think you are, though) you can create a private temporary table). You create it once, using the create global temporary table .... Data you insert into it is visible ONLY to you, nobody else; it is restricted to your own transaction (if it is created with the ON COMMIT DELETE ROWS) or session (ON COMMIT PRESERVE ROWS). Pick the one that suits you best.
What does it mean? It means that you'd create the GTT once, providing column list and their datatypes. Every user, that uses your procedure, would insert its own data set into it (you'd use a query with the LAST NAME parameter, as you said) and use it throughout the transaction (or session). Many users can do that at the same time, but - as I've said - everyone would see only its own data.
Here's the pseudocode:
-- create table once. Do NOT create it, drop it, create again tomorrow, drop ...
-- Create it once, use it many times.
create global temporary table gtt_my_data
(id number,
c_name varchar2(20), ...
)
on commit preserve rows;
create index i1_gmd_id on gtt_my_data (id);
-- your procedure
procedure p_myproc (par_last_name in varchar2) is
begin
insert into gtt_my_data (id, c_name, ...)
select id, c_name, ...
from some_table join some_other_table ...
where some_table.last_name = par_last_name;
-- now, do whatever you do. When you need to fetch data from the GTT, do so
select ... into ...
from table_x join gtt_my_data on ...
update ... set some_column = (select another_column
from table_y join gtt_my_data on ...
)
end;
Once you're done: if you end the session, data will be removed from the GTT. If you want, you can do it manually (either delete or truncate its contents).
[EDIT #2: INSERT INTO A GTT]
Insert is wrong; you don't insert values, but something like this:
INSERT INTO my_global_temp_table
WITH test_joined_1 AS
(SELECT tps.personid,
tps.lastname,
tsd.address
FROM test_persons tps
LEFT JOIN test_address_a tsd ON tps.personid = tsd.personid
WHERE tps.lastname = last_name
)
SELECT tj1.personid,
tj1.lastname,
tj1.address,
ts.bank
FROM test_joined_1 tj1
LEFT JOIN test_salary_a ts ON tj1.personid = ts.personid
WHERE tj1.lastname = last_name;
Simplified, on Scott's schema:
SQL> create table test (empno number, deptno number);
Table created.
SQL> insert into test (empno, deptno)
2 with temp as
3 (select empno, deptno from emp)
4 select t.empno, t.deptno
5 from temp t join dept d on d.deptno = t.deptno
6 where d.deptno = 10;
3 rows created.
SQL>

SQL: Stored procedure

I am implementing a library management system in SQL. I have the following table structure and some values inserted in them:
create table books
(
IdBook number(5),
NameBook varchar2(35),
primary key(IdBook)
);
create table users
(
IdUsers number(5),
NameUser varchar2(20),
primary key(IdUsers)
);
create table borrowed
(
IdBorrowed number(5),
IdUsers number(5),
IdBook number(5),
DueDate date,
DateReturned date,
constraint fk_borrowed foreign key(IdUsers) references users(IdUsers),
constraint fk_borrowed2 foreign key(IdBook) references books(IdBook)
);
insert into books values(0,'FairyTale');
insert into books values(1,'Crime and Punishment');
insert into books values(2,'Anna Karenina');
insert into books values(3,'Norwegian Wood');
insert into users values(01,'Robb Dora');
insert into users values(02,'Pop Alina');
insert into users values(03,'Grozavescu Teodor');
insert into users values(04,'Popa Alin');
insert into borrowed values(10,02,3,'22-Jan-2017',null);
insert into borrowed values(11,01,1,'25-Jan-2017','19-Dec-2016');
insert into borrowed values(12,01,3,'22-Jan-2017',null);
insert into borrowed values(13,04,2,'22-Jan-2017','13-Dec-2016');
What I want now is that my db to allow "borrowing" books for the users(i.e insert into the borrowed table) that have no unreturned books(i.e date returned is not null) and if they have unreturned books I want to abandon the whole process. I thought to implement this in the following way:
create or replace procedure borrowBook(IdBorrowed in number,IdUsers number,IdBook number,DueDate date,DateReturned date) as begin
if exists (SELECT u.IdUsers, u.NameUser, b.DateReturned
FROM users u, borrowed b
WHERE u.IDUSERS = b.IdUsers and DateReturned is not null),
insert into borrowed values(IdBorrowed,IdUsers,IdBook,DueDate,DateReturned);
end borrowBook;
The above procedure does not check if the parameter I pass to this function is the same as the one in my select and I do not know how to do this and correctly insert a value in my table.
Any help would be much appreciated. Thank in advance!
You should not name your parameters the same as columns also used inside the procedure.
You can also simplify your procedure to a single INSERT statement, no IF required:
create or replace procedure borrowBook(p_idborrowed in number, p_idusers number, p_idbook number, p_duedate date, p_datereturned date)
as
begin
insert into borrowed (idborrowed, idusers, idbook, duedate, datereturned)
select p_idborrowed, p_idusers, p_idbook, p_duedate, p_datereturned
from dual
where not exists (select *
from users u
join borrowed b on u.idusers = b.idusers
and b.datereturned is not null);
end borrowBook;
It's also good coding style to explicitly list the columns for an INSERT statement. And you should get used to the explicit JOIN operator instead of using implicit joins in the where clause.
What about this one:
create or replace procedure borrowBook( p_IdBorrowed in number ,
p_IdUsers number ,
p_IdBook number ,
p_DueDate date ,
p_DateReturned date )
as
begin
if (SELECT COUNT(*)
FROM borrowed
WHERE IDUSERS = p_IdUsers
AND DateReturned IS NULL) = 0 THEN
insert into borrowed values (p_IdBorrowed ,
p_IdUsers ,
p_IdBook ,
p_DueDate ,
p_DateReturned );
end if ;
end borrowBook;
You would seem to want something like this:
create or replace procedure borrowBook (
in_IdBorrowed in number,
in_IdUsers number,
in_IdBook number,
in_DueDate date,
in_DateReturned date
) as
v_flag number;
begin
select (case when exists (select 1
from borrowed b
where b.IdUsers = in_IdUsers and b.DateReturned is not null
)
then 1 else 0
end)
into v_flag
from dual;
if (flag = 0) then
insert into borrowed
values(in_IdBorrowed, in_IdUsers, in_IdBook, in_DueDate, v_DateReturned);
end if
end -- borrowBook;

Insert values of one table in a database to another table in another database

I would like to take some data from a table from DB1 and insert some of that data to a table in DB2.
How would one proceed to do this?
This is what I've got so far:
CREATE VIEW old_study AS
SELECT *
FROM dblink('dbname=mydb', 'select name,begins,ends from study')
AS t1(name varchar(50), register_start date, register_end date);
/*old_study now contains the data I wanna transfer*/
INSERT INTO studies VALUES (nextval('studiesSequence'),name, '',3, 0, register_start, register_end)
SELECT name, register_start, register_end from old_study;
This is how my table in DB2 looks:
CREATE TABLE studies(
id int8 PRIMARY KEY NOT NULL,
name_string VARCHAR(255) NOT NULL,
description VARCHAR(255),
field int8 REFERENCES options_table(id) NOT NULL,
is_active INTEGER NOT NULL,
register_start DATE NOT NULL,
register_end DATE NOT NULL
);
You should include the column names in both the insert and select:
insert into vip_employees(name, age, occupation)
select name, age, occupation
from employees;
However, your data structure is suspect. Either you should use a flag in employees to identify the "VIP employees". Or you should have a primary key in employees and use this primary key in vip_employees to refer to employees. Copying over the data fields is rarely the right thing to do, especially for columns such as age which are going to change over time. Speaking of that, you normally derive age from the date of birth, rather than storing it directly in a table.
INSERT INTO studies
(
id
,name_string
,description
,field
,is_active
,register_start
,register_end
)
SELECT nextval('studiesSequence')
,NAME
,''
,3
,0
,register_start
,register_end
FROM dblink('dbname=mydb', 'select name,begins,ends from study')
AS t1(NAME VARCHAR(50), register_start DATE, register_end DATE);
You can directly insert values that retured by dblink()(that means no need to create a view)
Loop and cursor are weapons of last resort. Try to avoid them. You probably want INSERT INTO ... SELECT:
INSERT INTO x(x, y, z)
SELECT x, y, z
FROM t;
SqlFiddleDemo
EDIT:
INSERT INTO vip_employees(name, age, occupation) -- your column list may vary
SELECT name, age, occupation
FROM employees;
Your syntax is wrong. You cannot have both, a values clause for constant values and a select clause for a query in your INSERT statement.
You'd have to select constant values in your query:
insert into studies
(
id,
name_string,
description,
field,
is_active,
register_start,
register_end
)
select
studiesSequence.nextval,
name,
'Test',
null,
0,
register_start,
register_end
from old_study;

i am trying to execute the before insert trigger , but i m getting the sql errors

what i want to achieve is i have a table called orders.
i want to perform the before insert trigger on my orders table.i want to capture the
username of person performing INSERT into table.
one table called info which contain the user.
this is my code
create table orders
(
order_id int,
quantity int,
cost int,
total_cost int,
created_date datetime,
created_by varchar(20)
)
create trigger beforeInsertdata
before insert
on orders
for each row
declare
v_username varchar2(10);
begin
-- Find username of person performing INSERT into table
SELECT user INTO v_username
FROM info;
-- Update create_date field to current system date
:new.create_date := sysdate;
-- Update created_by field to the username of the person performing the INSERT
:new.created_by := v_username;
END;
--user information--
create table info
(
userid int ,
user_name varchar(10)
)
insert into info values(1,'vivek')
select * from info
Basically, triggers are classified into two main types:-
1)After Triggers (For Triggers)
2)Instead Of Triggers
and the syntax for trigger is
CREATE TRIGGER trigger_name ON table_name
[FOR|AFTER|INSTEAD OF] [INSERT|UPDATE|DELETE]
AS
//your code goes here
GO
NOTE : FOR keyword used for INSERT |UPDATE Command where as AFTER USED FOR DELETE Command.
It's hard to tell what you're really trying to do. I've modified your code sample so that it will work on SQL2K5 and made some assumptions about how you're wanting to use the connected user account.
CREATE TABLE orders (
order_id int,
quantity int,
cost int,
total_cost int,
created_date datetime,
created_by varchar(20)
);
CREATE TABLE info (
userid int,
user_name varchar(10)
);
INSERT INTO info
VALUES (1, 'vivek');
SELECT *
FROM info;
CREATE TRIGGER orders_InsteadOfInsert ON orders
INSTEAD OF INSERT AS BEGIN
SET NOCOUNT ON;
-- varchar(10) is to match your table, but probably should be larger
DECLARE #CurrentUser VarChar(10);
SELECT #CurrentUser = SYSTEM_USER;
IF (#CurrentUser NOT IN (SELECT user_name FROM info)) BEGIN
-- consider using an identity column for the key instead of this
INSERT INTO info (userid, user_name)
SELECT
ISNULL((SELECT MAX(userid) FROM info), 0) + 1,
#CurrentUser;
END;
INSERT INTO orders (order_id, quantity, cost, total_cost, created_date, created_by)
SELECT
INS.order_id,
INS.quantity,
INS.cost,
INS.total_cost,
GETDATE(),
#CurrentUser
FROM INSERTED INS;
END;