MySQL error after attempting to convert Microsoft SQL function - mysql

I am trying to convert a function I made in Microsoft SQL into a function for MySQL however I have absolutely no clue how to do that. I tried converting the original code through SQLines and messing with it to no avail.
The working Microsoft code is
ALTER FUNCTION [dbo].[TotalTripsGuideFunc] (#guideid CHAR(4))
RETURNS VARCHAR
BEGIN
DECLARE #trip_counts INT
DECLARE #results VARCHAR
SELECT #trip_counts = COUNT(*) FROM dbo.Reservation,dbo.TripGuides WHERE Reservation.TripID = TripGuides.TripID AND TripGuides.GuideNum = #guideid
SELECT #results = #guideid + ' has ' + CAST (#trip_counts AS VARCHAR(4))+ ' guides '
RETURN #results
END
and the attempted MySql code is
DELIMITER //
CREATE FUNCTION TotalTripsGuideFunc (p_guideid CHAR(4))
RETURNS VARCHAR(1)
BEGIN
DECLARE v_trip_counts INT DEFAULT 0;
DECLARE v_results VARCHAR(1);
SELECT COUNT(*) INTO v_trip_counts FROM Reservation,TripGuides
WHERE Reservation.TripID = TripGuides.TripID AND TripGuides.GuideNum = p_guideid;
SELECT v_results = concat(p_guideid , ' has ', CAST(v_trip_counts AS CHAR), ' guides ');
RETURN v_results;
END;
//
DELIMITER ;
Which returns the error
1415 - Not allowed to return a result set from a function
EDIT
Here is the revised code
DELIMITER //
CREATE FUNCTION TotalTripsGuideFunc (p_guideid CHAR(4))
RETURNS VARCHAR
BEGIN
DECLARE v_trip_counts INT DEFAULT 0;
DECLARE v_results VARCHAR(30);
SELECT COUNT(*) INTO v_trip_counts FROM Reservation,TripGuides
WHERE Reservation.TripID = TripGuides.TripID AND TripGuides.GuideNum = p_guideid;
SET v_results = concat(p_guideid , ' has ', v_trip_counts, ' guides ');
RETURN v_results;
END;
//
DELIMITER ;
Which returns the new error
1064 - You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'BEGIN DECLARE v_trip_counts INT DEFAULT 0; DECLARE v_results VARCHAR(3' at line 3

Small mistakes:
DECLARE v_results VARCHAR(1); should be a little bigger like DECLARE v_results VARCHAR(30);
RETURNS VARCHAR(1) should be RETURNS VARCHAR
You used the select into right in the first select and wrong in the second. Just need to fix it:
-- from
SELECT v_results = concat(p_guideid , ' has ', CAST(v_trip_counts AS CHAR), ' guides ');
-- to
SELECT concat(p_guideid , ' has ', v_trip_counts, ' guides ') INTO v_results;
-- OR just:
SET v_results = concat(p_guideid , ' has ', v_trip_counts, ' guides ');
-- no need for CAST, MySQL will do implicit conversion
EDIT: here is a working final version:
DELIMITER //
CREATE FUNCTION TotalTripsGuideFunc (p_guideid VARCHAR(30)) RETURNS VARCHAR(30)
BEGIN
DECLARE v_trip_counts INTEGER;
DECLARE v_results VARCHAR(30);
SELECT COUNT(*) INTO v_trip_counts FROM Reservation,TripGuides
WHERE Reservation.TripID = TripGuides.TripID AND TripGuides.GuideNum = p_guideid;
SET v_results = concat(p_guideid , ' has ', v_trip_counts, ' guides ') ;
RETURN v_results;
END
//
DELIMITER ;

I think you should return like this RETURN ( v_results )

Related

Create MySQL procedures

I am currently trying to create a stored procedure on my MySQL stored on Google Cloud Platform.
The SQL is correct seeing that I can create the procedure locally, but I can't figure out why it won't work from the command line:
mysql> CREATE PROCEDURE helpme
-> (
-> #cid varchar(4)
-> )
-> AS
-> DECLARE #res_cnt INT
-> DECLARE #name CHAR(10)
->
-> SELECT #res_cnt = COUNT(*) FROM dbo.TripGuides WHERE GuideNum = #cid
-> SELECT #name = LastName FROM dbo.Guide WHERE GuideNum = #cid
-> PRINT #name + ' has ' + CAST (#res_cnt AS VARCHAR(10))+' guides.';
ERROR 1064 (42000): 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 '#cid varchar(4)
)
AS
DECLARE #res_cnt INT
DECLARE #name CHAR(10)
SELECT #res_cn' at line 3
mysql>
I've tried a few different things thank I have bumped into. When declaring #cid I tried both
#cid CHAR(4)
#cid VARCHAR(4)
resulting in the same error being thrown.
For MySQL use
CREATE PROCEDURE helpme ( cid VARCHAR(4) )
SELECT CONCAT( ( SELECT LastName
FROM Guide
WHERE GuideNum = cid ),
' has ',
( SELECT COUNT(*)
FROM TripGuides
WHERE GuideNum = cid ),
' guides.'
) AS message;
Just out of curiosity how would I go about declaring res_cnt and name as to the original SQL call i did before?
CREATE PROCEDURE helpme ( cid VARCHAR(4) )
BEGIN
DECLARE res_cnt INT;
DECLARE `name` CHAR(10);
SELECT COUNT(*) INTO res_cnt FROM TripGuides WHERE GuideNum = cid;
SELECT LastName INTO `name` FROM Guide WHERE GuideNum = cid;
SELECT CONCAT( `name`, ' has ', res_cnt, ' guides.' ) AS output_message;
END
And do not forget about DELIMITER re-assign in that case.

