当前位置: 首页>>技术教程>>正文


C语言MariaDB批量INSERT

有几种方法可以将数据加载到MariaDB。对于文件,LOAD DATA INFILE /LOAD DATA LOCAL INFILE两种基本方法都可以使用,速度都非常快。对于字段信息,可以使用insert语句将数组传递给MariaDB Server,如下所示:


INSERT (column1,column2) VALUES(<column 1 row 1>, <column 2 row 1>),(<column 1 row 2>, <column 2 row 2>)

这没什么特别的,但是我们将在本文中介绍使用数组进行INSERT的另一种方法,该方法使用MariaDB API将程序中的数组传递给MariaDB。这中方法将数据加载到MariaDB的速度非常快。

首先,让我们看一下我们使用C编程API来访问mariadb服务器。之所以使用C API是因为该API是围绕MariaDB协议的精简包,因此对C API的说明还同时可以介绍该协议本身可能实现的功能。其他连接器,例如JDBCODBCNode.js具有各种功能级别,在某些情况下还具有与MariaDB进行交互的其他方式,但这仅发生在连接器本身内部。

下面介绍的一种API是基于文本的,这是原始的MariaDB API。在此API中,所有数据均以文本形式发送和接收。让我们先看一个示例Table。

CREATE TABLE customers(id INTEGER NOT NULL PRIMARY KEY,
cust_name VARCHAR(255),
cust_regdate DATETIME NOT NULL,
cust_numorders INTEGER NOT NULL);

现在,让我们看一个简单的程序,该程序使用基于文本的原始API向该表中插入一些行:

#include <stdio.h>
#include <mysql.h>

int main(int argc, char *argv[])
{
   MYSQL *conn;

   conn = mysql_init(NULL);
   if(mysql_real_connect(conn, "localhost", "root", NULL, "blog", 3306,
     "/var/lib/mysql/mysql.sock", CLIENT_INTERACTIVE) == NULL)
   {
      fprintf(stderr, "Error: %s\n", mysql_error(conn));
      return 1;
   }

   if(mysql_query(conn, "INSERT INTO customers VALUES(1, 'Joe Bloggs',"
     "'2019-03-05 14:30:00', 0)") != 0)
   {
      fprintf(stderr, "Error: %s\n", mysql_error(conn));
      return 1;
   }

   if(mysql_query(conn, "INSERT INTO customers VALUES(2, 'Homer Simpson',"
     "'2019-04-12 09:15:00', 0)") != 0)
   {
      fprintf(stderr, "Error: %s\n", mysql_error(conn));
      return 1;
   }

   mysql_close(conn);
   return 0;
}

看起来很简单,首先初始化连接句柄并进行连接,然后使用2条INSERT语句插入两行。我们传递的所有列,无论是字符串,整数还是日期都表示为字符串。我们也可以通过在单个SQL语句中传递所有行来使这种INSERT更有效,如下所示:

#include <stdio.h>
#include <mysql.h>

int main(int argc, char *argv[])
{
   MYSQL *conn;

   conn = mysql_init(NULL);
   if(mysql_real_connect(conn, "localhost", "root", NULL, "blog", 3306,
     "/var/lib/mysql/mysql.sock", CLIENT_INTERACTIVE) == NULL)
   {
      fprintf(stderr, "Error: %s\n", mysql_error(conn));
      return 1;
   }

   if(mysql_query(conn, "INSERT INTO customers VALUES(1, 'Joe Bloggs',"
     "'2019-03-05 14:30:00', 0),(2, 'Homer Simpson',"
     "'2019-04-12 09:15:00', 0)") != 0)
   {
      fprintf(stderr, "Error: %s\n", mysql_error(conn));
      return 1;
   }

   mysql_close(conn);
   return 0;
}

MariaDB API中的Prepared语句

Prepared语句API与基于文本的API不同:首先,我们不将数据作为SQL语句的一部分传递,而是在SQL语句中包含要在其中放置数据的占位符;然后将这些占位符与程序变量相关联,此过程称为捆绑,变量中才是实际数据。

