How to use GROUP function (MAX) in FUNCTION? - sql

CREATE TABLE cursor_table(
emp_id NUMBER(10) PRIMARY KEY,
emp_name VARCHAR2(30),
emp_salary NUMBER(5)
);
INSERT INTO cursor_table VALUES(101,'addeesh',25000);
INSERT INTO cursor_table VALUES(102,'arunkumar',28000);
INSERT INTO cursor_table VALUES(103,'ashokkumar',35000);
INSERT INTO cursor_table VALUES(104,'durairaj',22000);
CREATE OR REPLACE FUNCTION high_payed_emp
RETURN NUMBER
IS
high_payed_emp_id NUMBER(10):=0;
DECLARE max_emp_salary = MAX(emp_salary);
BEGIN
SELECT emp_id INTO high_payed_emp_id
FROM cursor_table
WHERE emp_salary = max_emp_salary;
RETURN high_payed_emp_id;
END;
I need to create a function that will find the highest paid employee.
Error message: Encountered the symbol "DECLARE" when expecting one of the following: begin function pragma procedure subtype type current cursor delete exists prior

You need a SQL statement to use your MAX function.
CREATE OR REPLACE FUNCTION high_payed_emp
RETURN NUMBER
IS
high_payed_emp_id NUMBER(10):=0;
max_emp_salary NUMBER :=0;
BEGIN
SELECT MAX(emp_salary)
INTO max_emp_salary
FROM cursor_table;
SELECT emp_id INTO high_payed_emp_id
FROM cursor_table
WHERE emp_salary = max_emp_salary;
RETURN high_payed_emp_id;
END;

Please check the code below, you had both syntactical and logical errors.
CREATE OR REPLACE FUNCTION high_payed_emp
RETURN NUMBER
IS
high_payed_emp_id NUMBER(10):=0;
BEGIN
select emp_id
into high_payed_emp_id
from (
select emp_id
from cursor_Table
order by emp_salary desc
)
where rownum = 1;
RETURN high_payed_emp_id;
END;

Just use the subquery to compare max salary, your code should look like this:
CREATE OR REPLACE FUNCTION high_payed_emp
RETURN NUMBER
IS
high_payed_emp_id NUMBER(10);
BEGIN
SELECT emp_id INTO high_payed_emp_id
FROM cursor_table
WHERE emp_salary = (Select max(emp_salary) from cursor_table);
RETURN high_payed_emp_id;
END;
Execution:
Select high_payed_emp from dual;
OUTPUT:
HIGH_PAYED_EMP
--------------
103

Related

Procedure to insert data if not exist pl sql

I have a table with these columns (id,first_name,birth). I want to create a procedure that insert a new customer only if the id inserted doesn't exist in the table. If it already exist, then don't insert it. This is my code so far, but I got an error 'line 3 sql statement ignored'. Any idea? I need to use procedure and pl sql in oracle. Thanks!
CREATE OR REPLACE PROCEDURE add_emp(v_id IN int,
v_name IN varchar2,
v_bday IN date) IS
BEGIN
INSERT INTO Employees
(Id, First_name, Birth)
SELECT *
FROM (SELECT v_id, v_name, v_bday) AS tmp
WHERE NOT EXISTS (SELECT Id FROM Employees WHERE Id = v_id);
END;
/
DECLARE
m_id int := 3;
m_name varchar2 := 'John';
m_bday date := '16-Dec-1990';
BEGIN
add_cust(m_id, m_name, m_bday);
END;
/
Your procedure has some syntax issue which is fixed in following code:
CREATE OR REPLACE PROCEDURE ADD_EMP (
V_ID IN INT,
V_NAME IN VARCHAR2,
V_BDAY IN DATE
) IS
BEGIN
INSERT INTO EMPLOYEES (
ID,
FIRST_NAME,
BIRTH
)
SELECT V_ID,
V_NAME,
V_BDAY
FROM DUAL -- FROM clause was missing
WHERE NOT EXISTS (
SELECT ID
FROM EMPLOYEES
WHERE ID = V_ID
);
END;
/
Also, Your calling PL/SQL block has some issues which are corrected in the following code:
DECLARE
M_ID INT := 3;
M_NAME VARCHAR2(10) := 'John'; -- varchar2 must be declared with size
M_BDAY DATE := DATE '1990-12-16'; -- added date literal to convert string to date
BEGIN
ADD_CUST(M_ID, M_NAME, M_BDAY);
END;
/
In Oracle SELECT does not work without a FROM clause (other DBMS products are different). So you need to provide a table; you can use DUAL, which is a dummy table provided by Oracle which is guaranteed to return one row.
INSERT INTO Employees(Id,First_name,Birth)
SELECT v_id, v_name, v_bday
from dual
WHERE NOT EXISTS (
SELECT Id FROM Employees WHERE Id = v_id
);
Your INSERT statement would work with a slight change
CREATE OR REPLACE PROCEDURE add_emp(v_id Employees.Id%type,
v_name Employees.First_name%type,
v_bday Employees.Birth%type) IS
BEGIN
INSERT INTO Employees
(Id, First_name, Birth)
SELECT v_id, v_name, v_bday
FROM dual
WHERE NOT EXISTS (SELECT * FROM Employees WHERE Id = v_id);
END;
/
You can replace INSERT statement with MERGE as an alternative DML such as
MERGE INTO Employees e1
USING
(SELECT v_id AS id, v_name AS First_name, v_bday AS birth
FROM dual) e2
ON ( e1.id = e2.id )
WHEN MATCHED THEN UPDATE SET e1.First_name = e2.First_name,
e1.Birth = e2.Birth -- If you need to change for the matching case
WHEN NOT MATCHED THEN INSERT( e1.id, e1.First_name, e1.birth )
VALUES( e2.id, e2.First_name, e2.birth );

