[英]MySQL Stored Procedure Only Returning One Select Query
我有以下存储过程:
CREATE PROCEDURE `Get_Events_And_Deadlines_By_User`(IN `intervalStart` INT, IN `intervalEnd` INT)
BEGIN
IF (intervalStart != 0 AND intervalEnd != 0) THEN
SELECT 1 AS `test`;
ELSEIF (intervalStart != 0 AND intervalEnd = 0) THEN
BEGIN
SELECT 2 AS `test`;
IF ((SELECT FOUND_ROWS())=1) THEN
SELECT 3 AS `test`;
END IF;
END;
ELSE
SELECT 4 AS `test`;
END IF;
END
当我运行call Get_Events_And_Deadlines_By_User(1,0)
我只得到带有2作为结果的select查询,if语句中的select永不返回。 在返回存储过程之前,似乎仅执行遇到的第一个选择查询。 为什么是这样? 我该怎么做才能解决此问题? 我希望在if成立时,将if语句内的select查询作为唯一结果。
您的过程将返回多个结果集。
PDO:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'localonly', 'localonly', array(
PDO::ATTR_EMULATE_PREPARES=>false,
PDO::MYSQL_ATTR_DIRECT_QUERY=>false,
PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION
));
$result = $pdo->query('Call Get_Events_And_Deadlines_By_User(1,0)');
do {
foreach( $result as $row ) {
echo join(', ', $row), "\r\n";
}
echo "-----\r\n";
} while($result->nextRowset());
我的测试:
mysql> DELIMITER $$
mysql> DROP PROCEDURE IF EXISTS `Get_Events_And_Deadlines_By_User`$$
Query OK, 0 rows affected, 1 warning (0,00 sec)
mysql> CREATE PROCEDURE `Get_Events_And_Deadlines_By_User`(
-> IN `intervalStart` INT,
-> IN `intervalEnd` INT
-> )
-> BEGIN
-> IF (intervalStart != 0 AND intervalEnd != 0) THEN
-> SELECT 1 AS `test`;
-> ELSEIF (intervalStart != 0 AND intervalEnd = 0) THEN
-> SELECT 2 AS `test`;
-> IF ((SELECT FOUND_ROWS())=1) THEN
-> SELECT 3 AS `test`;
-> END IF;
-> ELSE
-> SELECT 4 AS `test`;
-> END IF;
-> END$$
Query OK, 0 rows affected (0,00 sec)
mysql> DELIMITER ;
mysql> CALL `Get_Events_And_Deadlines_By_User`(1, 0);
+------+
| test |
+------+
| 2 |
+------+
1 row in set (0,00 sec)
+------+
| test |
+------+
| 3 |
+------+
1 row in set (0,00 sec)
Query OK, 0 rows affected (0,00 sec)
问: 我只得到带有2作为结果的选择查询,if语句内的选择从不返回。 在返回存储过程之前,似乎仅执行遇到的第一个选择查询。 为什么是这样?
答:该过程将返回第二个结果集。 客户负责请求第二个(和后续)结果集。 (在PHP中,这需要调用mysqli mysqli_next_result
或PDO nextRowset
函数,具体取决于所使用的接口库。
但这似乎并不是您真正的问题。
问: 如何解决此问题?
答:这实际上取决于您想要实现的行为。 一个过程可以返回多个结果集,而客户端可以处理它们。
问: 当if成立时,我希望select查询在if语句内成为唯一结果。
答:您想运行一个查询(示例过程中的SELECT 2
查询),但是您不希望该过程将其作为结果集返回。 您只想知道查询返回多少行,并根据返回的行有条件地控制存储程序中的流。
您可以使用几种构造来实现这一目标。 可以在过程中运行查询, 而不必将该查询的结果作为结果集返回。
使用SELECT EXISTS (subquery)
测试“大于零”的行
我怀疑您真的不想使用FOUND_ROWS
函数; 而且我怀疑您不想测试找到的行数是否完全等于1。
如果您要尝试确定某个查询是否返回一个或多个行,则可以使用以下模式:
IF ( SELECT EXISTS ( SELECT 2 AS `test` ) ) THEN
SELECT 2 AS `test`;
ELSE
SELECT 3 AS `test`;
END IF;
鉴于示例代码中的查询保证可以返回准确的一行,我只是在猜测您要实现的目标。
如果您有查询,并且想要查看该查询是否返回一行,则可以将其包装在EXISTS()
条件中,该条件将返回布尔值。
如果子查询返回至少一行,则EXISTS(subquery)
返回值1;否则,返回0。 如果子查询未返回任何行,则返回0。
可以在IF
使用该构造,以控制存储程序中的逻辑流。
IF ( SELECT EXISTS(subquery) ) THEN
-- subquery returned at least one row, so do something
ELSE
-- subquery didn't return any rows, so do something else
END IF;
使用SELECT COUNT(1) INTO FROM (subquery) q
获得确切的行数
如果测试子查询中的行的“存在”还不够; 如果由于某种原因需要获取子查询返回的确切行数,则可以使用COUNT()
聚合。 例如:
SELECT COUNT(1) FROM (subquery) q
为避免将该查询的结果作为过程的结果集返回,可以将其返回的值分配给过程变量或用户定义的变量。 假设您已在过程顶部声明了过程变量,如下所示:
DECLARE myrowcount INT;
您可以执行以下操作:
SELECT COUNT(1) INTO myrowcount FROM (subquery) q
IF ( myrowcount = 1 ) THEN
-- subquery returned exactly one row
ELSE
-- subquery either returned zero rows, or returned more than one row
END IF;
可以使用用户定义的变量代替过程变量。 我的喜好是使用过程变量,如果不需要在执行过程之外继续保留该行数。
(使用用户定义的变量的最大弊端是引入了不必要的歧义。后来读代码的人却想知道在过程结束后是否真的需要存储在用户定义的变量中的值。知道这是否是过程的有目的的,有目的的副作用,还有其他依赖于我的想法。在我看来,避免歧义是使用过程变量的充分理由。)
根据到目前为止您在评论中提到的内容,我认为您正在寻找以下内容。 我所做的就是向您的过程中添加一个新参数作为输出,而不是使用SELECT
来填充test的值,而是使用了一个从外部向您的函数发送的变量,并从外部接收更新的值并使用SELECT @test
获得您的价值,一次只能获得一个价值,而不是两个。
如果您对我在过程中留下的评论有疑问,建议您阅读有关FOUND_ROWS()使用的更多信息。
DELIMITER $$
DROP PROCEDURE IF EXISTS `Get_Events_And_Deadlines_By_User` $$
CREATE PROCEDURE `Get_Events_And_Deadlines_By_User`(
IN `intervalStart` INT,
IN `intervalEnd` INT,
OUT `test` INT
)
BEGIN
IF (intervalStart != 0 AND intervalEnd != 0) THEN
SET test = 1;
ELSEIF (intervalStart != 0 AND intervalEnd = 0) THEN
BEGIN
SET test = 2;
# include your select query here,
# as you need if for FOUND_ROWS(), or
# it will always return 1 cause there is no successful
# SELECT query before it and it will be always 1.
SELECT * FROM someTable;
IF ((SELECT FOUND_ROWS())=1) THEN
SET test = 3;
END IF;
END;
ELSE
SET test = 4;
END IF;
END$$
DELIMITER ;
CALL Get_Events_And_Deadlines_By_User(1,0,@test);
SELECT @test;
希望对您有所帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.