store procedure not giving correct values - mysql

I am trying to find the number of employees with a specific salary range using store procedure, I am getting results but they are not equal to the results of the simple query (second query below) which means that the store procedure is not working properly?
I was hoping if someone can help me with this.
-- ANSWER : NULL /there is no salary amount less than 38000, however with the store procedure I am getting 967330
Secondly, is there any other way to shorten the case when statements within the store procedure ? in the current version I have to add subquery each time.
database name: employees
column name: salary
employee_id = emp_no
DELIMITER $$
CREATE PROCEDURE SALARY_RANGE
(INOUT no_employees INT, IN salary INT)
BEGIN
CASE
WHEN (salary <= 38000) THEN (SELECT COUNT(emp_no) INTO no_employees
FROM salaries
WHERE salary <= 38000);
WHEN (salary<= 68000) THEN (SELECT COUNT(emp_no) INTO no_employees
FROM salaries
WHERE salary <= 68000);
WHEN (salary <= 88000) THEN (SELECT COUNT(emp_no) INTO no_employees
FROM salaries
WHERE salary <= 88000);
WHEN (salary <= 100000) THEN (SELECT COUNT(emp_no) INTO no_employees
FROM salaries
WHERE salary <= 100000);
ELSE (SELECT COUNT(emp_no) INTO no_employees
FROM salaries WHERE salary > 100000);
END CASE;
END$$
CALL SALARY_RANGE(#finding_salary_range_count,'38000')$$
select #finding_salary_range_count;
-- to check the if the answer matched the above store procedure
select count(case when salary <= 88000 then emp_no else null end) as count_sal
from salaries;

I would expect code like this:
CREATE PROCEDURE SALARY_RANGE (
OUT out_num_employees INT,
IN in_salary INT
)
BEGIN
SELECT COUNT(emp_no) INTO out_num_employees
FROM salaries
WHERE salary <= in_salary or in_salary IS NULL
END;
I don't see a need for multiple SELECT statements.

Related

Get employees who received a raise in 2 consecutive years

I am trying to Get employees who received a raise in 2 consecutive years, in this case employee 1000 is the right answer.
here is the data and the sql i have tried.
EID
SALARY
YEAR
1000
10,000
2016
1000
7,500
2015
1000
6,000
2014
1001
8,000
2016
1001
7,500
2015
1002
7,500
2016
1002
7,500
2015
1002
5,000
2014
1003
6,000
2016
1003
7,000
2015
1003
5,000
2014
i have used following code however it gets only row number by EID and not calcualtion of last year and present year, i need to find employee who got raise in 2 consecutive years.
output
select * ,
row_number() over(partition by eid order by salary and year desc)as rn
from gs;
You can do it using the LEAD window function that compute the two consecutive previous value of the salary. Then you can check how many employees have at least one row with salary1 < salary2 < salary3.
SELECT DISTINCT
eid
FROM (
SELECT
eid,
year,
salary,
(LEAD(salary, 1) OVER(PARTITION BY eid ORDER BY year DESC)) AS prev_salary1,
(LEAD(salary, 2) OVER(PARTITION BY eid ORDER BY year DESC)) AS prev_salary2
FROM
employees
) consecutive3
WHERE
salary > prev_salary1
AND
prev_salary1 > prev_salary2
The assumption is that there are no missing years for which a salary of a dependent was not recorded.
Here's the fiddle: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=8c0d8a1deec8e77bb32a173656c3e386.
EDIT: Detailed explanation
Let's do the example of Jennifer, who has worked for five years and got these salaries:
2018 -> 65000
2017 -> 55000
2016 -> 50000
She's a candidate for being selected as her salary was raised three times consecutively.
1. LEAD(salary, 1) OVER(PARTITION BY eid ORDER BY year DESC)
Allows you to get the salary for year "Y" and the salary for year "Y-1":
("year" -> "salary", "previous_salary")
2018 -> 65000 , 55000
2017 -> 55000 , 50000
2016 -> 50000 , NULL
2. LEAD(salary, 2) OVER(PARTITION BY eid ORDER BY year DESC)
Allows you to get the salary for year "Y" and the salary for year "Y-1":
("year" -> "salary", "previous_salary", "previous_salary_by_2_years")
2018 -> 65000 , 55000 , 50000
2017 -> 55000 , 50000 , NULL
2016 -> 50000 , NULL , NULL
3. WHERE salary > prev_salary1 AND prev_salary1 > prev_salary2
Some filtering on the employees who
have their year3 salary higher than their year2 salary (salary > prev_salary1)
have their year2 salary higher than their year1 salary (prev_salary1 > prev_salary2)
I know that this has already been answered but here is my take using the lag function to determine if there was an increase from the previous year and ran that twice.
SELECT *
FROM (
SELECT
t2.*,
LAG(increase) over (partition by eid order by year) AS increaseNextYear
FROM (
SELECT
t1.*,
COALESCE(salary - LAG(salary) over (partition by eid order by year), 0) > 0 AS increase
FROM tbl_test t1
) as t2
) t3 where increase AND increaseNextYear
with
dates as
(
select
a.*,
dense_rank() OVER (
partition by eid
order by year desc, salary
)
as rn,
case
when
lead(salary,2)over(partition by eid order by year, salary)
>
lead(salary,1)over(partition by eid order by year, salary)
and
lead(salary,1)over(partition by eid order by year, salary)
>
salary
then
1
else
0
end
as flag
from
employees a
)
select
eid
from
dates
where
rn = 3
and flag = 1
Not a beautiful query, but straight-forward: find employees who had a salary in a year where the salary in the previous year was lower and the salary in the year before that even lower. Using LAG is more elegant, but I thought I'd throw this in, just to show an alternative.
select *
from employee
where exists
(
select null
from gs
where gs.eid = employee.id
and exists
(
select null
from gs prev
where prev.eid = gs.eid
and prev.year = gs.year - 1
and prev.salary < gs.salary
and exists
(
select null
from gs prevprev
where prevprev.eid = prev.eid
and prevprev.year = prev.year - 1
and prevprev.salary < prev.salary
)
)
);
Same thing with a join:
select *
from employee
where exists
(
select null
from gs
join gs prev on prev.eid = gs.eid
and prev.year = gs.year - 1
and prev.salary < gs.salary
join gs prevprev on prevprev.eid = prev.eid
and prevprev.year = prev.year - 1
and prevprev.salary < prev.salary
where gs.eid = employee.id
);
For versions prior to 8.0 (mine is 5.7) which lack the cutting edge features of the newer one, I tried a procedure to accomplish the job. First and foremost, get all the eid which have no less than three years' salary record, which is the minimum requirement of the consecutive bonus. The rest is to fetch and compare using a cursor from the eid pool. The result is stored in a temporary table t .
delimiter //
drop procedure if exists lucky_emp//
create procedure lucky_emp()
begin
declare old_eid int default 0;
declare old_salary int;
declare new_eid int ;
declare new_salary int;
declare bonus_year int;
declare fin bool default false;
declare c cursor for select eid,salary from salary where eid in(select eid from salary group by eid having count(eid)>=3) order by eid,year;
declare continue handler for not found set fin=true;
drop temporary table if exists t ;
create temporary table t (t_eid int);
open c;
lp:loop
fetch c into new_eid ,new_salary;
if fin=true then
leave lp;
end if;
if new_eid !=old_eid then
set old_eid=new_eid,old_salary=0,bonus_year=0;
end if;
if new_salary> old_salary then
set bonus_year=bonus_year+1,old_salary=new_salary;
else
set bonus_year=0;
end if;
if bonus_year=3 and new_eid not in(select t_eid from t) then
insert t values(new_eid);
end if;
end loop lp;
end//
delimiter ;
select * from t ;
Select a.*, b.prev_sal1, b.prev_sal2
from employees a
join (
Select eid ,year,
lag(salary,1) over (partition by eid order by year) as prev_sal1,
lag(salary,2) over (partition by eid order by year) as prev_sal2
from employees ) b
on a.eid=b.eid
and a.year = b.year
where salary>prev_sal1 and prev_sal1>prev_sal2
fiddle: https://dbfiddle.uk/rfGv31zM

Fetch all employees if not employee selected from UI else select specific employee data

I want to select all employees if no employee is selected else need data for selected employee.
I have written this query but it is throwing error.
DECLARE #emp_id INT
SET #emp_id=0 -- This will change based on the selected employee from UI
SELECT * FROM employees
WHERE employee_id in (CASE WHEN #emp_id=0 THEN (SELECT employee_id FROM employees) ELSE #emp_id END)
Sample Script for reproducing issue
CREATE TABLE employees (
employee_id INT ,
first_name VARCHAR (20) DEFAULT NULL,
last_name VARCHAR (25) NOT NULL
);
INSERT INTO employees values (1, '1_fname', '1_lname'),(2, '2_fname', '2_lname'),(3, '3_fname', '3_lname'),(4, '4_fname', '4_lname'),(5, '5_fname', '5_lname')
I want to this in single query.
Assuming that you're using prepared statements, you will need to provide the selected employee 2 times - as argument 1 and 2:
SELECT * FROM employees
WHERE CASE WHEN COALESCE(?,0) = 0 THEN TRUE ELSE id = ? END
For single query, we can INNER JOIN with same table to emulate whether employees should shown or not. Using this query will produce desired output.
select e.*
from employees e
inner join (
select employee_id,
case
when #emp_id = 0 then true -- will display all employees
when #emp_id = #emp_id then true -- only display selected employee
else false
end as is_shown
from employees
) filter on e.employee_id = filter.employee_id and filter.is_shown = 1;
Or using another approach
select employee_id, first_name, last_name
from (
select e.*,
case
when #emp_id = 0 then true
when #emp_id = employee_id then true
else false
end as is_shown
from employees e
) employees
where is_shown = 1;
Simple flow control inside stored procedure
delimiter $$
use `precise`$$
drop procedure if exists `GetEmployee`$$
create definer=`user`#`%` procedure `GetEmployee`(empID int)
begin
case when empID = 0 then
select *
from employees;
else
select *
from employees
where employee_id = empID;
end case
;
end$$
delimiter ;

MySQL function with limit

I am trying to write a MySQL function with input variable like this
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
RETURN (
# Write your MySQL query statement below.
SELECT Salary FROM (SELECT * FROM Employee
ORDER BY Salary DESC LIMIT N-1, 1) AS tmp
);
END
And it reports error. However, the following code works
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
RETURN (
# Write your MySQL query statement below.
SELECT Salary FROM (SELECT * FROM Employee
ORDER BY Salary DESC LIMIT N, 1) AS tmp
);
END
Is there any way to achieve the goal that is to select the Nth highest salary with similar code? Thank you. (I am only allowed to change the content in the 'return' parentheses).
You can write something like this.
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
RETURN (
SELECT Salary FROM Employee e1
WHERE N-1 = (SELECT COUNT(DISTINCT salary) FROM Employee e2
WHERE e2.salary > e1.salary)
);
END

Issues creating top N query for grouped datsets

I am working on a database query and I am facing issues creating the right query to get required output.
Below is my create table query and insert statements.
create table DETAILS (dep_name varchar(50), emp_name varchar(50), salary int);
insert into DETAILS values ('marketing', 'ravi', 50000);
insert into DETAILS values ('marketing', 'suresh', 25000);
insert into DETAILS values ('marketing', 'mahesh', 60000);
insert into DETAILS values ('legal', 'raja', 40000);
insert into DETAILS values ('legal', 'gavi', 50000);
insert into DETAILS values ('legal', 'rana', 90000);
insert into DETAILS values ('legal', 'jyoti', 60000);
My requirement is to display only top 2 salaried employees from each department. Please see sample output below.
dep_name emp_name salary
legal rana 90000
legal jyoti 60000
marketing mahesh 60000
marketing ravi 50000
I have tried several queries but not able to crack this. Can somebody get me to frame the right query?
If you are using Oracle then it can be done by dense_rank() over (partition by .. order by ..)
If your RDBMS is mysql you cannot use dense_rank() you have to emulate the concept. See this link.
Try:
select
dep_name,
emp_name,
salary
from (
SELECT
#row_number:=CASE
WHEN #dep_name = dep_name THEN #row_number + 1
ELSE 1
END AS num,
#dep_name := dep_name as dep_name,
emp_name,
salary
FROM DETAILS ,(SELECT #dep_name := '', #row_number := 0) as t
ORDER BY dep_name, salary desc
) x
where num < 3
ORDER BY dep_name, salary desc;
sql fiddle demo
This will be one option to do it in MySQL (not tested)
SELECT dep_name, emp_name, salary
FROM (
SELECT
#nr := IF(#old_val = c.col,(#nr +1),1) AS nr,
#old_val:=c.col AS tmp,
c.*
FROM details AS c , (SELECT #nr:=0, #old_val:='') AS TMP
ORDER BY salary DESC
) AS result WHERE nr < 3

Procedure Limit

I need help please! I tried searching for answers but didn't find anything.
I am using MYSQL6.
What I did is create 2 views and I want to limit these views to get a specific amount of results that is calculated through a function.
FUNCTION:
DELIMITER $$
CREATE FUNCTION `get_total_customers`() RETURNS int(11)
BEGIN
DECLARE TotalCustomers INT;
SELECT (count(customer_id)*0.2) INTO TotalCustomers FROM customer;
RETURN TotalCustomers;
END
VIEW1:
CREATE VIEW PreferredStatus AS
SELECT customer_id, SUM(amount) AS TotalAmount, get_total_customers() AS Total FROM payment
GROUP BY customer_id
ORDER BY TotalAmount DESC;
VIEW2:
CREATE VIEW RegularStatus AS
SELECT customer_id, SUM(amount) AS TotalAmount, get_total_customers() AS Total FROM payment
GROUP BY customer_id
ORDER BY TotalAmount ASC
LIMIT Total;
This is the procedure that would use the view.
> DELIMITER $$
CREATE PROCEDURE `CustomerStatus`(in custID int, out status varchar(200))
BEGIN
declare number1 int;
declare totalvar int;
declare limit2 int;
IF custID in (SELECT customer_id FROM PreferredStatus) THEN
set status = "preferred";
ELSEIF custID in (SELECT customer_id FROM RegularStatus) THEN
set status = "casual";
ELSE
set status = "regular";
End If;
END
HELP PLEASE!
-Thanks.
You can add LIMIT to the SELECT that creates the VIEW:
CREATE VIEW PreferredStatus AS
SELECT customer_id,
SUM(amount) AS TotalAmount,
get_total_customers() AS Total
FROM payment
GROUP BY customer_id
ORDER BY TotalAmount DESC
LIMIT 20;
sqlfiddle demo
A warning from the docs:
If a view definition includes a LIMIT clause, and you select from the
view using a statement that has its own LIMIT clause, it is undefined
which limit applies.