【问题标题】:MySQL Stored Procedure Only Returning One Select QueryMySQL 存储过程只返回一个选择查询
【发布时间】:2016-01-11 19:12:30
【问题描述】:

我有以下存储过程:

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 作为结果的选择查询,if 语句中的选择永远不会返回。在返回存储过程之前,似乎只执行了遇到的第一个选择查询。为什么是这样?我能做些什么来解决这个问题?我希望 if 语句中的 select 查询是 if 成立时的唯一结果。

【问题讨论】:

  • 您也可以发布您的 PHP 代码吗?该过程在 2 个 SELECT 中返回多个结果集,您的代码不仅需要遍历每个结果集的记录,还需要遍历多个结果集
  • @vmachan 我现在实际上没有任何 PHP 代码,我只在 phpmyadmin 中对其进行了测试,并且仅从第一个查询(if 语句之前)中获得了一个结果集。
  • 更改:IF ((SELECT FOUND_ROWS())==1) THEN by IF ((SELECT FOUND_ROWS())=1) THEN
  • @wchiquito 对不起,这是我的问题中的错字,我已经编辑过了。问题依然存在。如果我一起删除 if 语句,我什至会遇到问题:SELECT 2 AS test; SELECT 3 AS test;

标签: php mysql sql stored-procedures


【解决方案1】:

您的过程返回多个结果集。
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());

【讨论】:

  • @VolkerV 感谢您的回答!有什么办法可以从存储过程中返回第二个查询?
  • 是的,跳过第一个。
  • 代替SELECT 2 AS `test`; 的实际查询是什么?使用SELECT Count(*) INTO @nmrows FROM tablename WHERE .... 之类的东西可能更容易,然后检查@nmrows
【解决方案2】:

我的测试:

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)

之后:Retrieving Multiple Result sets with stored procedure in php/mysqli

【讨论】:

    【解决方案3】:

    问: 我只得到了 2 作为结果的 select 查询,if 语句中的 select 永远不会返回。在返回存储过程之前,似乎只执行了遇到的第一个选择查询。这是为什么呢?

    答:过程正在返回第二个结果集。客户端负责请求第二个(和后续)结果集。 (在 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。

    该结构可用于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;
    

    可以使用用户定义的变量代替过程变量。我的偏好是使用过程变量,如果在执行过程之后不需要保留该行数。

    (使用用户定义变量的最大缺点是它引入了不必要的歧义。后来阅读代码的人想知道存储在用户定义变量中的值在过程结束后是否真的需要。他们不知道这是否是该过程的有目的的、有意的副作用,是否依赖于其他东西。在我看来,避免这种歧义是使用过程变量的充分理由。)

    【讨论】:

      【解决方案4】:

      根据您迄今为止在 cmets 中提到的内容,我认为您正在寻找以下内容。我所做的只是将一个新参数作为输出添加到您的过程中,而不是使用SELECT 来填充测试的值,而是使用从外部发送到您的函数的变量并从外部接收更新的值并使用@ 987654323@ 获取您的价值,一次只有一个价值,而不是两个。

      如果您对我在程序中留下的评论有任何疑问,我建议您阅读更多关于FOUND_ROWS() usage 的信息。

      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;
      

      希望对你有帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-04-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-13
        • 2012-04-01
        相关资源
        最近更新 更多