【问题标题】:Call MySQL Stored Procedure in a C application giving an error在 C 应用程序中调用 MySQL 存储过程给出错误
【发布时间】:2015-08-21 13:59:06
【问题描述】:

我想在一个用 C 编写的应用程序中使用 MySQL 的CURSOR

我创建了一个存储过程

CREATE PROCEDURE curdemo(IN id INT)
BEGIN
    DECLARE a INT;
    DECLARE flag BOOL DEFAULT FALSE;
    DECLARE done INT DEFAULT FALSE;
    DECLARE cur1 CURSOR FOR SELECT test.Cars.Id FROM test.Cars;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    OPEN cur1;

    read_loop: LOOP
            FETCH cur1 INTO a;
            IF done THEN
                    LEAVE read_loop;
            END IF;

            IF flag THEN
                    SELECT test.Cars.Price FROM test.Cars WHERE test.Cars.id = a;
                    LEAVE read_loop;
            END IF;

            IF a = id THEN
                    SET flag = TRUE;
            END IF;
    END LOOP;

    CLOSE cur1;
    END//

这是我在数据库测试中名为 Cars 的表

+------+------------+--------+

| Id   | Name       | Price  |

+------+------------+--------+

|    1 | Audi       |  52642 |

|    2 | Mercedes   |  57127 |

|    3 | Skoda      |   9000 |

|    4 | Volvo      |  29000 |

|    5 | Bentley    | 350000 |

|    6 | Citroen    |  21000 |

|    7 | Hummer     |  41400 |

|    8 | Volkswagen |  21600 |

+------+------------+--------+

当我使用来自 C 应用程序的以下查询时,我得到结果 = NULL,因此只有在 id = 8 时才会出现分段错误,否则它可以正常工作

 snprintf(statement, 100, "CALL curdemo('%d')", id);
    // Call stored procedure query
    if ( mysql_query(device_table_conn, statement) )
    {
            error_function(device_table_conn);
            //return 1;
    }

    result = mysql_use_result(device_table_conn);

如果发生错误,mysql_use_result() 将返回 NULL

但如果 CALL 返回 NULL 或空结果,则不应返回 NULL。

【问题讨论】:

  • 你为什么要使用游标?
  • 在使用游标之前,我正在获取所有表信息,然后检查匹配条件,但我在某处读取游标为同一事物提供了更好的结果,而不是仅获取行或获取所有表我需要的结果。这只是一个测试用例。我的实际表格有更多数据。我说的对吗?
  • 我没有测试游标和简单查询,但是我在 mysql.com 论坛上看到游标性能不佳,只有游标可以提供帮助时才应该使用它们。
  • 嗨,Devart,你能给我链接来支持你的回复吗?感谢您的回复。

标签: mysql c


【解决方案1】:

终于明白了。

按照处理多个语句的步骤: https://dev.mysql.com/doc/refman/5.0/en/c-api-multiple-queries.html

这里是代码

    snprintf(statement, 100, "CALL curdemo('%d')", id);
    if ( mysql_query(device_table_conn, statement) )
    {
            error_function(device_table_conn);
            //return 1;
    }

    do{
            // did current statement return data?
            result = mysql_store_result(device_table_conn);
            if (result)
            {
                    // yes; process rows and free the result set
                    num_fields = mysql_num_fields(result);

                    while ((row = mysql_fetch_row(result)))
                    {
                            for ( i = 0; i < num_fields; i++ )
                            {
                                    printf("%s\n", row[i]);
                            }
                    }

                    mysql_free_result(result);
            }
            else
            {
                    mysql_free_result(result);
            }
            // more results? -1 = no, >0 = error, 0 = yes (keep looping)
            if ((status = mysql_next_result(device_table_conn)) > 0)
            {
                    ;
            }
    } while (status == 0);

    mysql_close(device_table_conn);

对于任何错误的 id(表中不存在)以及 id = 8 时,它不会返回任何内容。

【讨论】:

    【解决方案2】:

    你的read_loop 逻辑很有趣。

    让我们来看看吧:

    1. 你从光标中获取到a
    2. 你测试你的done标志;
    3. 你测试你的flag标志;
    4. 您针对id 测试a 的值并设置flag
    5. 你再次从光标获取,覆盖a的值;
    6. 您测试您的done 标志;
    7. 您测试了您的flag 标志,现在是true,此时您执行选择,但针对a 的错误值

    当您的输入 id8 时,您最终会超过表中的最后一行,这就是我认为您得到空结果的原因。

    你需要测试aid 的值做其他事情之前:

    read_loop: LOOP
            FETCH cur1 INTO a;
            IF done THEN
                    LEAVE read_loop;
            END IF;
    
            IF a = id THEN
                    SET flag = TRUE;
            END IF;
    
            IF flag THEN
                    SELECT test.Cars.Price FROM test.Cars WHERE test.Cars.id = a;
                    LEAVE read_loop;
            END IF;
    
    END LOOP;
    

    编辑

    我不得不问,为什么不直接获取指定 ID 的价格,调用mysql_store_result(),然后调用mysql_num_rows() 来查看查询是否真的返回了什么?如果您要获取特定的行键,那么您的结果集将永远只有 0 或 1 行,因此立即存储它不应该有问题。

    要么,要么先执行select count(*) from cars where id = :id;,并且仅在结果> 0时继续。

    【讨论】:

    • 您好约翰,感谢您的回复。您认为我最终获取了最后一行可能是我得到空结果的原因。但我不这么认为,因为 done 标志是 NOT FOUND 条件的处理程序,因此存储的程序不会因错误而终止。如果 NOT FOUND 条件不会发生,则不会设置标志,因此不会为表中不存在的错误 id 执行 LEAVE 条件。我的应用程序要求是我想获取当前 id 的下一个条目的价格。我已经测试过你说它因为错误的 id 而失败的案例。
    • @EdgeGoldberg:这是一个有趣的要求,如果提前了解这一点会有所帮助。那么除了空结果集之外,应该 id 8 的“下一个条目”是什么?
    • 如果没有更多行可用,则出现 NOT FOUND 条件。因此 id 8 的结果集将始终为空结果集。我有一个问题 mysql_use_result() 是否为空结果集返回 NULL?
    • @EdgeGoldberg:在阅读了documentation 之后——如果我知道的话。它针对错误条件返回NULL,但没有明确说明空结果集是否算作错误条件,至少在我快速浏览时没有明确说明。不得不说,MySQL 的在线文档很差。不幸的是,我不知道有什么更好的资源。
    • 感谢@John Bode 的回复。如果找到解决方案,会通知您。再次感谢。
    猜你喜欢
    • 2012-12-11
    • 1970-01-01
    • 2018-02-09
    • 2019-03-23
    • 1970-01-01
    • 2018-11-13
    • 1970-01-01
    • 2012-08-25
    • 2012-10-05
    相关资源
    最近更新 更多