相同的SQL语句只需要准备(Prepare)一次,在此之后,我们可以执行多次,只需将程序变量数据更改即可。绑定过程不仅需要知道对其绑定的变量的引用,而且还需要知道一些其他信息,例如所引用的数据类型、数据长度以及所谓的指示变量。其中指示变量补充说明了所引用的数据变量的信息,例如它是否为NULL、所引用的字符串是否为NULL终止,或者是否将长度作为字符串的实际长度。示例如下:

#include <stdio.h>
#include <string.h>
#include <mysql.h>

int main(int argc, char *argv[])
{
   MYSQL *conn;
   MYSQL_STMT *stmt;
   int id;
   char id_ind;
   char name[256];
   char name_ind;
   long name_len;
   MYSQL_TIME regdate;
   char regdate_ind;
   int numorders;
   char numorders_ind;
   MYSQL_BIND bind[4];

   conn = mysql_init(NULL);
   if(mysql_real_connect(conn, "localhost", "root", NULL, "blog", 3306,
     "/var/lib/mysql/mysql.sock", CLIENT_INTERACTIVE) == NULL)
   {
      fprintf(stderr, "Error: %s\n", mysql_error(conn));
      return 1;
   }

   stmt = mysql_stmt_init(conn);
   if(mysql_stmt_prepare(stmt, "INSERT INTO customers VALUES(?, ?, ?, ?)", -1) != 0)
   {
      fprintf(stderr, "Error: %s\n", mysql_stmt_error(stmt));
      return 1;
   }

   memset(bind, 0, sizeof(bind));
   bind[0].u.indicator = &id_ind;
   bind[0].buffer_type = MYSQL_TYPE_LONG;
   bind[0].buffer = &id;
   bind[1].u.indicator = &name_ind;
   bind[1].buffer_type = MYSQL_TYPE_STRING;
   bind[1].buffer = name;
   bind[1].length = &name_len;
   name_len = -1;
   bind[2].u.indicator = &regdate_ind;
   bind[2].buffer_type = MYSQL_TYPE_DATETIME;
   bind[2].buffer = &regdate;
   bind[3].u.indicator = &numorders_ind;
   bind[3].buffer_type = MYSQL_TYPE_LONG;
   bind[3].buffer = &numorders;

   if(mysql_stmt_bind_param(stmt, bind) != 0)
   {
      fprintf(stderr, "Error: %s\n", mysql_stmt_error(stmt));
      return 1;
   }
   id_ind = regdate_ind = numorders_ind = STMT_INDICATOR_NONE;
   name_ind = STMT_INDICATOR_NTS;

   id = 1;
   strcpy(name, "Joe Bloggs");
   regdate.year = 2019;
   regdate.month = 3;
   regdate.day = 5;
   regdate.hour = 14;
   regdate.minute = 30;
   numorders = 0;

   if(mysql_stmt_execute(stmt) != 0)
   {
      fprintf(stderr, "Error: %s\n", mysql_stmt_error(stmt));
      return 1;
   }

   id = 2;
   strcpy(name, "Homer Simpson");
   regdate.year = 2019;
   regdate.month = 4;
   regdate.day = 12;
   regdate.hour = 9;
   regdate.minute = 15;
   numorders = 0;

   if(mysql_stmt_execute(stmt) != 0)
   {
      fprintf(stderr, "Error: %s\n", mysql_stmt_error(stmt));
      return 1;
   }

   mysql_close(conn);
   return 0;
}

批量加载–带输入数组的Prepare语句

如果要一次性插入两行或更多行,需要准备并执行以下操作:

INSERT INTO customers VALUES(?, ?, ?, ?),(?, ?, ?, ?)

那么,按照示例中Prepare语句的用法,我们需要绑定8个程序变量,这似乎并不十分灵活,对吧?您将需要根据要插入的行数准备不同的语句,这太笨拙。实际上有更好的方法,即使用数组绑定。也就是每个绑定程序变量都是一个值数组,这样可以用一条语句插入任意数量的行。还是用一个示例来说明具体做法:

#include <stdio.h>
#include <string.h>
#include <mysql.h>