"DECLARE" is not valid at this position for this server version, expecting: ERROR

I tried every way possible to put the "DECLARE" in my procedural SQL query (Using MySQL Workbench) and its showing:
"DECLARE" is not valid at this position for this server version, expecting: ERROR.
Now I need help.
DELIMITER //
CREATE PROCEDURE getdetails()
BEGIN
DECLARE
vin table.vin%type;
responsetime table.responseTimeStamp%type;
odometer table.odometer%type;
chargePercentage table.soc%type;
CURSOR sequential_vehicle_status is
SELECT vin, responseTimeStamp, odometer, soc FROM table ORDER BY vin, responseTimeStamp;
OPEN sequential_vehicle_status;
LOOP
FETCH sequential_vehicle_status into vin, responseTimeStamp, odometer, chargePercentage;
EXIT WHEN sequential_vehicle_status%notfound;
dbms_output.put_line(vin || " * " || responseTimeStamp || " * " || odometer || " * " || chargePercentage || "% " ||);
END LOOP;
CLOSE sequential_vehicle_status;
END //
DELIMITER ;
This code will work. Here is a demo. Please adjust the declared variables to your table column types.
CREATE PROCEDURE getdetails()
BEGIN
DECLARE finished INTEGER DEFAULT 0; --this is added so the exit from loop can be made
DECLARE vin int;
DECLARE responseTimeStamp int;
DECLARE odometer int;
DECLARE chargePercentage int;
DECLARE sequential_vehicle_status
cursor for
SELECT vin
, responseTimeStamp
, odometer
, soc
FROM `table`
ORDER BY vin, responseTimeStamp;
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET finished = 1;--this is added so the exit from loop can be made
OPEN sequential_vehicle_status;
start_loop: LOOP
IF finished = 1 THEN
LEAVE start_loop;
END IF;
FETCH sequential_vehicle_status into vin
, responseTimeStamp
, odometer
, chargePercentage;
select concat(vin, '*', responseTimeStamp, '*', odometer, '*', chargePercentage, '% ');
END LOOP;
CLOSE sequential_vehicle_status;
END;
Please read the comments to your question. DBMS_OUTPUT is oracle package.
Instead of that dbms_output line of code you can add this:
select concat(vin, '*', responseTimeStamp, '*', odometer, '*', chargePercentage, '% ');
You are combining mysql and oracle syntax both.
Dbms_output is package in oracle and IS clause is used in oracle to create cursor.
CREATE PROCEDURE getdetails()
BEGIN
DECLARE vin table.vin%type;
DECLARE responsetime table.responseTimeStamp%type;
DECLARE odometer table.odometer%type;
DECLARE chargePercentage table.soc%type;
DECLARE CURSOR sequential_vehicle_status for
SELECT vin, responseTimeStamp, odometer, soc FROM table ORDER BY vin, responseTimeStamp;
Also if you want to print something to console use select instead dbms_output which is oracle package.
ie. SELECT concat('VIN is ', vin);

How Can I Create a MySQL Function to Check JSON Validity?

