【问题标题】:Unexplained MySQL behavior using C libmysqlclient使用 C libmysqlclient 无法解释的 MySQL 行为
【发布时间】:2016-09-13 02:49:57
【问题描述】:

我用 C 语言编写了一个处理引擎,它从一个表中提取 40k 条记录,处理数据并将结果写入另一个表。当我将处理后的数据打印到屏幕上时,它看起来很完美,因此所有变量都保持正确的值。我需要将数据写入具有 3 列作为主键的表中。我这样创建表:

CREATE TABLE halfcomp 
(
    MarketCode varchar(20) NOT NULL,
    TRZNum varchar(20) NOT NULL,
    origTRZNum varchar(20) NOT NULL,
    PRIMARY KEY (MarketCode, TRZNum, origTRZNum)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

当 C 程序写入数据库时​​,我得到的结果是垃圾。我在不应该的地方得到重复值,结果乱序,列看起来在不同的地方切换等等。我已经玩了几天了,我认为它与主键有关.

我认为这是因为我创建了另一个具有自动递增整数主键的表,这将我的其他值释放为普通的旧表值。我创建了这样的表:

CREATE TABLE halfcomp
(
    Ind int(11) auto_increment NOT NULL,
    MarketCode varchar(20) DEFAULT NULL,
    TRZNum varchar(20) DEFAULT NULL,
    origTRZNum varchar(20) DEFAULT NULL,
    PRIMARY KEY (Ind)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

当我使用相同的 C 代码写入此表时,结果非常完美。没有重复值,没有混合列。但问题是,我需要 MarketCode、TRZNum 和 origTRZNum 列作为主键。我是否不恰当地创建了我的第一个表?我很茫然。

我已经用细齿梳完成了 C 程序,它看起来不错,并且相同的 C 代码在其他表上也可以工作。但是有了这张表,它就不起作用了。任何见解将不胜感激!谢谢!

编辑:我已经更新了代码。我不再随意使用 malloc 或 sprintf,我将缓冲区值直接设置为我的字符串变量的值。我也尝试在字符串的末尾添加一个“\0”。到目前为止,没有任何帮助,我得到了同样不稳定的行为。只有当值是主键时才会发生这种情况,否则它们会正确加载。我必须假设 C 在开始时将数据库信息传递给我时在字符串的末尾添加了一个空终止符,并且在处理数据时我使用 strcpy 来回传递值,所以我必须相信空终止符已经存在。还有其他想法吗??

int writeDB(void) {
    MYSQL *conn2;
    MYSQL_STMT *stmt2;
    MYSQL_BIND bind2[3];

    char *server = "localhost";
    char *user = "root";
    char *password = "";
    char *database = "thekeydb";
    int port = 3306;

    int k;
    int length = 15000;
    unsigned long mc2_length, mn2_length, om2_length;
    const char *insertStmt2 = "INSERT INTO tempcomp (MarketCode, TRZNum, origTRZNum) VALUES (?,?,?)";

    conn2 = mysql_init(NULL);
    mysql_real_connect(conn2, server,user,password,database, port, NULL, 0);
    stmt2 = mysql_stmt_init(conn2);
    mysql_autocommit(conn2, 0);
    mysql_query(conn2, "START TRANSACTION");

    for (k=0; k < length; k++) {
        memset((void *) bind2, 0, sizeof(bind2));

        mc2_length = strlen(comps[k].marketCode);
        mn2_length = strlen(comps[k].trzNum);
        om2_length = strlen(comps[k].origTrzNum);

        if (mysql_stmt_prepare(stmt2, insertStmt2, strlen(insertStmt2)) != 0) {
            printf("Unable to create new session: Could not prepare statement\n");
        }

        bind2[0].buffer_type = MYSQL_TYPE_STRING;
        bind2[0].buffer = comps[k].marketCode;
        bind2[0].buffer_length = STRING_SIZE;
        bind2[0].is_null = 0;
        bind2[0].length = &mc2_length;

        bind2[1].buffer_type = MYSQL_TYPE_STRING;
        bind2[1].buffer = comps[k].trzNum;
        bind2[1].buffer_length = STRING_SIZE;
        bind2[1].is_null = 0;
        bind2[1].length = &mn2_length;

        bind2[2].buffer_type = MYSQL_TYPE_STRING;
        bind2[2].buffer = comps[k].origTrzNum;
        bind2[2].buffer_length = STRING_SIZE;
        bind2[2].is_null = 0;
        bind2[2].length = &om2_length;

        if (mysql_stmt_bind_param(stmt2, bind2) != 0) {
            printf("Unable to create new session: Could not bind parameters");
        }
        mysql_stmt_execute(stmt2);
        printf("%s, %s, %s, %s\n", comps[k].marketCode, comps[k].trzNum, comps[k].origTrzNum, comps[k].address);
    }

    mysql_query(conn2, "COMMIT");
    mysql_autocommit(conn2, 1);
    mysql_stmt_free_result(stmt2);
    mysql_stmt_close(stmt2);
    mysql_close(conn2);

    return 1;
}

【问题讨论】:

    标签: mysql c key


    【解决方案1】:

    几乎可以肯定,您不是null 终止您的字符串。如果您将字符串作为参数传递给 libmysqlclient 中的准备好的语句,则必须指定字符串的长度。如果您的字符串不是null 终止的,并且您使用strlen() 将长度传递给MYSQL_BIND 结构,或者您将1 将它们传递给sprintf() 或类似的以构建查询,则预期会出现像您的 porgram 行为这样的意外行为。


    1如果你真的在这样做,不要这样做,而是使用准备好的语句。

    【讨论】:

    • LOL 我同时使用 strlen() 和 sprintf() 来查找长度并将参数传递给绑定。你怎么能确定你是空终止你的字符串?我也是 C 新手,所以我不知道如何强制我的字符串为空终止。
    • 我添加了代码,以便您可以看到我是如何传递字符串的。我通过调用数据库获取数据来完成所有这些工作。我填充了一个二维数组(结构的结构)来保存所有数据。引用这个全局数组是我将字符串传递给绑定的方式。我是否需要以某种方式将某些内容连接到字符串的末尾?
    • 在使用 sprintf 和 strlen 等函数之前,我曾尝试使用 strcat() 将“\0”添加到字符串的末尾,但我仍然遇到同样的不稳定行为。当我在开始时执行查询以获取所有数据时,我获取每一行,然后将行数据传递给我的变量。我会认为,如果它们是字符串,它们会被 API 传递,并且字符串中已经包含空值,不是吗?我真的很想相信你的空终止理论是正确的,但我不相信。我一定还是做错了什么。
    • 你不需要sprintf() 任何东西,而且你显然malloc() 任意大小,你应该malloc() 精确大小。
    • 是的。我为 malloc() 使用任意大小。我会改变的。两个问题(1)我是否需要在我的字符串中添加一个终止 null 或者我应该假设它们已经存在? (2) 对于绑定时的buffer_length,我使用任意值50来容纳传递给数据库的所有字符串的长度。它需要是一个精确的值吗?
    【解决方案2】:

    为了社区,我必须诚实地回答这个问题,尽管我承认我这样做很尴尬。在这一点上,我相信我一直在正确地将数据写入数据库,但是为了比较而错误地查看了数据库数据。当我将数据打印到屏幕上时,我正在查看三列。第一个是完全重复的市场代码,第二个是一个相当随机的代码,第三个是一个以 5 或 8 个或其他为一组重复的代码。当我执行“SELECT * FROM tempcomp”来查看数据时,第二列会重复,但会以较小的组(如 2 或 3)重复,第三列看起来是随机的。我现在相信 MySQL Workbench 在将数据呈现给我之前以某种方式对数据进行了排序。因此,数据库显示与程序显示的不同。当我使用 auto_incrementing 整数作为主键时,数据库以与屏幕显示相同的顺序显示数据。但是当我的三列是主键时,它对输出的排序不同。试图弄清楚这一点已经痛苦了 3 天,并且发现我的代码实际上没有错误至少可以说是令人尴尬的。

    【讨论】:

      猜你喜欢
      • 2016-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-22
      • 1970-01-01
      • 2015-07-08
      相关资源
      最近更新 更多