SQL Median Value Undeclared Variable - mysql

I am trying to create a stored procedure to obtain the median age within a table but am getting an undeclared variable error. My code is:
DELIMITER //
CREATE PROCEDURE MedianAge()
BEGIN
SET #row_count = (SELECT COUNT(*) FROM employee);
SET #median_index = (#row_count/2);
SELECT TIMESTAMPDIFF(YEAR, bdate, CURDATE()) AS age
FROM employee ORDER BY bdate DESC
LIMIT median_index, median_index;
END //
DELIMITER ;
I am receiving the error:
Error Code: 1327. Undeclared variable: median_index
As far as I am aware I have declared the variable correctly and am unsure why the SELECT statement does not work.

You are using user-defined variables (prefixed with #). However, within the stored procedures you should use local variables (with no prefix). These variables have to be declared prior to use. The difference is well explained here.
You would get something like this:
DELIMITER //
CREATE PROCEDURE MedianAge()
BEGIN
DECLARE row_cnt INT unsigned;
DECLARE median_index INT unsigned;
SET row_cnt = (SELECT COUNT(*) FROM employees);
SET median_index = (row_cnt/2);
SELECT TIMESTAMPDIFF(YEAR, bdate, CURDATE()) AS age
FROM employee ORDER BY bdate DESC
LIMIT median_index, median_index;
END //
DELIMITER ;
Also, keep in mind that row_count is a reserved word in MySQL (a function name). It's better to avoid using it as a variable name.

Related

How to return boolean based on number of records in database?

Here's what I've tried. My host is returning an error, "Sorry an unexpected error happened!" .
I want it to return true if there is at least 1 record with combination pdriver_id, ptruck_number, and pdate.
DELIMITER %%
CREATE FUNCTION DriverActiveInTruckByDate(
pdriver_id INT,
ptruck_number INT,
pdate DATETIME
)
RETURNS boolean
DETERMINISTIC
BEGIN
DECLARE inDB INT DEFAULT 0;
SET inDB =
SELECT IF(COUNT(*) >= 1,1,0)
FROM
truck_timeline tl
WHERE 1=1
AND tl.driver_id = pdriver_id
AND tl.truck_number = ptruck_number
AND ((pdate BETWEEN tl.begin_date AND tl.end_date) OR (pdate >= tl.begin_date AND tl.end_date IS NULL))
END
%%
DELIMITER ;
Several fixes are needed:
The function is not DETERMINISTIC. This means the result will always be the same given the same inputs. In your case, the result may be different depending on the data in your truck_timeline table. So I would suggest using READS SQL DATA.
If you use SET variable = SELECT... you must put the SELECT in a subquery:
SET inDB = (SELECT ...);
The current manual recommends using SELECT ... INTO variable instead of SET. See https://dev.mysql.com/doc/refman/8.0/en/select-into.html
The INTO position at the end of the statement is supported as of MySQL 8.0.20, and is the preferred position.
SELECT ... INTO inDB;
The function you show doesn't have a RETURN statement. See https://dev.mysql.com/doc/refman/8.0/en/return.html
There must be at least one RETURN statement in a stored function.
Your Full Code could be like this:
DELIMITER %%
CREATE FUNCTION DriverActiveInTruckByDate(
pdriver_id INT,
ptruck_number INT,
pdate DATETIME
)
RETURNS boolean
DETERMINISTIC
BEGIN
DECLARE inDB INT DEFAULT 0;
SET inDB =
(SELECT IF(COUNT(*) >= 1,1,0)
FROM
truck_timeline tl
WHERE 1=1
AND tl.driver_id = pdriver_id
AND tl.truck_number = ptruck_number
AND ((pdate BETWEEN tl.begin_date AND tl.end_date) OR (pdate >= tl.begin_date AND tl.end_date IS NULL))
);
END %%
DELIMITER ;

"Delimiter" is not valid at this position, expecting CREATE

I was getting an error of "Delimiter" is not valid at this position, expecting CREATE" as I was writing a stored procedure and couldn't figure out the cause. I think it might be an issue with MySQL workbench possibly, because the following code gives the same error but was copied straight off of this website.
DELIMITER $$
CREATE PROCEDURE GetTotalOrder()
BEGIN
DECLARE totalOrder INT DEFAULT 0;
SELECT COUNT(*)
INTO totalOrder
FROM orders;
SELECT totalOrder;
END$$
DELIMITER ;
Edit: My real stored procedure is:
DELIMITER //
CREATE PROCEDURE GetSimilar(inputdate char(10))
BEGIN
Declare id(tinyint) DEFAULT 0;
Set id := (select t.IdTimelineinfo
From timelineinfo t
WHERE t.Date = inputdate);
SELECT t.Date From timelineinfo t where t.date = inputdate;
SELECT o.Name, o.Race, o.Sex, o.IdOfficer
FROM timelineinfo
JOIN timelineinfo_officer ON timelineinfo.IdTimelineinfo = timelineinfo_officer.IdTimelineinfo
JOIN officers o ON timelineinfo_officer.IdOfficer = o.IdOfficer
WHERE timelineinfo.IdTimelineinfo = id
UNION
SELECT s.IdSubject, s.Name, s.Race, s.Sex
FROM timelineinfo
JOIN timelineinfo_subject ON timelineinfo.IdTimelineinfo = timelineinfo_subject.IdTimelineinfo
JOIN subjects s ON timelineinfo_subject.IdSubject = s.IdSubject
WHERE timelineinfo.IdTimelineinfo = id;
UNION
Select *
From media m
Where (m.IdTimelineinfo = id);
END //
DELIMITER ;
Watch out where you edit the procedure SQL code. There's a dedicated routine object editor (just like there are for tables, triggers, views etc.), which only accept SQL code for their associated object type. Hence they don't need a delimiter and even signal an error if you use one.
On the other hand you can always directly edit SQL code in the SQL IDE code editors, where no such special handling is implemented. In this case you need the delimiter.

I Getting error when run this below query : RETURN statements in scalar valued functions must include an argument

I am getting this error message
RETURN statements in scalar valued functions must include an argument
when run this query:
create function gender(#gender nvarchar(40))
returns nvarchar(40)
as
begin
(select name,cast(DOB as date) as DOB from datenames where gender = #gender)
return
end
The write way to create a function in mysql for your example is as follows:
DELIMITER \\
create function gender(Igender nvarchar(40))
returns nvarchar(40)
begin
DECLARE customerLevel NVARCHAR(40);
IF EXISTS (select name,cast(DOB as date) as DOB from datenames where gender = Igender) THEN
SET customerLevel = 'SOMETHING1';
ELSE
SET customerLevel = 'SOMETHING2';
END IF;
RETURN (customerLevel);
end
No need to as
No need to # before input
You need to return something.
Don't forget to use DELIMITER.
If you use phpmyadmin and has problem with nvarchar read this post: Unrecognize data type nvarchar in database or simply change it to to varchar.

GROUP_CONCAT as input of MySQL function

Is it possible to use a GROUP_CONCAT in a SELECT as the input of a MySQL function? I cannot figure out how to cast the variable it seems. I've tried blob. I've tried text (then using another function to break it up into a result set, here) but I haven't had any success.
I want to use it like this:
SELECT
newCustomerCount(GROUP_CONCAT(DISTINCT items.invoicenumber)) AS new_customers
FROM items;
Here is the function:
DROP FUNCTION IF EXISTS newCustomerCount;
DELIMITER $$
CREATE FUNCTION newCustomerCount(invoicenumbers BLOB)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE new_customers INT;
SET new_customers = 0;
SELECT
SUM(nc.record) INTO new_customers
FROM (
SELECT
1 AS customer,
(SELECT COUNT(*) FROM person_to_invoice ps2 WHERE person_id = ps1.person_id AND invoice < ps1.invoice) AS previous_invoices
FROM person_to_invoice ps1
WHERE invoice IN(invoicenumbers)
HAVING previous_invoices = 0
) nc;
RETURN new_customers;
END$$
DELIMITER ;
Because Mysql functions do not support dynamic queries, I recommend you re-think your basic strategy to pass in a list of invoice numbers to your function. Instead, you could modify your function to accept a single invoice number and return the number of new customers just for the one invoice number.
Also, there are some optimizations you can make in your query for finding the number of new customers.
DROP FUNCTION IF EXISTS newCustomerCount;
DELIMITER $$
CREATE FUNCTION newCustomerCount(p_invoice INT)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE new_customers INT;
SET new_customers = 0;
SELECT
COUNT(DISTINCT ps1.person_id) INTO new_customers
FROM
person_to_invoice ps1
WHERE
ps1.invoice = p_invoice
AND NOT EXISTS (
SELECT 1
FROM person_to_invoice ps2
WHERE ps1.person_id = ps2.person_id
AND ps2.invoice < ps1.invoice
);
RETURN new_customers;
END$$
DELIMITER ;
Then you can still get the total number of new customers for a given list of invoice numbers like this:
SELECT
SUM(newCustomerCount(invoice)) as total_new_customers
FROM items
WHERE ...
You could try FIND_IN_SET() instead of IN(). The performance will probably be horrible when passing in a long list of invoice numbers. But it should work.
WHERE FIND_IN_SET(invoice, invoicenumbers)
You are looking in the wrong place.
WHERE invoice IN(invoicenumbers) will not do the desired substitution. Instead you need to use CONCAT to construct the SQL, then prepare and execute it.

Trying to get Count single value from mysql stored procedure

I am trying to do a count in a mysql stored procedure but cant get the syntax right help1
delimiter//
create procedure get_count_workmen_type(
IN employee_payroll int,
OUT mycount int
)
begin
SELECT count(*) into mycount from workman
where employee_payroll = employee_payroll
end //
delimiter;
You should prefix your parameter names (personally I use "p_") to differentiate them from column names, etc. For example where employee_payroll = employee_payroll will always be true because it's comparing the column to itself.
Also you should add a semi-colon to the end of your select statement.
Putting those two changes together gives you something like this:
delimiter //
create procedure get_count_workmen_type(
IN p_employee_payroll int,
OUT p_mycount int
)
begin
SELECT count(*)
into p_mycount
from workman
where employee_payroll = p_employee_payroll;
end //
delimiter ;