I'm fairly new to MySQL but I'd like to create a function to validate a JSON objects that are stored in my database tables.
I looked up information on creating a function, but must be missing something as I can't seem to get it to work. It doesn't seem like it would be overly complicated but perhaps I'm not using the appropriate syntax.
Here is my code:
DELIMITER //
CREATE FUNCTION CHECKJSON( DB_NAME varchar(255), TABLE_NAME varchar(255), JSON_COLUMN varchar(255))
RETURNS varchar(300)
BEGIN
DECLARE notNullCount int;
DECLARE validJSONCount int;
DECLARE result varchar(300);
SET notNullCount = (SELECT count(*) FROM DB_NAME.TABLE_NAME WHERE JSON_COLUMN IS NOT NULL);
set validJSONCount = (SELECT count(*) FROM DB_NAME.TABLE_NAME WHERE JSON_VALID(JSON_COLUMN) > 0);
CASE
WHEN (validJSONCount = notNullCount) THEN
SET result = CONCAT('VALID JSON COUNT: ', validJSONCount)
ELSE
SET result = CONCAT('INVALID JSON COUNT: ', (notNullCount - validJSONCount))
END;
RETURN result;
END //
DELIMITER ;
When I try to run this code, I get the following error message:
"Error Code: 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 'ELSE SET result = CONCAT('INVALID JSON COUNT: ', (notNullCount - validJSONC' at line 14"
Any thoughts on how I might improve this code? Thanks!
Since MySQL 5.7 you have a pretty and simple function for this:
JSON_VALID(value)
Returns 0 or 1 to indicate whether a value is valid JSON. Returns NULL if the argument is NULL.
https://dev.mysql.com/doc/refman/5.7/en/json-attribute-functions.html#function_json-valid
You're missing a couple of ; and to end the case it should be END CASE.
DELIMITER //
CREATE FUNCTION CHECKJSON( DB_NAME varchar(255), TABLE_NAME varchar(255), JSON_COLUMN varchar(255))
RETURNS varchar(300)
BEGIN
DECLARE notNullCount int;
DECLARE validJSONCount int;
DECLARE result varchar(300);
SET notNullCount = (SELECT count(*) FROM DB_NAME.TABLE_NAME WHERE JSON_COLUMN IS NOT NULL);
set validJSONCount = (SELECT count(*) FROM DB_NAME.TABLE_NAME WHERE JSON_VALID(JSON_COLUMN) > 0);
CASE
WHEN (validJSONCount = notNullCount) THEN
SET result = CONCAT('VALID JSON COUNT: ', validJSONCount) ;
ELSE
SET result = CONCAT('INVALID JSON COUNT: ', (notNullCount - validJSONCount)) ;
END CASE;
RETURN result;
END //
DELIMITER ;

Shuffle a string with mysql/sql