int main(int argc, char *argv[])
{
   MYSQL *conn;
   MYSQL_STMT *stmt;
   int id[2];
   char id_ind[2];
   char *nameptr[2];
   char name[2][256];
   char name_ind[2];
   long name_len[2];
   MYSQL_TIME *regdateptr[2];
   MYSQL_TIME regdate[2];
   char regdate_ind[2];
   int numorders[2];
   char numorders_ind[2];
   MYSQL_BIND bind[4];
   unsigned int numrows;

   conn = mysql_init(NULL);
   if(mysql_real_connect(conn, "localhost", "root", NULL, "blog", 3306,
     "/var/lib/mysql/mysql.sock", CLIENT_INTERACTIVE) == NULL)
   {
      fprintf(stderr, "Error: %s\n", mysql_error(conn));
      return 1;
   }

   stmt = mysql_stmt_init(conn);
   if(mysql_stmt_prepare(stmt, "INSERT INTO customers VALUES(?, ?, ?, ?)", -1) != 0)
   {
      fprintf(stderr, "Error: %s\n", mysql_stmt_error(stmt));
      return 1;
   }

   memset(bind, 0, sizeof(bind));
   bind[0].u.indicator = id_ind;
   bind[0].buffer_type = MYSQL_TYPE_LONG;
   bind[0].buffer = id;
   bind[1].u.indicator = name_ind;
   bind[1].buffer_type = MYSQL_TYPE_STRING;
   bind[1].buffer = nameptr;
   bind[1].length = name_len;
   bind[2].u.indicator = regdate_ind;
   bind[2].buffer_type = MYSQL_TYPE_DATETIME;
   bind[2].buffer = regdateptr;
   bind[3].u.indicator = numorders_ind;
   bind[3].buffer_type = MYSQL_TYPE_LONG;
   bind[3].buffer = numorders;

   if(mysql_stmt_bind_param(stmt, bind) != 0)
   {
      fprintf(stderr, "Error: %s\n", mysql_stmt_error(stmt));
      return 1;
   }

   memset(&regdate, 0, sizeof(regdate));

   id_ind[0] = regdate_ind[0] = numorders_ind[0] = STMT_INDICATOR_NONE;
   name_ind[0] = STMT_INDICATOR_NTS;
   id[0] = 1;
   nameptr[0] = name[0];
   strcpy(name[0], "Joe Bloggs");
   name_len[0] = 256;
   regdateptr[0] = &regdate[0];
   regdate[0].year = 2019;
   regdate[0].month = 3;
   regdate[0].day = 5;
   regdate[0].hour = 14;
   regdate[0].minute = 30;
   numorders[0] = 0;

   id_ind[1] = regdate_ind[1] = numorders_ind[1] = STMT_INDICATOR_NONE;
   name_ind[1] = STMT_INDICATOR_NTS;
   id[1] = 2;
   nameptr[1] = name[1];
   strcpy(name[1], "Homer Simpson");
   name_len[1] = 256;
   regdateptr[1] = &regdate[1];
   regdate[1].year = 2019;
   regdate[1].month = 4;
   regdate[1].day = 12;
   regdate[1].hour = 9;
   regdate[1].minute = 15;
   numorders[1] = 0;

   numrows = 2;
   mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &numrows);
   if(mysql_stmt_execute(stmt) != 0)
   {
      fprintf(stderr, "Error: %s\n", mysql_stmt_error(stmt));
      return 1;
   }

   mysql_close(conn);
   return 0;
}

这里有几个要注意的要点。首先,当我们绑定到数组时,任何字符类型为char *字符串或MYSQL_TIME的数据类型都必须是指针数组,您可以在上面的代码中看到这一点。这虽然使代码看起来有些复杂,但是也算是是一个优点,因为绑定的数据可以位于任意地方(例如,每一行可以是某个类的成员或struct的某个字段)。

结论

将数据作为程序数组加载到MariaDB有多个优点,它在编程上比单个数组字符串更易于处理,尤其是如果后者包含多行数据;对齐包含在类或结构体中的数据也更加容易,从而实现更好的代码集成。最后,性能要好一些,尤其是当有许多行数据要插入的时候。
mariadb

参考资料

本文由《纯净天空》出品。文章地址: https://vimsky.com/article/4497.html,未经允许,请勿转载。