oracle sql : "get or insert" stored procedure

I would like to do a stored procedure that receive an input param and then
if targeted table does not contains that value, a new row is created and then the id of the created row is returned
if targeted table already contain input param, the id of the row is returned
For moment I only manage to insert new row only if input param is new:
--exemple of table with a primary id a column with value
create table unique_number_table (
id NUMBER(12) not null,
UNIQUE_NUMBER VARCHAR2(80) not null,
constraint PK_ID primary key (ID)
);
create sequence SEQ_NUMBER
INCREMENT BY 1
START WITH 2
MAXVALUE 999999999
MINVALUE 0;
create or replace procedure insert_or_get_unique_number ( input_number in varchar ) is
begin
insert into unique_number_table (id, UNIQUE_NUMBER)
select SEQ_NUMBER.NEXTVAL ,input_number
from dual
where not exists(select * from unique_number_table
where UNIQUE_NUMBER =input_number);
end insert_or_get_unique_number;
Do you know how to do this?
Seems to me like you want a stored function and not a procedure.
create or replace function insert_or_get_unique_number (input_number varchar2)
return UNIQUE_NUMBER_TABLE.ID%type
is
L_NUM UNIQUE_NUMBER_TABLE.ID%type;
begin
select ID
into L_NUM
from UNIQUE_NUMBER_TABLE
where UNIQUE_NUMBER = input_number;
return L_NUM;
exception
when NO_DATA_FOUND then
insert into unique_number_table (id, UNIQUE_NUMBER)
values (SEQ_NUMBER.NEXTVAL, input_number)
returning ID into L_NUM;
return L_NUM;
end insert_or_get_unique_number;
This is a possible solution to your problem.
CREATE OR REPLACE PROCEDURE insert_or_get_unique_number (
input_number IN VARCHAR,
c_out out sys_refcursor
) IS
Lv_input_exists INT;
lv_myRowid VARCHAR2(200);
err_code varchar2(600);
err_msg varchar2(500);
BEGIN
--step 1 check if the input param exists. -
select count(*)
INTO Lv_input_exists
FROM unique_number_table
WHERE unique_number = input_number;
--step 2 if it exists than get the rowid of that row and return that value
IF Lv_input_exists > 0
THEN
OPEN c_out for
SELECT ROWID
FROM unique_number_table Uni
WHERE uni.id = input_number ;
RETURN;
ELSE
-- STEP 3 the input number does not exists therefore we need to insert and return the rowid--
INSERT INTO unique_number_table (
id,
unique_number
)
VALUES(
seq_number.NEXTVAL,
input_number)
returning ROWID into lv_myRowid;
----STEP 4 Open the cursor and return get the rowid.
OPEN c_out for
SELECT lv_myRowid
FROM DUAL ;
SYS.dbms_output.put_line( 'Done' );
END IF;
EXCEPTION WHEN OTHERS THEN
err_code := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 200);
SYS.dbms_output.put_line( err_code || ' '||': '||err_msg );
END insert_or_get_unique_number;
you can test the procedure like so.
set serveroutput on ;
DECLARE
INPUT_NUMBER VARCHAR2(200);
C_OUT sys_refcursor;
BEGIN
INPUT_NUMBER := '3';
INSERT_OR_GET_UNIQUE_NUMBER(
INPUT_NUMBER => INPUT_NUMBER,
C_OUT => C_OUT
);
DBMS_SQL.return_result(C_OUT);
END;

Copy data for another table using Collections Types index-by table (associated with a table)

