Parameterize mySQL Stored Procedure - mysql

I have the following mySQL (v5.7) Stored Procedure which works great (it calculates the average of the most recent 25 rows):
CREATE DEFINER=`SqueezeOJ`#`%` PROCEDURE `NVG_SMA25`(
IN myDate DATE,
OUT mySMA25 DECIMAL(8,2)
)
BEGIN
SELECT ROUND(AVG(ClosingPrice), 2) INTO mySMA25 FROM NVG
WHERE TradeDate IN (SELECT * FROM (SELECT DISTINCT TradeDate
FROM NVG
WHERE TradeDate <= myDate
ORDER BY TradeDate DESC LIMIT 25) as calculating);
END
I'd like to parameterize the LIMIT 25 to LIMIT X - where I can pass X into the procedure, but everything I try leads to errors.
Can someone point me in the right direction?
Thanks, Jason

13.2.9 SELECT Statement
...
The LIMIT clause can be used to constrain the number of rows returned
by the SELECT statement. LIMIT takes one or two numeric arguments,
which must both be nonnegative integer constants, with these
exceptions:
Within prepared statements, LIMIT parameters can be specified using ? placeholder markers.
Within stored programs, LIMIT parameters can be specified using integer-valued routine parameters or local variables.
...
See dbfiddle.

Related

Limitations to the RETURN block in a MySQL function

I'm working on a LeetCode problem that asks the user to return the Nth highest salary from a table called Employee with columns Id and Salary.
The code that the user is given to start with is
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
RETURN (
# Write your MySQL query statement below.
);
END
The temptation is to put something like
SELECT e.salary as getNthHighestSalary
FROM employee e
ORDER BY e.salary DESC
LIMIT 1 OFFSET (N-1)
But evidently that doesn't work. Successful queries define another variable M=N-1 outside the return block, and then use OFFSET M.
My question:
What is the principle involved here? Clearly there are some
limitations as to what operations you're allowed to do inside the return block.
A reference is totally fine; some searching didn't yield anything.
In short: you cannot use offset (N-1) there, because the syntax doesn't allow it. (And you should get a syntax error).
In general, limit and offset expect the actual number:
The LIMIT clause can be used to constrain the number of rows returned by the SELECT statement. LIMIT takes one or two numeric arguments, which must both be nonnegative integer constants.
There is an exceptions though inside stored procedures:
Within stored programs, LIMIT parameters can be specified using integer-valued routine parameters or local variables.
So you can use a variable there, but not an expression. That is why it works if you use the precalculated variable M instead.
DELIMITER //
CREATE FUNCTION getNthHighestSalary2(N INT) RETURNS INT
DETERMINISTIC
BEGIN
RETURN
(SELECT salary
FROM employee
ORDER BY salary DESC
LIMIT 1 OFFSET N);
END//
DELIMITER ;
The function return an integer, because you select only 1 value with LIMIT 1.
A result set with two rows, would produce an error.
the DETERMINISTIC i added because mysql wants there an option
Also tables can't be used as return value.
If you want to return more variables, you have to add user define variables after N INT, which can the be used in the SEELCT where you call the function

Can't Set User-defined Variable From MySQL to Excel With ODBC