I was wondering, if there is some way to shuffle the letters of a string in mysql/sql, i.e. something like the pseudocode: SELECT SHUFFLE('abcdef')?
Couldn't find any from http://dev.mysql.com/doc/refman/5.0/en/string-functions.html and searching for it just seems to find solutions for shuffling results, not a string.
Here you go:
DELIMITER //
DROP FUNCTION IF EXISTS shuffle //
CREATE FUNCTION shuffle(
v_chars TEXT
)
RETURNS TEXT
NOT DETERMINISTIC -- multiple RAND()'s
NO SQL
SQL SECURITY INVOKER
COMMENT ''
BEGIN
DECLARE v_retval TEXT DEFAULT '';
DECLARE u_pos INT UNSIGNED;
DECLARE u INT UNSIGNED;
SET u = LENGTH(v_chars);
WHILE u > 0
DO
SET u_pos = 1 + FLOOR(RAND() * u);
SET v_retval = CONCAT(v_retval, MID(v_chars, u_pos, 1));
SET v_chars = CONCAT(LEFT(v_chars, u_pos - 1), MID(v_chars, u_pos + 1, u));
SET u = u - 1;
END WHILE;
RETURN v_retval;
END;
//
DELIMITER ;
SELECT shuffle('abcdef');
See sqlfiddle.com for the output.
Tested successfully with mariadb 10.1 (mysql 5.6 equivalent)
Edit: this solution is for Microsoft SQL Server.
As it's not allowed to use RAND() in user defined function, we create a view to use it later in our shuffle function:
CREATE VIEW randomView
AS
SELECT RAND() randomResult
GO
The actual shuffle function is as following:
CREATE FUNCTION shuffle(#string NVARCHAR(MAX))
RETURNS NVARCHAR(MAX) AS
BEGIN
DECLARE #pos INT
DECLARE #char CHAR(1)
DECLARE #shuffeld NVARCHAR(MAX)
DECLARE #random DECIMAL(18,18)
WHILE LEN(#string) > 0
BEGIN
SELECT #random = randomResult FROM randomView
SET #pos = (CONVERT(INT, #random*1000000) % LEN(#string)) + 1
SET #char = SUBSTRING(#string, #pos, 1)
SET #shuffeld = CONCAT(#shuffeld, #char)
SET #string = CONCAT(SUBSTRING(#string, 1, #pos-1), SUBSTRING(#string, #pos+1, LEN(#string)))
END
RETURN #shuffeld
END
Calling the function
DECLARE #string NVARCHAR(MAX) = 'abcdefghijklmnonpqrstuvwxyz0123456789!"ยง$%&/()='
SELECT dbo.shuffle(#string)
There is nothing in standard SQL - your best bet is probably to write a user defined function

How to CREATE TYPE type_name AS existing_table_name

Is there a way to create table type in SQL Server 2008 based on scheme of existing table?
CREATE TABLE A (id INT, name VARCHAR(30))
CREATE TYPE type_a AS TABLE.A
Something like that.
No, this kind of composable DML is not yet possible. Microsoft has rejected this suggestion in the past, but with enough votes (e.g. more than 1!) it may get reconsidered in the future:
http://connect.microsoft.com/SQLServer/feedback/details/294130/table-valued-parameters-add-support-for-create-type-type-from-table-table-name-options-syntax-construct
You can use following stored procedure to create a type with same schema existing table may have.
Create PROCEDURE [dbo].[Sp_DefineTypeOutOfTableSchema]
#TableNames NVARCHAR(500)
AS
BEGIN
DECLARE #TableName NVARCHAR(100)
DECLARE #strSQL NVARCHAR(max)
DECLARE #strSQLCol NVARCHAR(1000)
DECLARE #ColName NVARCHAR(100)
DECLARE #ColDataTaype NVARCHAR(50)
DECLARE #ColDefault NVARCHAR(50)
DECLARE #ColIsNulable NVARCHAR(50)
DECLARE #ColCharMaxlen NVARCHAR(50)
DECLARE #ColNumPrec NVARCHAR(50)
DECLARE #ColNumScal NVARCHAR(50)
IF LEN(#TableNames) > 0 SET #TableNames = #TableNames + ','
WHILE LEN(#TableNames) > 0
BEGIN
SELECT #TableName = LTRIM(SUBSTRING(#TableNames, 1, CHARINDEX(',', #TableNames) - 1))
DECLARE schemaCur CURSOR FOR
SELECT COLUMN_NAME,DATA_TYPE,IS_NULLABLE,COLUMN_DEFAULT,CHARACTER_MAXIMUM_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME =#TableName
OPEN schemaCur
SELECT #strSQL=''
FETCH NEXT FROM schemaCur
INTO #ColName,#ColDataTaype,#ColIsNulable,#ColDefault,#ColCharMaxlen,#ColNumPrec,#ColNumScal
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #strSQLCol=''
SELECT #strSQLCol= '['+#ColName+'] '+'[' + #ColDataTaype +'] '
IF #ColDataTaype='nvarchar' or #ColDataTaype='char' or #ColDataTaype='varchar' or #ColDataTaype='vchar'
BEGIN
SELECT #strSQLCol=#strSQLCol+ '(' + #ColCharMaxlen +') '
END
ELSE IF #ColDataTaype='numeric' or #ColDataTaype='decimal'
BEGIN
SELECT #strSQLCol=#strSQLCol +'(' + #ColNumPrec +',' +#ColNumScal + ') '
END
IF #ColIsNulable='YES'
BEGIN
SELECT #strSQLCol=#strSQLCol+ 'NULL '
END
ELSE
BEGIN
SELECT #strSQLCol=#strSQLCol+ ' NOT NULL '
END
IF #ColDefault IS NOT NULL
BEGIN
SELECT #strSQLCol=#strSQLCol+ ' DEFAULT(' +#ColDefault + '),'
END
ELSE
BEGIN
SELECT #strSQLCol=#strSQLCol+ ' ,'
END
SELECT #strSQL=#strSQL+#strSQLCol
--print #strSQL
FETCH NEXT FROM schemaCur
INTO #ColName,#ColDataTaype,#ColIsNulable,#ColDefault,#ColCharMaxlen,#ColNumPrec,#ColNumScal
END
CLOSE schemaCur
DEALLOCATE schemaCur
--print #strSQL
SELECT #strSQL=left( #strSQL, len(#strSQL)-1)
--print #strSQL
IF EXISTS (SELECT * FROM sys.types WHERE IS_TABLE_TYPE = 1 AND name = 't_' +#TableName)
BEGIN
EXEC('DROP TYPE t_' +#TableName )
END
SELECT #strSQL = 'CREATE TYPE t_' + #TableName + ' AS TABLE (' + #strSQL + ')'
--print #strSQL
EXEC (#strSQL)
SELECT #TableNames = SUBSTRING(#TableNames, CHARINDEX(',', #TableNames) + 1, LEN(#TableNames))
END
END
you can use it like this
Exec Sp_DefineTypeOutOfTableSchema 'Table1name,Table2name'
You could experiment with creating a function that pulled the table definition out of sysobjects, systypes, syscolumns, syscomments, etc., and built a CREATE statement out of it. You'd just have to make sure to grab all of the important pieces (columns, constraints, comments, etc.) from the various sys tables.
Then call it like... EXEC myCREATEtable #template_table_name or some such...