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;
Related
I have created a database for an airline that currently has 3 tables: Table1: FLIGHTS, Table2: Clients, Table3: Reservation, and I want to create a function that returns the time left in the month from the reservation made till the flights date inside a function. After I run it, nothing is returning. Any tips?
DROP DATABASE if exists Chartered_Airlines;
CREATE DATABASE Chartered_Airlines;
USE Chartered_Airlines;
CREATE TABLE FLIGHTS(FLIGHT_NO INT(5) NOT NULL, DEPARTURE VARCHAR(15), ARRIVAL VARCHAR(15), TYPEFL VARCHAR(15), SEATS INT(4) NOT NULL, FREE_SEATS INT(4), FLIGHT_DATE DATE,
PRIMARY KEY(FLIGHT_NO));
CREATE TABLE CUSTOMERS(CL_NO INT(5) NOT NULL, LAST_NAME VARCHAR(15) NOT NULL, FIRST_NAME VARCHAR(15) NOT NULL, CITIZENSHIP VARCHAR(15) NOT NULL, B_DATE DATE,
PRIMARY KEY(CL_NO));
CREATE TABLE RESERVATIONS(RES_NO INT(5) NOT NULL, CL_NO INT(5) NOT NULL, FLIGHT_NO INT(5), COST FLOAT(15), RES_DATE DATE,
PRIMARY KEY(RES_NO),
FOREIGN KEY(FLIGHT_NO) REFERENCES FLIGHTS(FLIGHT_NO),
FOREIGN KEY(CL_NO) REFERENCES CUSTOMERS(CL_NO));
SHOW TABLES;
INSERT INTO FLIGHTS VALUES(10,'ATHENS','LONDON','EXTERNAL',310,34, '2023/10/04');
INSERT INTO FLIGHTS VALUES(20,'ATHENS','MYKONOS','INTERNAL',100,5, '2022/12/12');
INSERT INTO FLIGHTS VALUES(30,'ATHENS','PARIS','EXTERNAL',256,16, '2022/09/07');
INSERT INTO CUSTOMERS VALUES(5472,'ALEKSANDRAKIS','GEORGIOS','GREECE','1989/01/01');
INSERT INTO CUSTOMERS VALUES(1354,'STAVROU','ANTREAS','GREECE','1977/07/07');
INSERT INTO CUSTOMERS VALUES(6598,'AMALFIO','ROBERTO','ITALY','1995/05/02');
INSERT INTO CUSTOMERS VALUES(1953,'EUSTATHIOU','NIKOS','GREECE','2001/06/03');
INSERT INTO CUSTOMERS VALUES(1387,'STAKAS','VASILLIS','GREECE','1990/01/07');
INSERT INTO RESERVATIONS VALUES(3174,5472,10,6482, '2020/03/02');
INSERT INTO RESERVATIONS VALUES(3143,1354,10,6482, '2021/05/09');
INSERT INTO RESERVATIONS VALUES(1286,6598,20,662, '2022/10/12');
INSERT INTO RESERVATIONS VALUES(3275,1953,20,662, '2022/09/08');
INSERT INTO RESERVATIONS VALUES(7654,1387,30,264, '2022/09/06');
SELECT * FROM FLIGHTS;
SELECT * FROM CUSTOMERS;
SELECT * FROM RESERVATIONS;
DESC FLIGHTS;
DESC RESERVATIONS;
DESC CUSTOMERS;
DELIMITER //
CREATE FUNCTION TIME_LEFT(RES_NO INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE TIME_LEFT INT DEFAULT 0;
SELECT TIMESTAMPDIFF(MONTH,RESERVATIONS.RES_DATE,FLIGHTS.FLIGHT_DATE) INTO TIME_LEFT FROM RESERVATIONS
WHERE RES_NO = RES_NO AND RESERVATIONS.FLIGHT_NO = FLIGHTS.FLIGHT_NO ;
RETURN TIME_LEFT;
END;
//
SELECT TIME_LEFT(3143);
I was expecting that it returns the date difference between reservation made date and flight date
Your forgot to use join and also use a variable name for holding the value passed in function:
DELIMITER //
CREATE FUNCTION TIME_LEFT(VAR_RES_NO INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE TIME_LEFT INT DEFAULT 0;
SELECT TIMESTAMPDIFF(MONTH,RESERVATIONS.RES_DATE,FLIGHTS.FLIGHT_DATE) INTO TIME_LEFT FROM RESERVATIONS join FLIGHTS
WHERE RES_NO = VAR_RES_NO AND RESERVATIONS.FLIGHT_NO = FLIGHTS.FLIGHT_NO ;
RETURN TIME_LEFT;
END;
//
I think you need to include the table Flights in the SELECT statement, as well as specify the difference between RES_NO in the input vs reservations e.g.,
CREATE FUNCTION TIME_LEFT(RES_NO_in INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE TIME_LEFT INT DEFAULT 0;
SELECT TIMESTAMPDIFF(MONTH,RESERVATIONS.RES_DATE,FLIGHTS.FLIGHT_DATE) INTO TIME_LEFT
FROM RESERVATIONS, FLIGHTS
WHERE RESERVATIONS.RES_NO = RES_NO_in AND RESERVATIONS.FLIGHT_NO = FLIGHTS.FLIGHT_NO;
RETURN TIME_LEFT;
END;
or
CREATE FUNCTION TIME_LEFT(RES_NO_in INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE TIME_LEFT INT DEFAULT 0;
SELECT TIMESTAMPDIFF(MONTH,RESERVATIONS.RES_DATE,FLIGHTS.FLIGHT_DATE) INTO TIME_LEFT
FROM RESERVATIONS
INNER JOIN FLIGHTS ON RESERVATIONS.FLIGHT_NO = FLIGHTS.FLIGHT_NO
WHERE RESERVATIONS.RES_NO = RES_NO_in;
RETURN TIME_LEFT;
END;
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
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>
When I try to call the procedure with call insertp_detail ('P005','02','T001','CAT2') , it will show the error :
SQL0811N The result of a scalar fullselect, SELECT INTO statement, or
VALUES INTO statement is more than one row. SQLSTATE=21000
The expected result that I want with this procedure is to insert the values into purchase_detail table.
CREATE PROCEDURE insertp_detail
(IN purchase_id char(4), seat_id char(2), trans_id char(4), seat_type varchar(5))
BEGIN
IF ((SELECT COUNT(trans_id)
FROM purchase_detail
GROUP BY trans_id) < 3)
THEN INSERT into purchase_detail values(purchase_id, seat_id, trans_id, seat_type);
end if;
end
This is correct. You have a group by which generally returns multiple rows. Presumably you intend to count the number of rows only for the trans_id provided as an argument to the stored procedure:
CREATE PROCEDURE insertp_detail (
IN in_purchase_id char(4),
IN in_seat_id char(2),
IN in_trans_id char(4),
IN in_seat_type varchar(5)
)
BEGIN
IF ((SELECT COUNT(*)
FROM purchase_detail
WHERE pd.trans_id = in_trans_id
) < 3)
THEN
INSERT INTO purchase_detail
VALUES (in_purchase_id, in_seat_id, in_trans_id, in_seat_type);
END IF;
END;
Note that the parameters to the stored procedure have prefixes to distinguish them from column names.
You should also list the columns for purchase_detail for the INSERT statement.
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;