I have a query that has a user-defined variable set on top of the main query. Its something like this.
set #from_date = '2019-10-01', #end_date = '2019-12-31';
SELECT * FROM myTable
WHERE create_time between #from_date AND #end_date;
It works just fine when I executed it in MySQL Workbench, but when I put it to Excel via MySQL ODBC it always shows an error like this.
I need that user-defined variable to works in Excel. What am I supposed to change in my query?
The ODBC connector is most likely communicating with MySQL via statements or prepared statements, one statement at a time, and user variables are not supported. A prepared statement would be one way you could bind your date literals. Another option here, given your example, would be to just inline the date literals:
SELECT *
FROM myTable
WHERE create_time >= '2019-10-01' AND create_time < '2020-01-01';
Side note: I expressed the check on the create_time, which seems to want to include the final quarter of 2019, using an inequality. The reason for this is that if create_time be a timestamp/datetime, then using BETWEEN with 31-December on the RHS would only include that day at midnight, at no time after it.
Use subquery for variables values init:
SELECT *
FROM myTable,
( SELECT #from_date := '2019-10-01', #end_date := '2019-12-31' ) init_vars
WHERE create_time between #from_date AND #end_date;
Pay attention:
SELECT is used, not SET;
Assinging operator := is used, = operator will be treated as comparing one in this case giving wrong result;
Alias (init_vars) may be any, but it is compulsory.
Variable is initialized once but may be used a lot of times.
If your query is complex (nested) the variables must be initialized in the most inner subquery. Or in the first CTE if DBMS version knows about CTEs. If the query is extremely complex, and you cannot determine what subquery is "the most inner", then look for execution plan for what table is scanned firstly, and use its subquery for variables init (but remember that execution plan may be altered in any moment).

How to dynamicly assing LIMIT in MySQL?

I have query
SELECT *
LIMIT 3, (6 - 3)
Wchich returns:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(6 - 3)' at line 2
I basicly want to select 6 rows * from 3-rd row, in MSSQL it's WHERE RowNumber BETWEEN 3 and 6 but dynamicly knowing :from and :to parameters instaed of :from and :size
USE OFFSET in this case
SELECT name
FROM your_db
ORDER BY your_column DESC
LIMIT 10 OFFSET 10
The LIMIT clause can be used to constrain the number of rows returned by the SELECT statement. LIMIT takes one or two numeric arguments, which must both be nonnegative integer constant.
Basically if you're not in a stored procedure or a prepared statement you can't do it.
Within prepared statements, LIMIT parameters can be specified using ? placeholder markers.
Within stored programs, LIMIT parameters can be specified using
integer-valued routine parameters or local variables
So, inside a stored procedure, the following would work:
declare from_row bigint;
declare to_row bigint;
SELECT name
FROM your_db
ORDER BY your_column DESC
LIMIT from_row OFFSET (to_row-from_row);

Convert MSSQL paging statement to MySQL paging statement

I wish to convert a basic paging MSSQL statement to MySQL.
particularly the ROW_NUMBER() and OVER combined with ORDER BY are tricky to me.
SELECT * FROM (select ROW_NUMBER() OVER (ORDER BY publishdate DESC) as RowNum,
* FROM news WHERE publishdate <=getdate()) as info
WHERE RowNum > 0 AND RowNum <= (100)
How would I convert thisto a MySQL statement?
Try LIMIT syntax like this:
select *
FROM news WHERE publishdate <= CURDATE()
LIMIT 0,100; # Retrieve rows 1-100
The LIMIT clause can be used to constrain the number of rows returned by the SELECT statement. LIMIT takes one or two numeric arguments, which must both be nonnegative integer constants (except when using prepared statements).
With two arguments, the first argument specifies the offset of the first row to return, and the second specifies the maximum number of rows to return. The offset of the initial row is 0 (not 1):
SELECT * FROM tbl LIMIT 5,10; # Retrieve rows 6-15

Integers in calculated fields for MySQL

I'm attempting to query a MySQL database. The fields that are stored in the database as integers are returned as integers which is as I would expect. When there is a value calculated in a Stored Procedure it is always returned as longlong, even if I can guarantee that the number will be either 0 or 1.
I need a way to ensure that the result of a calculated field is still returned as an int.
Interestingly, doing the calculation in a view, and then querying that view seems to fix the problem, but it takes an enormous performance hit.
Edit
An example of the sorts of procedures I'm trying to use would be:
DELIMITER //
CREATE PROCEDURE getProjectFinance(IN projectID varchar(30))
BEGIN
SELECT p.projectID as id,
(Select sum(COALESCE(v.Cost, 0))
from variations v
where v.projectID = p.projectID) as total
FROM Projects p
WHERE p.projectID = projectID;
END//
DELIMITER ;
DELIMITER //
CREATE PROCEDURE getAllProjectsFinance()
BEGIN
SELECT p.projectID as id,
(Select sum(COALESCE(v.Cost, 0))
from variations v
where v.projectID = p.projectID) as total
FROM Projects p
END//
DELIMITER ;
Edit 2
I've attempted to simplify the problem slightly. The following SQL command returns a record with a single field (count) which is of type longlong (8 bytes). I want it to be of type integer (4 bytes)
SELECT (Select 1) as count;
Using cast as follows doesn't help either:
SELECT cast((Select 1) as signed integer) as count;
What if you just cast the calculated value?
cast((
Select sum(COALESCE(v.Cost, 0))
from variations v
where v.projectID = p.projectID
)
as integer
) as total
CREATE FUNCTION name(parameter paramType, .... ) RETURNS returnType instead of create procedure ... you should also use a return clause at the end of the body of your function. For more information look at the documentation the common and function specific parts.