示例代码:
BEGIN
PREPARE stmt_user_login FROM
"SELECT COUNT(*)
FROM `t_users`
WHERE `t_users`.`username` = ? AND `t_users`.`password` = ?;";
SET @a := $username;
SET @b := $password;
EXECUTE stmt_user_login USING @a, @b;
DEALLOCATE PREPARE stmt_user_login;
IF FOUND_ROWS() = '1' THEN
PREPARE stmt_user_loggedin FROM
"INSERT INTO
`t_sessions`
(`username`, `last_request`, `last_ip`, `last_port`, `token`, `validity`)
VALUES
(?, ?, ?, ?, ?, ?);";
SET @c := $user_name;
SET @d := $last_request;
SET @e := $last_ip;
SET @f := $last_port;
SET @g := $token;
SET @h := $validity;
EXECUTE stmt_user_loggedin USING @c, @d, @e, @f, @g, @h;
DEALLOCATE PREPARE stmt_user_loggedin;
END IF;
END
答案 0 :(得分:0)
是的,您可以在存储过程中使用多个预准备语句。
但这不是你手术中的问题。
FOUND_ROWS()
返回上一个查询返回的行数,而不是计数。在您的过程中,行数始终为1,即使它在上一个查询中报告的计数为0。 *
相反,您应该使用SELECT ... INTO
来捕获值。
此外,您不需要使用预准备语句在查询中安全地包含过程参数。你可以这样做:
BEGIN
DECLARE usercount INT;
SELECT COUNT(*) FROM t_users
WHERE t_users.username = $username AND t_users.password = $password
INTO usercount
IF usercount = 1 THEN
INSERT INTO t_sessions (username, last_request, last_ip, last_port, token, validity)
VALUES ($user_name, $last_request, $last_ip, $last_port, $token, $validity);
END IF;
END
我假设$username
和$password
,其他带有$
前缀的标识符是过程参数的名称。在SQL标识符上使用$
前缀是不常见的,但它是合法的。
您的程序甚至可以进一步简化:
BEGIN
INSERT INTO t_sessions (username, last_request, last_ip, last_port, token, validity)
SELECT $user_name, $last_request, $last_ip, $last_port, $token, $validity
FROM t.users WHERE t.users.username = $username AND t_users.password = password
LIMIT 1;
END
这种方法的工作方式是,如果SELECT中存在匹配项,它将返回一行,其中的列由$
- 前缀变量中的值填充。然后将该行插入t_sessions
。如果没有匹配,则返回零行,因此不会发生INSERT。
* 异常:FOUND_ROWS()
似乎在SELECT COUNT(*)
查询上返回0,如果查询使用NULL访问类型优化或“不可能在哪里”,就像使用{ {1}}。我记录了一个错误:http://bugs.mysql.com/bug.php?id=73283
还有更多,我发现针对MySQL {5.6}报告了很多其他错误,导致WHERE 1=0
返回错误的结果。我不建议使用此功能。
答案 1 :(得分:0)
我最终在存储过程中使用了简单的语句,并使用PHP中的预准备语句来调用该过程。
这是我从phpMyAdmin导出的SQL PROCEDURE:
CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_login`(IN `$username` VARCHAR(32) CHARSET utf8, IN `$password` VARCHAR(32) CHARSET utf8, IN `$token` VARCHAR(32) CHARSET utf8, IN `$last_request` VARCHAR(10) CHARSET utf8)
MODIFIES SQL DATA
BEGIN
SET @username := $username;
SET @password := $password;
SET @token := $token;
SET @last_request := $last_request;
SET @validity := '1800';
DELETE FROM `t_sessions` WHERE (`t_sessions`.`last_request` + @validity) < (@last_request + @validity)
AND `t_sessions`.`user` = @username
AND
(SELECT COUNT(`t_users`.`user_id`)
FROM `t_users`
WHERE `t_users`.`username` = @username
AND `t_users`.`password` = @password) = 1;
INSERT INTO `t_sessions` (`t_sessions`.`user`, `t_sessions`.`token`, `t_sessions`.`validity`, `t_sessions`.`last_request`)
SELECT `t_users`.`username`, @token, @validity, @last_request
FROM `t_users`
WHERE `t_users`.`username` = @username
AND `t_users`.`password` = @password;
SELECT
CASE LAST_INSERT_ID()
WHEN 0 THEN 0
ELSE 1
END AS `LOGGEDIN`;
END
和PHP部分:
include('conn.php'); // "conn.php is the file where I'm storing the connection script to mysql"
$conn->select_db("test");
$query_stmt = $conn->prepare("CALL `sp_login`(?, ?, ?, ?);");
$query_stmt->bind_param("ssss", $username, $password, $token, $last_request);
$query_stmt->execute();
$query_stmt->bind_result($result);
while ($query_stmt->fetch()) {
switch ($result) {
CASE 0:
$query_stmt->close();
echo 'BAD';
break;
CASE 1:
$query_stmt->close();
echo 'OK';
break;
}
if ($conn) {$conn->close();}
exit;
}