Well I have a procedure were I add two the salary of two employees. I get the salary with the id of the employee. If the employee was not found I should set the salary to 0. This is my code
PROCEDURE private_salaries(
sal_res OUT NUMBER,
emp_1 NUMBER,
emp_2 NUMBER)
IS
sal_1 NUMBER;
sal_2 NUMBER;
BEGIN
SELECT salary INTO sal_1 FROM employees WHERE employee_id = emp_1;
SELECT salary INTO sal_2 FROM employees WHERE employee_id = emp_2;
END;
The procedure can be simplified as below,
create or replace PROCEDURE private_salaries(
emp_1 NUMBER,
emp_2 NUMBER,
sal_res out NUMBER)
is
begin
select sal1 + sal2 into sal_res
from
(
select
nvl((SELECT salary FROM employees WHERE employee_id = emp_1),0) sal1,
nvl((SELECT salary FROM employees WHERE employee_id = emp_2),0) sal2
from dual);
end;
Test Cases :
When both of them exists - Returns sum of the salary
When either one doesn't exists - Returns salary of one that exists
When none of them exists - Returns zero.
Recommendation - Use function instead of procedure and you may use them in SQL,for procedure a PL/SQL block is required though.
Related
Here is my procedure where i want to insert values with the conditions like emp id start with sequence 131, phone number must be 13 digits, hire date not greater than sys date and salary not greater than 50,000. while executing im facing error . how to solve this.
create or replace procedure empinsert AS
cursor employee is select length(PHONE_NUMBER),HIRE_DATE,SAL from emp;
e_mobile_no emp.phone_number%type;
e_hire_date emp.hire_date%type;
e_salary emp.sal%type;
Begin
open employee ;
loop
fetch employee into e_mobile_no,e_hire_date,e_salary;
if ( e_mobile_no = 13 and e_hire_date < sysdate and e_salary <=50000 ) then
INSERT INTO EMP (EMP_ID,
EMP_NAME,
EMAIL,
PHONE_NUMBER,
HIRE_DATE,
JOB_ID,
SAL)
values(empinc.nextval, 'ramji','ramji#gmail.com','8975432109875','10/07/2021','JUN_TECH',40000);
else
exit;
end if;
end loop;
end;
/
I am trying to assign an input variable into pl/sql.
I want to query the database by letting the user input department_id and outputs the first name, last name, and salary. The salary is second highest salary in the table.
declare
v_dept_id int;
Begin
Select emp1.first_name, emp1.last_name, emp1.salary, emp1.department_id
From employees emp1
Where (1) =
(select count(distinct(emp1.salary))
From employees emp2
Where emp2.salary > emp1.salary) ;
End;
clarification
EDIT: I edited the code to include V_dept_id however it doesnt run
declare
v_dept_id int;
Begin
Select emp1.first_name, emp1.last_name, emp1.salary, emp1.department_id
into v_dept_id
From employees emp1
Where ((1) =
(select count(distinct(emp1.salary))
From employees emp2
Where emp2.salary > emp1.salary)) and v_dept_id = '&Enter_dept_id' ;
End;
error:
Error starting at line : 4 in command -
declare
v_dept_id int;
Begin
Select emp1.first_name, emp1.last_name, emp1.salary, emp1.department_id
into v_dept_id
From employees emp1
Where ((1) =
(select count(distinct(emp1.salary))
From employees emp2
Where emp2.salary > emp1.salary)) and v_dept_id = '&Enter_dept_id' ;
End;
Error report -
ORA-06550: line 6, column 16:
PL/SQL: ORA-00947: not enough values
ORA-06550: line 5, column 1:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Edit2 based on answer below feedback with the error
I don't know how to let the enter a number into the prompt correctly
declare
v_dept_id number;
v_fname varchar(50);
v_lname varchar(50);
v_salary NUMBER(8,2);
Begin
Select emp1.department_id, emp1.first_name, emp1.last_name, emp1.salary
into v_dept_id, v_fname, v_lname, v_salary
From employees emp1
Where ((1) =
(select count(distinct(emp1.salary))
From employees emp2
Where emp2.salary > emp1.salary)) and v_dept_id = '&Enter_dept_id' ;
End;
Error starting at line : 1 in command -
declare
v_dept_id number;
v_fname varchar(50);
v_lname varchar(50);
v_salary NUMBER(8,2);
Begin
Select emp1.department_id, emp1.first_name, emp1.last_name, emp1.salary
into v_dept_id, v_fname, v_lname, v_salary
From employees emp1
Where ((1) =
(select count(distinct(emp1.salary))
From employees emp2
Where emp2.salary > emp1.salary)) and v_dept_id = '&Enter_dept_id' ;
End;
Error report -
ORA-01403: no data found
ORA-06512: at line 8
01403. 00000 - "no data found"
*Cause: No data was found from the objects.
*Action: There was no data from the objects which may be due to end of fetch.
Raj_Te has explained why your code will not compile. But I don't much like your method of retrieving the second highest salary. I guess your method works but it's not clear what that section of code is trying to achieve.
In the following code the DENSE_RANK function will return the salary ranking of each row. Any rows that have the same salary will get the same rank :
SELECT
emp.first_name
,emp.last_name
,emp.salary
,emp.department_id
FROM
(SELECT
emp1.first_name
,emp1.last_name
,emp1.salary
,emp1.department_id
,DENSE_RANK() OVER (PARTITION BY emp1.department_id ORDER BY emp1.salary DESC) dr
FROM
employees emp1
WHERE emp1.department_id = &dept_id
) emp
WHERE 1=1
AND emp.dr = 2
;
By stating emp.dr = 2 we are saying we want all rows where the salary is ranked second highest.
You are selecting 4 fields in a single variable. Thats why its givng not enough values. You must decalre either 4 variables or create a records with 4 columns and then capture the values.
Select emp1.first_name, emp1.last_name, emp1.salary, emp1.department_id
into v_dept_id <--- trying to fetch 4 columns values to a single varibale.
You can decalre new variables of same datatype as of your selection and do it:
Select emp1.first_name, emp1.last_name, emp1.salary, emp1.department_id
into v_emp_first_nm,v_emp_last_nm ,v_emp_sal,v_dept_id
I'm trying to create a PL/SQL procedure where by I delete records that are grouped and selected by cursor but I only want one record remaining. I want to delete first by Xcomment, if there are multiple entries with id_number, activity_code, start_dt, activity_participation_code exist, then delete all but ONE entry with blank/null xcomment. If there are multiple entries with blank xcomment, then delete all but one with blank table_nmb. If multiple entries with blank table_nmb then delete highest sequence until only one is left. Essentially, I only want one record per all these fields. I'm having trouble thinking of how to do this so any help would be appreciated.
Here is my code so far:
Create Or Replace Function Y_Cleanup_Cursor
Return Sys_Refcursor
As
My_Cursor Sys_Refcursor;
Begin
Open My_Cursor For
Select Q.Id_Number, Q.Activity_Code, Q.Start_Dt, Q.Activity_Participation_Code, Q.Rec_Count, A.Xcomment, A.Table_Nmb, A.Xsequence
From (Select Id_Number, Activity_Code, Start_Dt, Activity_Participation_Code, Count(0) As Rec_Count
From Activity A
Group By Id_Number, Activity_Code, Start_Dt, Activity_Participation_Code
Having Count(0) > 1) Q,
Activity A
Where
Q.Id_Number = A.Id_Number And
Q.Activity_Code = A.Activity_Code And
Q.Start_Dt = A.Start_Dt And
Q.Activity_Participation_Code = A.Activity_Participation_Code;
Return My_Cursor;
End Y_Cleanup_Cursor;
Create Or Replace Procedure Help_Me_Please(Code In Varchar2)
Is
-- Declare Variables
-- I Stands For Internal Variable
L_Cursor Sys_Refcursor;
I_Id_Number Varchar2(10 Byte);
I_Xsequence Number (6);
I_Activity_Code Varchar2(05 Byte);
I_Start_Dt Varchar2(08 Byte);
I_Activity_Participation_Code Varchar2(02 Byte);
I_Table_Nmb Varchar2(15 Byte);
I_Xcomment Varchar2(255 Byte);
I_Rec_Count Number (6);
L_Counter Integer;
Begin
L_Cursor := Y_Cleanup_Cursor;
Loop
Fetch L_Cursor Into
I_Id_Number, I_Activity_Code, I_Start_Dt, I_Activity_Participation_Code, I_Rec_Count, I_Xcomment, I_Table_Nmb, I_Xsequence;
Select Count (Id_Number)
Into L_Counter
From Activity Where
Id_Number = I_Id_Number
And Activity_Code = I_Activity_Code
And Start_Dt = I_Start_Dt
And Activity_Participation_Code = I_Activity_Participation_Code
And Trim(Xcomment) Is Null;
If L_Counter <> I_Rec_Count Then
Begin
Delete From Activity
Where
Id_Number = I_Id_Number
And Activity_Code = I_Activity_Code
And Start_Dt = I_Start_Dt
And Activity_Participation_Code = I_Activity_Participation_Code
And Trim(Xcomment) Is Null;
end;
End If;
Exit When L_Cursor%Notfound;
End Loop;
Close L_Cursor;
End Help_Me_Please;
From what I gather you want to delete all rows except 1 where there are repeating columns
first make sure to backup your table:
create table [backup_table] as select * from [table];
Try This:
DELETE FROM backup_table
WHERE rowid not in
(SELECT MIN(rowid)
FROM backup_table
GROUP BY [col1], [col2]);
Col1 and col2, etc are the columns that should be identical
I want to create a table NEW_DEPTS. I have DEPARTMENT_ID and DEPARTMENT_NAME which is in DEPARTMENTS table.
desc departments
Name Null? Type
------------------------------- -------- ----
DEPARTMENT_ID NOT NULL NUMBER(4)
DEPARTMENT_NAME NOT NULL VARCHAR2(30)
MANAGER_ID NUMBER(6)
LOCATION_ID NUMBER(4)
My EMPLOYEES table contains:
desc employees
Name Null? Type
------------------------------- -------- ----
EMPLOYEE_ID NOT NULL NUMBER(6)
FIRST_NAME VARCHAR2(20)
LAST_NAME NOT NULL VARCHAR2(25)
EMAIL NOT NULL VARCHAR2(25)
PHONE_NUMBER VARCHAR2(20)
HIRE_DATE NOT NULL DATE
JOB_ID NOT NULL VARCHAR2(10)
SALARY NUMBER(8,2)
COMMISSION_PCT NUMBER(2,2)
MANAGER_ID NUMBER(6)
DEPARTMENT_ID NUMBER(4)
Now, I want to create a table NEW_DEPTS which should contain DEPARTMENT_ID and DEPARTMENT_NAME along with total SALARY that each Department has. Every department have certain number of employees, so, for department number 10, 20, 30, and so on, total Salary should be calculated.
My main query is:
create table new_depts as
select d.department_id,d.department_name,sum(e.salary) dept_sal
from employees e,departments d
where e.department_id = d.department_id;
I got an Error:
select d.department_id,d.department_name,sum(e.salary) dept_sal
*
ERROR at line 2:
ORA-00937: not a single-group group function
So, I googled it. I found that group function like sum, avg, max, min can't be used in select statement. I needed GROUP BY clause. So, I wanted to solve it individually. Therefore, I skipped the "create table..." statement and wrote:
select distinct d.department_id,d.department_name,sum(e.salary) dept_sal
from employees e,departments d
where e.department_id = d.department_id
group by department_id;
I got Error:
group by department_id;
*
ERROR at line 4:
ORA-00911: invalid character
Then I wrote:
select d.department_id,d.department_name,sum(e.salary) dept_sal
from employees e,departments d
where e.department_id = d.department_id
group by d.department_id;
I got Error:
select d.department_id,d.department_name,sum(e.salary) dept_sal
*
ERROR at line 1:
ORA-00979: not a GROUP BY expression
Then I wrote:
select d.department_id,d.department_name,sum(e.salary) dept_sal
from employees e,departments d
where e.department_id = d.department_id
group by department_id;
I got Error:
group by department_id
*
ERROR at line 4:
ORA-00918: column ambiguously defined
I know that its a "join". But not getting how should I place GROUP BY clause so that my GROUP FUNCTION may run correctly.
You need to group by all columns not in an aggregate function and since you have defined a table alias you must use that in your columns. The error ORA-00918: column ambiguously defined is because you have two columns named department_id so you must specify the table the department_id is from:
select d.department_id,
d.department_name,
sum(e.salary) dept_sal
from employees e
inner join departments d
on e.department_id = d.department_id
group by d.department_id, d.department_name;
You will notice that I altered the query to use ANSI JOIN syntax (INNER JOIN).
We have a table which has three columns in it:
Customer_name, Age_range, Number_of_people.
1 1-5 10
1 5-10 15
We need to return all the number of people in different age ranges as rows of a single query. If we search for customer #1, the query should just return one row:
Header- Age Range (1-5) Age Range (5-10)
10 15
We needed to get all the results in a single row; When I query for customer 1, the result should be only number of people in a single row group by age_range.
What would be the best way to approach this?
You need to manually perform a pivot:
SELECT SUM(CASE WHEN age_range = '5-10'
THEN number_of_people
ELSE NULL END) AS nop5,
SUM(CASE WHEN age_range = '10-15'
THEN number_of_people
ELSE NULL END) AS nop10
FROM customers
WHERE customer_name = 1;
There are easy solutions with 10g and 11g using LISTGAGG, COLLECT, or other capabilities added after 9i but I believe that the following will work in 9i.
Source (http://www.williamrobertson.net/documents/one-row.html)
You will just need to replace deptno with customer_name and ename with Number_of_people
SELECT deptno,
LTRIM(SYS_CONNECT_BY_PATH(ename,','))
FROM ( SELECT deptno,
ename,
ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY ename) -1 AS seq
FROM emp )
WHERE connect_by_isleaf = 1
CONNECT BY seq = PRIOR seq +1 AND deptno = PRIOR deptno
START WITH seq = 1;
DEPTNO CONCATENATED
---------- --------------------------------------------------
10 CLARK,KING,MILLER
20 ADAMS,FORD,JONES,SCOTT,SMITH
30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD
3 rows selected.
This will create a stored FUNCTION which means you can access it at any time.
CREATE OR REPLACE FUNCTION number_of_people(p_customer_name VARCHAR2)
RETURN VARCHAR2
IS
v_number_of_people NUMBER;
v_result VARCHAR2(500);
CURSOR c1
IS
SELECT Number_of_people FROM the_table WHERE Customer_name = p_customer_name;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO v_number_of_people;
EXIT WHEN c1%NOTFOUND;
v_result := v_result || v_number_of_people || ' ' || CHR(13);
END;
END;
To run it, use:
SELECT number_of_people(1) INTO dual;
Hope this helps, and please let me know if there are any errors, I didn't testrun the function myself.
Just do
select Number_of_people
from table
where Customer_name = 1
Are we missing some detail?