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的说明还同时可以介绍该协议本身可能实现的功能。其他连接器,例如JDBC,ODBC和Node.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 = ®date_ind;
bind[2].buffer_type = MYSQL_TYPE_DATETIME;
bind[2].buffer = ®date;
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(®date, 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] = ®date[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] = ®date[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有多个优点,它在编程上比单个数组字符串更易于处理,尤其是如果后者包含多行数据;对齐包含在类或结构体中的数据也更加容易,从而实现更好的代码集成。最后,性能要好一些,尤其是当有许多行数据要插入的时候。