I'm learning mysql stored procedures and, as it turns out, I'm not so good at it for now. I want to create a stored procedure that selects some columns from different tables and, obviously, outputs the result. I have:
USE `usertable159`;
DROP procedure IF EXISTS `getDataFor`;
DELIMITER $$
USE `usertable159`$$
CREATE DEFINER=`michael`#`%` PROCEDURE `getDataFor`(IN COUNTRY VARCHAR(2), IN ASIN VARCHAR(20),IN FC VARCHAR(1))
BEGIN
SET #sql = "select p.price, p.sku, p.fulfillment_channel, GROUP_CONCAT(es.excludedSeller) excluded, r.excludeNonFeatured
FROM "+COUNTRY+"_products p
LEFT JOIN ("+COUNTRY+"_excludedSellers es, "+COUNTRY+"_excludeRules r)
ON p.seller_sku = es.seller_sku and p.seller_sku = r.seller_sku where p.ASIN = '" + ASIN + "' and p.fulfillment_channel = " + FC + ";
";
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END $$
DELIMITER ;
From all the errors I expected when writing this... the one I get is surprising to me:
Error Code: 1054. Unknown column 'de' in 'field list'
This is how I call it:
call getDataFor(de, B000LNHB8A, 2);
The IN parameter COUNTRY is not in the selected columns, how does it come?
I also tried calling it with
call getDataFor('de', 'B000LNHB8A', '2');
which results in
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 '2' at line 1
You have several problems:
1) You cannot concatenate strings with + in MySQL. You must use the CONCAT() built-in function.
2) Your LEFT JOIN syntax is incorrect. It should be LEFT JOIN x ON ... LEFT JOIN y ON ....
3) You must quote your arguments. It looks like you also tried that. You definitely need to do that.
You need to quote the call arguments:
call getDataFor('de', 'B000lNHB8A', 2)
^--^ ^----------^
Remember that any "string" that's not in quotes will be interpreted as a table/field name reference, NOT the value it represents.
Related
I have this MySQL procedure:
CREATE PROCEDURE Employee_Dilligence(IN No_Hours INT)
BEGIN
SELECT EmpName,employees.EmpID, Hours_Worked,
Dilligence(Hours_Worked/(TIMESTAMPDIFF(Day, StartDate, CURDATE())*No_Hours))
AS concat('Worked more than', No_Hours, 'Hours')
FROM company.employees INNER JOIN company.employee_project INNER JOIN company.projects
ON employees.EmpID = employee_project.EmpID AND employee_project.ProjID = projects.ProjID
WHERE projects.ProjID = ProjectID;
And it works correctly, except for one thing: the concat here:
AS concat('Worked more than', No_Hours, 'Hours')
I do not understand why, though. The error message I'm getting is:
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 '('Worked more than', No_Hours, 'hours per day')
FROM company.employees INNER JOIN c' at line 5
Yet it looks like I am using concat correctly, so how can I fix this problem? If I replace the problematic line with just:
AS N_Hours_Worked_Per_Day
then it works fine, but isn't as nice.
The expected output I want would look something like this:
Employee_Dilligence(2)
#COLUMN NAMES OF OUTPUT:
EmpName | EmpID | Hours_Worked | Worked more than 2 hours per day
Employee_Dilligence(3)
#COLUMN NAMES OF OUTPUT:
EmpName | EmpID | Hours_Worked | Worked more than 3 hours per day
You can't use CONCAT as an alias for a field in MySQL. The alias must be a string, this will be the field's name in your result.
To build an SQL that include a variable as a fieldname, you can use a prepared statement with the following:
DELIMITER $$
CREATE PROCEDURE Employee_Dilligence(IN No_Hours INT)
BEGIN
SET #sql = CONCAT('SELECT EmpName,employees.EmpID, Hours_Worked, Dilligence(Hours_Worked/(TIMESTAMPDIFF(Day, StartDate, CURDATE())*',No_Hours,')) AS "Worked more than', No_Hours, 'Hours" FROM company.employees INNER JOIN company.employee_project INNER JOIN company.projects ON employees.EmpID = employee_project.EmpID AND employee_project.ProjID = projects.ProjID WHERE projects.ProjID = ProjectID;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
With this you first make your query as a string, then make a prepared statement from it, which can be executed by the server.
It seems you are doing a syntax error. please see blow how to use concate in query.
SELECT CONCAT(first_name, ' ', last_name) AS 'Name', dept FROM users;
I'm converting all of my existing MSSQL databases and stored procedures am stuck on a new stored procedure where I need to update an existing record. The procedure gets called from a web form once a record has been inserted into the database and en email sent successfully (or at least passed off to the SMTP server)
I've had a working procedure in MSSQL for a long time but am trying to convert it to MySQL. I'm passing in 3 variables - a bit indicating the email got sent, a string indicating which SMTP server has been used to sent the email and a unique record id so I'll know what record to update. I'm also adding the date and time to another field to know when the procedure ran.
I've got the following but keep getting an error "#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 '' at line 7 - yet I don't see anything off at line 7 - at least to my eye.
The code I'm trying to use is:
CREATE PROCEDURE `sp_Test`(
`emailSent_In` BIGINT UNSIGNED,
`emailTransport_In` VARCHAR(100),
`formSecret_In` VARCHAR(32)
)
BEGIN
SET #`query` := CONCAT('UPDATE ',`tbl_JustSayThanks`,'
SET `emailSent` = `emailSent_In`,
`emailTransport` = ',`emailTransport_In`,',
`emailSentDate` = NOW()
WHERE `formSecret` = ', `formSecret_In`, '');
PREPARE `stmt` FROM #`query`;
EXECUTE `stmt`;
#`query` := NULL;
DEALLOCATE PREPARE `stmt`;
END//
DELIMITER ;
Just FYI, I'm using the CONCAT based on a previous answer I received from wchiquito and will be passing in the table name eventually. But, I wanted to get it to work on a simplified level before going there.
The following is wrong:
SET #`query` := CONCAT('UPDATE ',`tbl_JustSayThanks`,'
because you seem to be concatenating your SQL text with the value of tbl_JustSayThanks, but I think you mean to use the identifier itself. This should therefore be:
SET #`query` := CONCAT('UPDATE `tbl_JustSayThanks`',
The following is wrong:
`emailTransport` = ',`emailTransport_In`,',
because the variable is a VARCHAR but you don't quote it as a string literal in your SQL statement. It's easy to get mixed up with the multiple levels of quoting. It should be:
`emailTransport` = ''', `emailTransport_In`, ''',
The following is wrong for the same reason:
WHERE `formSecret` = ', `formSecret_In`, '');
it should be:
WHERE `formSecret` = ''', `formSecret_In`, '''');
This still suffers from SQL injection problems, unless you can guarantee that the input parameters are safe (which is not a good assumption). If you need to concatenate values into your SQL expressions, you should use the QUOTE() function to do escaping:
SET #query = CONCAT('
UPDATE tbl_JustSayThanks
SET emailSent = ', QUOTE(emailSent_In), '
emailTransport = ', QUOTE(emailTransport_In), '
emailSentDate = NOW()
WHERE formSecret = ', QUOTE(formSecret_In));
More comments:
You don't need to delimit every identifier with back-ticks, only those that conflict with SQL reserved words, or contain whitespace or punctuation or international characters. None of your identifiers you show require delimiting.
When you use prepared statements, you should use query parameters with the ? placeholders, intead of concatenating variables into the SQL string. You don't quote parameter placeholders in your SQL query. That way you won't run into hard-to-debug syntax errors like the ones you found.
Here's an example showing the fixes:
CREATE PROCEDURE sp_Test(
emailSent_In BIGINT UNSIGNED,
emailTransport_In VARCHAR(100),
formSecret_In VARCHAR(32)
)
BEGIN
SET #query = '
UPDATE tbl_JustSayThanks
SET emailSent = ?,
emailTransport = ?,
emailSentDate = NOW()
WHERE formSecret = ?';
SET #es = emailSent_In;
SET #et = emailTransport_In;
SET #fs = formSecret_In;
PREPARE stmt FROM #query;
EXECUTE stmt USING #es, #et, #fs;
DEALLOCATE PREPARE stmt;
END//
DELIMITER ;
Final comment:
Your example query has no dynamic syntax elements, only dynamic values. So you don't need to use a prepared statement at all.
This is how I'd really write the procedure:
CREATE PROCEDURE sp_Test(
emailSent_In BIGINT UNSIGNED,
emailTransport_In VARCHAR(100),
formSecret_In VARCHAR(32)
)
BEGIN
UPDATE tbl_JustSayThanks
SET emailSent = emailSent_In,
emailTransport = emailTransport_In,
emailSentDate = NOW()
WHERE formSecret = formSecret_In;
END//
DELIMITER ;
You should also be aware that MySQL stored procedures are greatly inferior to Microsoft SQL Server. MySQL doesn't keep compiled stored procedures, it doesn't support packages, it doesn't have a debugger... I recommend you do not use MySQL stored procedures. Use application code instead.
I currently have a stored procedure in MySQL called Admin_Emails, which accepts three parameters: siteID VARCHAR(20) categoryID INT approved INT
The siteID is always in the following format: s-xxx(x)-xxxxxx.
In the procedure I add the siteID to my query using the following:
IF(siteID IS NOT NULL)
THEN
SET #query = CONCAT(#query, " AND e.site_id = ", siteID);
END IF;
If I run the procedure (ex. CALL Admin_Emails('s-osp-123ABC', 2, 1) then I get the following error:
#1054 - Unknown column 's' in 'where clause'
The s is coming from the siteID string. I tried modifying my CONCAT statement to the following:
SET #query = CONCAT(#query, " AND e.site_id = ", "'" + siteID + "'");
which will not produce an error, but will give me results with siteID's that do not match the input. I purposely produced an error by excluding the AND in the query, and it was showing a 0 where the siteID should be:
#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 'e.site_id = 0 AND e.category_id = 2 AND e.is_approved != 0' at line 20
I'm at a loss. I've tried searches for issues related to VARCHAR strings in stored procedures, but can't find similar situations. Is anyone aware of what I'm doing wrong here?
I didn't want to overload with the whole procedure, so I'm hoping that what I've provided here is enough, but if not I can produce more of my procedure.
why not
CONCAT(#query, " AND e.site_id = ", "'", siteID,"'");
Seems odd to try and + when concat() supports n values of string concat...
I think ++ is trying to do math which is resulting in the error.
Or you have an or limit at the end of the query and with the addition of and.... if the or doesn't have proper ()'s it could cause the issue of unexpected results.
Be warned if SiteID passed in isn't properly sanitized, you have SQL injection possibilities now.
I'm working with stored procedures in mysql, so I have the following procedure:
DELIMITER ##
DROP PROCEDURE IF EXISTS generarEstadisticoRD ##
CREATE PROCEDURE generarEstadisticoRD ( mesInicial INT,anualInicial INT, mesFinal INT,anualFinal INT, codigoEntidad CHAR(3),mes INT )
BEGIN
DECLARE controlador INT;
DECLARE tipoDocumento CHAR(2);
DECLARE cursorDocumentos CURSOR FOR SELECT DISTINCT e.claseDocIdentidadFallecido
FROM EstadisticoRD e WHERE e.anual>=anualInicial AND e.anual<=anualFinal
AND e.mes >=mesInicial AND e.mes<=mesFinal AND e.codOficina=codigoEntidad;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET controlador = 1;
DROP TEMPORARY TABLE IF EXISTS estadistico;
CREATE TEMPORARY TABLE IF NOT EXISTS
estadistico( TIPO CHAR(2), MES INT );
OPEN cursorDocumentos;
cursorLoop : LOOP
FETCH cursorDocumentos INTO tipoDocumento;
IF( controlador=1 ) THEN
LEAVE cursorLoop;
END IF
/**
*Lógica
*/
INSERT INTO estadistico(`TIPO`,`MES`)
SELECT DISTINCT
c.descripcion,
IFNULL( (SELECT e.numRegistrosReportados FROM estadisticoRD e WHERE e.codOficina=codigoEntidad
AND e.claseDocIdentidadFallecido=tipoDocumento AND e.mes=mes ), 0)
FROM estadisticoRD e, claseDoc c WHERE e.codOficina=codigoEntidad AND e.claseDocIdentidadFallecido=tipoDocumento
AND c.claseDoc = e.claseDocIdentidadFallecido;
END LOOP cursorLoop;
CLOSE cursorDocumentos;
SELECT * FROM estadistico;
END ##
DELIMITER ;
I get the following messages when I try to execute the procedure:
Executed successfully in 0,001 s, 0 rows affected.
Line 2, column 1
Error code 1064, SQL state 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 'INSERT INTO estadistico(`TIPO`,`MES`)
SELECT DISTINCT c.descripcion,
' at line 24
Line 3, column 1
So, what am I doing wrong?.
UPDATE 1:
The I corrected the mistake with semicolon thanks #Daniel Victoria
But now I get the following mistake:
Error code 1267, SQL state HY000: Illegal mix of collations (latin1_spanish_ci,IMPLICIT) and (latin1_swedish_ci,IMPLICIT) for operation '='
Exactly I get this mistake when I do
SELECT DISTINCT e.claseDocIdentidadFallecido
FROM EstadisticoRD e WHERE ... AND e.codOficina=codigoEntidad;
why when I do e.codOficina=codigoEntidad I get this mistake, how to fixed it?.
UPDATE 2:
To solve it, I need to put COLLATE latin1_swedish_ci after to the column that has the mistake.
In this case the new query is :
SELECT DISTINCT *
FROM estadisticoRD e WHERE e.anual>=anualInicial AND e.anual<=anualFinal
AND e.mes >=mesInicial AND e.mes<=mesFinal AND e.codOficina = codigoEntidad COLLATE latin1_swedish_ci;
I hope to finish this procedure the best way.
Your are missing a semicolon (;) after the "END IF"
I am trying to have a conditional change in a parameter for update statement.
I am getting the following error when I try the following function
/home/y/bin/mysql -u root < testpri.sql > out
ERROR 1415 (0A000) at line 4: Not allowed to return a result set from a function
Contents of testpri.sql are as follows:
use `zestdb`;
DROP FUNCTION IF EXISTS UPDATEPASSWD;
DELIMITER //
CREATE FUNCTION UPDATEPASSWD(n INT) RETURNS varchar(255) DETERMINISTIC
BEGIN
DECLARE mypasswd varchar(255);
IF (n = 1) THEN
SET mypasswd = '12ccc1e5c3c9203af7752f937fca4ea6263f07a5';
SELECT 'n is 1' AS ' ';
ELSE
SET mypasswd = '1a7bc371cc108075cf8115918547c3019bf97e5d';
SELECT 'n is 0' AS ' ';
END IF;>
SELECT CONCAT('mypasswd is ', mypasswd) AS ' ';
RETURN mypasswd;
END //
DELIMITER ;
CALL UPDATEPASSWD(0);
What am I missing?
I think it's actually your debugging SELECT calls.
From the docs:
Statements that return a result set can be used within a stored procedure but not within a stored function. This prohibition includes SELECT statements that do not have an INTO var_list clause...
I arrived in search of answers to the same question, and found another way to work around the issue, so that I can use the SELECT statement that is the heart and soul of the MySQL function that elicited the warning.
Consider the following snippet.
SET intNMatches = ( SELECT COUNT(*) ...
SET coerces the SELECT statement to return its one and only column, a row count, into intNMatches, a local variable cast to BIGINT. Since it contains trade secrets, I can't show the rest of the query. Suffice it to say that the query installs without causing the MySQL engine to issue a warning.