I don't know how to create a function with collections type. I am familiar with SQL, but I do not know that particular function. Here is what I tried:
Create or Replace Function COPY_EMPLOYEES_WITH_RT(
Begin
insert into jjj_employees ( select * from employees)
I want to create a function COPY EMPLOYEES_WITH_RT and copy data from the table EMPLOYEES to jjj_EMPLOYEES using Collections Types index-by table (associated with a table).
CREATE OR REPLACE FUNCTION COPY_EMPLOYEES_WITH_RT(O_ERROR_MESSAGE OUT VARCHAR)
--DECLARE type
TYPE EMP_RECORD IS TABLE OF employees%ROWTYPE;
l_emp EMP_RECORD;
-- define cursor
CURSOR c_employees IS
SELECT *
FROM employees;
--
BEGIN
--
open c_employees;
loop
fetch c_employees
bulk collect into l_emp limit 1000;
exit when l_emp.count = 0;
-- Process contents of collection here.
Insert into jrf_employees values l_emp;
END LOOP;
CLOSE c_employees;
EXCEPTION
--
WHEN OTHERS THEN
O_error_message := SQLERRM;
END;
/
it's like something like that, i just didn't know, what is wrong
If you are using object-relational tables and particularly want to use PL/SQL associative arrays (index-by table types) then you can create your tables as:
CREATE TYPE employee_t IS OBJECT(
id NUMBER(10,0),
first_name VARCHAR2(20),
last_name VARCHAR2(20)
);
CREATE TABLE employees OF employee_t (
id PRIMARY KEY
);
CREATE TABLE jjj_employees OF employee_t (
id PRIMARY KEY
);
With the sample data:
INSERT INTO employees
SELECT 1, 'One', 'Uno' FROM DUAL UNION ALL
SELECT 2, 'Two', 'Dos' FROM DUAL UNION ALL
SELECT 3, 'Three', 'Tres' FROM DUAL;
And the function as:
CREATE FUNCTION COPY_EMPLOYEES_WITH_RT
RETURN NUMBER
IS
TYPE employee_a IS TABLE OF employee_t INDEX BY PLS_INTEGER;
emps employee_a;
i PLS_INTEGER;
BEGIN
FOR r IN ( SELECT VALUE(e) AS employee FROM employees e )
LOOP
emps( r.employee.id ) := r.employee;
END LOOP;
i := emps.FIRST;
WHILE i IS NOT NULL LOOP
INSERT INTO jjj_employees VALUES ( emps(i) );
i := emps.NEXT(i);
END LOOP;
RETURN 1;
END;
/
Then you can run the function using:
BEGIN
DBMS_OUTPUT.PUT_LINE( COPY_EMPLOYEES_WITH_RT() );
END;
/
And the table is copied as:
SELECT * FROM jjj_employees;
Outputs:
ID | FIRST_NAME | LAST_NAME
-: | :--------- | :--------
1 | One | Uno
2 | Two | Dos
3 | Three | Tres
db<>fiddle here
Update
Since you appear to want to use a collection and not an associative array:
CREATE OR REPLACE FUNCTION COPY_EMPLOYEES_WITH_RT
RETURN VARCHAR2
IS
--DECLARE type
TYPE EMP_RECORD IS TABLE OF employees%ROWTYPE;
l_emp EMP_RECORD;
-- DECLARE cursor
CURSOR c_employees IS
SELECT *
FROM employees;
BEGIN
OPEN c_employees;
LOOP
FETCH c_employees
BULK COLLECT INTO l_emp LIMIT 1000;
EXIT WHEN l_emp.COUNT = 0;
-- Process contents of collection here.
FORALL i IN 1 .. l_emp.COUNT
INSERT INTO jjj_employees VALUES l_emp(i);
END LOOP;
CLOSE c_employees;
RETURN NULL;
EXCEPTION
WHEN OTHERS THEN
RETURN SQLERRM;
END;
/
db<>fiddle

Informix SELECT INTO syntax error

I've been trying to use code which finds the count of elements in a table and stores it in a local variable. I basically just want to check the existence of a record, so if there is any easier way to do this.
Here is an example I found of storing the result of a query in a variable (link):
CREATE FUNCTION checklist( d SMALLINT )
RETURNING VARCHAR(30), VARCHAR(12), INTEGER;
DEFINE name VARCHAR(30);
DEFINE dept VARCHAR(12);
DEFINE num INTEGER;
SELECT mgr_name, department,
CARDINALITY(direct_reports)
FROM manager INTO name, dept, num
WHERE dept_no = d;
IF num > 20 THEN
EXECUTE FUNCTION add_mgr(dept);
ELIF num = 0 THEN
EXECUTE FUNCTION del_mgr(dept);
ELSE
RETURN name, dept, num;
END IF;
END FUNCTION;
But when I try to create my own version of this, I get a syntax error. I have no idea what the problem is.
CREATE FUNCTION test ()
RETURNING INTEGER AS num1;
DEFINE l_count INTEGER;
CREATE TEMP TABLE t_queued_calls (
session_id DEC(18,0) PRIMARY KEY,
calling_number NVARCHAR(50)
) WITH NO LOG;
INSERT INTO t_queued_calls VALUES (123456, '5555555555');
SELECT COUNT(*) FROM t_queued_calls INTO l_count WHERE session_id = 123456;
DROP TABLE t_queued_calls;
END FUNCTION;
The position of the INTO clause is wrong in both functions. The INTO clause goes after the select-list (the list of expressions after the keyword SELECT) and before the FROM clause (see the Informix "Guide to SQL: Syntax" manual on the SELECT statement), as in this code:
CREATE PROCEDURE test()
RETURNING INTEGER AS num1;
DEFINE l_count INTEGER;
CREATE TEMP TABLE t_queued_calls (
session_id DEC(18,0) PRIMARY KEY,
calling_number NVARCHAR(50)
) WITH NO LOG;
INSERT INTO t_queued_calls VALUES (123456, '5555555555');
SELECT COUNT(*) INTO l_count FROM t_queued_calls WHERE session_id = 123456;
DROP TABLE t_queued_calls;
RETURN l_count;
END PROCEDURE;
Also, the first function as shown in the question has the same problem with the ordering of the clauses. Also, it does not always RETURN a value, and the original version of the second function never returns a value (although it says it will).
The could be related to the fact the insert dont have the columns name
adapt your_column1, your_column2 to your table schema
INSERT INTO t_queued_calls (your_column1, your_column2) VALUES (123456, '5555555555');
SELECT COUNT(*) FROM t_queued_calls INTO l_count WHERE session_id = 123456;
And/Or the number of column from the select don't match the number and type in insertt ... you select un field only but insert two field
and select into is strange select format ...normally is insert into but select don't use into clause
I found the correct syntax from this question: [Use MERGE statement in a stored procedure with Informix
SELECT COUNT(*) INTO l_count FROM t_queued_calls WHERE session_id = 123456;

SQL Using an OBJECT as parameter for an ORDER mthod

I have a TYPE called Employee which has an ORDER method which takes an Employee as a parameter. I have created a table of Employee and inserted some data. I am trying to test this ORDER method using SELECT however i am having difficulty satisfying the parameter. here is my code
CREATE OR REPLACE TYPE Employee AS OBJECT(
EmpID VARCHAR(15),
eName VARCHAR(30),
ePhone NUMBER,
eAddress VARCHAR(15),
ePosition VARCHAR(15),
eHireDt DATE,
salary NUMBER,
ORDER MEMBER FUNCTION orderSalary (e Employee) RETURN NUMBER)
NOT FINAL;
/
CREATE OR REPLACE TYPE BODY Employee AS
ORDER MEMBER FUNCTION orderSalary(e Employee) return number IS
BEGIN
IF(self.salary > e.salary) then
return(1);
ELSIF (self.salary < e.salary) then
return(-1);
ELSE
return(0);
END IF;
END;
END;
/
CREATE TABLE Emp OF Employee (EmpID PRIMARY KEY)
OBJECT IDENTIFIER PRIMARY KEY;
insert into Emp values('001','kabir',6477732272,'Pharmacy','clerk','2016-03-28',2000);
Here is the line that is giving me trouble:
SELECT p.orderSalary(Employee s) FROM Emp p;
All i need to do is test this method to make sure it works, is there any way i can create an instance of Employee to use as a parameter, or perhaps take a row from the Emp table? Thanks!
SELECT p.orderSalary( VALUE( p ) ) FROM Emp p;
Or:
SELECT p.orderSalary( DEREF( REF( p ) ) ) FROM Emp p;
Or:
SET SERVEROUTPUT ON;
DECLARE
e1 EMPLOYEE := NEW EMPLOYEE('001','kabir',6477732272,'Pharmacy','clerk',DATE '2016-03-28',2000);
e2 EMPLOYEE := NEW EMPLOYEE('002','bob',1234567890,'Library','assistant',DATE '2016-03-07',1000);
BEGIN
DBMS_OUTPUT.PUT_LINE( e1.orderSalary( e2 ) );
END;
/
Update - Select different rows:
insert into Emp values('001','kabir',6477732272,'Pharmacy','clerk','2016-03-28',2000);
insert into Emp values('002','bob',1234567890,'Library','assistant',DATE '2016-03-07',1000);
SELECT e1.ename AS name1,
e2.ename AS name2,
e1.orderSalary( VALUE( e2 ) )
FROM Emp e1
CROSS JOIN Emp e2;
Outputs:
NAME1 NAME2 E1.ORDERSALARY(VALUE(E2))
------ ------ -------------------------
kabir kabir 0
kabir bob 1
bob kabir -1
bob bob 0