做設(shè)計(jì)去那些網(wǎng)站找素材旅游產(chǎn)品推廣有哪些渠道
mysql訪問(wèn)
- 1.引入MySQL 客戶端庫(kù)
- 2.C/C++ 進(jìn)行增刪改
- 3.查詢的處理細(xì)節(jié)
- 4.圖形化界面訪問(wèn)數(shù)據(jù)庫(kù)
- 4.1下載MYSQL Workbench
- 4.2MYSQL Workbench遠(yuǎn)程連接數(shù)據(jù)庫(kù)
點(diǎn)贊👍👍收藏🌟🌟關(guān)注💖💖
你的支持是對(duì)我最大的鼓勵(lì),我們一起努力吧!😃😃
1.引入MySQL 客戶端庫(kù)
從開(kāi)始到選擇我們用的都是命令行式的mysql訪問(wèn)mysqld,向mysqld下達(dá)我們的指令,其實(shí)在數(shù)據(jù)庫(kù)層面上,連接數(shù)據(jù)庫(kù)的客戶端除了現(xiàn)在命令行式的客戶端,還有圖形化界面、網(wǎng)頁(yè)版的,當(dāng)然也包括語(yǔ)言級(jí)別的庫(kù)或者包幫我們?nèi)ピL問(wèn)數(shù)據(jù)庫(kù)。
下面我們就用C/C++訪問(wèn)數(shù)據(jù)庫(kù)。關(guān)于訪問(wèn)數(shù)據(jù)庫(kù)我們要做兩個(gè)準(zhǔn)備工作,一是創(chuàng)建一個(gè)遠(yuǎn)端或者本地訪問(wèn)的請(qǐng)求也就是創(chuàng)建一個(gè)專門(mén)用來(lái)進(jìn)行用C/C++訪問(wèn)客戶端的賬號(hào)。
reate user 'connector'@'localhost' identified by 'xxx';
然后創(chuàng)建一個(gè)數(shù)據(jù)庫(kù),賦予這個(gè)賬號(hào)對(duì)數(shù)據(jù)庫(kù)中表的所有權(quán)限。
二是安裝C/C++要能訪問(wèn)的庫(kù),這個(gè)庫(kù)有兩種準(zhǔn)備,第一種是從官網(wǎng)下載庫(kù),
下面這個(gè)是用不同語(yǔ)言連接MYSQL,官方提供的庫(kù)。
下載推薦的8.0
我們用的是linux,所以選擇linux系統(tǒng)x86 64位
下載好,上傳到linux,然后解壓一下,mysql-connector是解壓后重命名的
這是解壓后的文件,最重要的就是include 頭文件,還有一個(gè)lib64 庫(kù)
未來(lái)頭文件給我們提供連接mysql的方法,庫(kù) 提供連接mysql的庫(kù)函數(shù)
我們就可以根據(jù)頭文件,直接調(diào)用頭文件的的方法,在編譯連接的時(shí)候把庫(kù)連入進(jìn)來(lái)。如果忘記怎么引入,在Linux哪里基礎(chǔ)IO動(dòng)靜態(tài)庫(kù)就有對(duì)應(yīng)的方法!
但是這種方式不推薦了!之間不是下載mysql,用yum源下載嗎。yum源它會(huì)給我們找到合適的服務(wù)器,客戶端,甚至是開(kāi)發(fā)包。所以我們直接在yum哪里去找進(jìn)行了。
沒(méi)下載過(guò)mysql 可以下一下如果你安裝過(guò)yum對(duì)應(yīng)msyql源的話,如果不會(huì)下請(qǐng)移步于
【MySQL】MySQL在 Linux下環(huán)境安裝
yum install -y mysql-community-server
如果你下載過(guò),你就可以看到這里有對(duì)應(yīng)的頭文件,未來(lái)我們主要用的就是mysql.h
如果你下載過(guò)但是找不到頭文件,執(zhí)行下面的命令,就可以看到了
install -y mysql-devel
mysql庫(kù)在系統(tǒng)默認(rèn)安裝路徑下
開(kāi)發(fā)環(huán)境我們有已經(jīng)準(zhǔn)備好了,接下來(lái)我們學(xué)習(xí)具體的接口。不過(guò)我們通過(guò) mysql_get_client_info() 函數(shù),來(lái)驗(yàn)證我們的引入是否成功。
#include <iostream>
#include <mysql/mysql.h>using namespace std;int main()
{//獲取當(dāng)前客戶端版本信息cout<<"mysql client Version: "<<mysql_get_client_info()<<endl;return 0;
}
如果直接編譯肯定會(huì)報(bào)錯(cuò),說(shuō)的是未定義的引用,也就是這個(gè)mysql_get_client_info()找不到。
之前基礎(chǔ)IO就說(shuō)過(guò),你要保證這個(gè)外來(lái)庫(kù)能被正確連接,gcc/g++默認(rèn)會(huì)選擇C和C++的庫(kù),雖然你的mysql在系統(tǒng)中,系統(tǒng)也會(huì)默認(rèn)會(huì)去/lib64路徑下去找,
但是現(xiàn)在的問(wèn)題是我怎么知道我需要連接那個(gè)庫(kù),盡管能找到這個(gè)庫(kù),但是我怎么知道我們?cè)撨B那個(gè)庫(kù)呢?所以我們需要需要在編譯的時(shí)候就指明需要連那個(gè)庫(kù),
g++ -o mytest test.cc -L/lib64/mysql -lmysqlclient
然后就可以打出來(lái)所有mysql客戶端的版本,說(shuō)明這個(gè)庫(kù)就被重新引入了
以前引入外來(lái)庫(kù)不是還帶上面-I選項(xiàng),今天這里咋沒(méi)有帶。這是因?yàn)間++默認(rèn)會(huì)去include里去找。然后拿著代碼里的
去找,所有不會(huì)報(bào)頭文件找不到的問(wèn)題。如果你非得帶-I,就把前面去掉
g++ -o mytest test.cc -I/usr/include/mysql -L/lib64/mysql -lmysqlclient
接下來(lái)我們我們通過(guò)這個(gè)庫(kù)它內(nèi)部給我提供對(duì)應(yīng)的方法來(lái)讓我們連接mysqld服務(wù)端,然后進(jìn)行訪問(wèn)。
2.C/C++ 進(jìn)行增刪改
接下來(lái)我們先認(rèn)識(shí)mysql常用接口。msyql接口介紹
這里還有mysql庫(kù)為了方便操作mysql,然后提前在庫(kù)中給我創(chuàng)建很多的數(shù)據(jù)結(jié)構(gòu)
msyql的是一套網(wǎng)絡(luò)服務(wù),就注定了實(shí)際在進(jìn)行mysql操作之前比如下達(dá)sql,一定在之前要連接mysql,而在連接mysql之前,我們還要先來(lái)創(chuàng)建一下mysql基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)。要使用庫(kù),必須先進(jìn)行初始化。mysql_init()幫我們創(chuàng)建一個(gè)MYSQL對(duì)象
MYSQL *mysql_init(MYSQL *mysql);
成功返回MYSQL對(duì)象,失敗返回nullptr,用的時(shí)候必須要調(diào)用mysql_close()對(duì)應(yīng)的鏈接。
MYSQL是 C api中一個(gè)非常重要的變量(mysql_init的返回值),里面內(nèi)存非常豐富,有port,dbname,charset等連接基本參數(shù)。它也包含了一個(gè)叫 st_mysql_methods的結(jié)構(gòu)體變量,該變量里面保存著很多函數(shù)指針,這些函數(shù)指針將會(huì)在數(shù)據(jù)庫(kù)連接成功以后的各種數(shù)據(jù)操作中被調(diào)用。
MYSQL實(shí)際上就是一個(gè)結(jié)構(gòu)體里面包含連接mysql相關(guān)保存好的一些屬性。這相當(dāng)于FILE*,用來(lái)標(biāo)識(shí)mysql客戶端一些資源,我們把這些東西可以稱之為句柄。
#include <iostream>
#include <mysql/mysql.h>using namespace std;int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL* my=mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
把數(shù)據(jù)庫(kù)對(duì)應(yīng)的MYSQL結(jié)構(gòu)體對(duì)象創(chuàng)建初始化好,最終不用它了把它釋放。可是要訪問(wèn)mysql之前要先連接數(shù)據(jù)庫(kù),連接數(shù)據(jù)庫(kù)的函數(shù)。
MYSQL *mysql_real_connect(MYSQL *mysql, const char *host,const char *user,const char *passwd,const char *db,unsigned int port,const char *unix_socket,unsigned long clientflag);
第一個(gè)參數(shù)就是剛才創(chuàng)建的MYSQL對(duì)象。連接成功后把連接成功套接字信息保存到MYSQL對(duì)象中。然后要連接的mysql的主機(jī)在哪里,用戶是誰(shuí),密碼是多少,連接那個(gè)數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)的端口號(hào)是多少,剩下設(shè)置為nullptr,0就可以了。
成功時(shí)把我們傳入的MYSQL給我們返回,失敗返回nullptr。
最開(kāi)始創(chuàng)建的只允許在本地主機(jī)連接數(shù)據(jù)庫(kù)的用戶就用上了。
#include <iostream>
#include <mysql/mysql.h>
#include <string>using namespace std;const string host="localhost"; //host="127.0.0.1" 也是可以的
const string user="connector";
const string passwd="xxx";
const string db="conn";
unsigned int port=xxx; //自己的端口號(hào)不要暴露int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL* my=mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if(mysql_real_connect(my,host.c_str(),user.c_str(),passwd.c_str(),db.c_str(),port,nullptr,0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}cout<<"connect success"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
自此我們完成了連接mysql的第工作
- 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄
- 進(jìn)行mysql 的connect連接
- 用完mysql之后close
然后就可以對(duì)mysql下達(dá)sql指令了。這個(gè)下達(dá)sql指令的函數(shù)是mysql_query對(duì)數(shù)據(jù)庫(kù)下達(dá)指令。第一個(gè)參數(shù)就是剛才的MYSQL對(duì)象,第二個(gè)參數(shù)就是下達(dá)的sql指令。以前我們?cè)诿钚袑?xiě)單sql后面會(huì)帶;,但是在這里并不需要帶;or \g,成功時(shí)返回0,失敗時(shí)返回非0。
在此之前可以自己先創(chuàng)建一個(gè)表
create table user(id bigint primary key auto_increment,name varchar(32) not null,age int not null,telphone varchar(32) unique);
之前我們學(xué)過(guò)一個(gè)sql指令,查看正在連接數(shù)據(jù)庫(kù)的用戶
show processlist;
可以看到我們C/C++庫(kù)確實(shí)連接到數(shù)據(jù)庫(kù)了
int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;string sql;while (true){cout << " mysql> ";if (!getline(cin, sql) || sql == "quit"){cout << "Bye" << endl;break;}int n = mysql_query(my, sql.c_str());if (n == 0){cout << sql << " success" << endl;}else{cerr << sql << " error" << endl;return 3;}}// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
這里名字我們先輸入英文,具體原因等會(huì)說(shuō)。下達(dá)的這個(gè)sql執(zhí)行可帶;或者不帶;。然后看到我們確實(shí)把信息插入到數(shù)據(jù)庫(kù)了。我們其實(shí)也可以自己搞一個(gè)mysql的客戶端。但是實(shí)際上我們也不會(huì)這樣做,別人自己有客戶端。我們直接向數(shù)據(jù)庫(kù)下達(dá)sql指令就行了。
更新
int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}string sql="update user set name='jim' where id=2";int n=mysql_query(my,sql.c_str());if(n == 0) cout<<sql<<" success"<<endl;else cout<<sql<<" error"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
執(zhí)行一下,看到id=2的名字確實(shí)更新為jim了。
所以未來(lái)我們所寫(xiě)的任何的關(guān)于數(shù)據(jù)庫(kù)方面的程序,你想使用數(shù)據(jù)庫(kù),你可以提前建好數(shù)據(jù)庫(kù)賬號(hào)然后賦權(quán),并且建表。然后編寫(xiě)對(duì)應(yīng)C/C++代碼。然后就可以為你的上層進(jìn)行數(shù)據(jù)層面上的支撐了。
插入
int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}//string sql="update user set name='jim' where id=2";string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";int n=mysql_query(my,sql.c_str());if(n == 0) cout<<sql<<" success"<<endl;else cout<<sql<<" error"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
刪除
int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}//string sql="update user set name='jim' where id=2";//string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";string sql="delete from user where id=1";int n=mysql_query(my,sql.c_str());if(n == 0) cout<<sql<<" success"<<endl;else cout<<sql<<" error"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
單sql本身就是事務(wù),mysql默認(rèn)事務(wù)提交是自動(dòng)的,所以今天你可以用一個(gè)連接訪問(wèn)數(shù)據(jù)庫(kù),也可以創(chuàng)建十個(gè)連接訪問(wèn)數(shù)據(jù)庫(kù),你可以用命令行方式訪問(wèn)數(shù)據(jù)庫(kù),也可以用C/C++訪問(wèn)數(shù)據(jù)庫(kù)。所以數(shù)據(jù)庫(kù)可以存在多個(gè)訪問(wèn),因?yàn)橛惺聞?wù),事務(wù)有ACID能保證原子性等等。所以我們?cè)趹?yīng)用層就不用擔(dān)心業(yè)務(wù)代碼執(zhí)行時(shí)出現(xiàn)問(wèn)題。
mysql_query這個(gè)函數(shù)現(xiàn)在可以增刪改,那查行不行呢?
int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}//string sql="update user set name='jim' where id=2";//string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";//string sql="delete from user where id=1";string sql="select * from user";int n=mysql_query(my,sql.c_str());if(n == 0) cout<<sql<<" success"<<endl;else cout<<sql<<" error"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
別人告訴我成功了啊,然后呢?
在我們數(shù)據(jù)庫(kù)語(yǔ)句中,增刪改是最簡(jiǎn)單的,因?yàn)樵鰟h改只需要保證成功即可,那么增刪改操作一定能完成。而select是最不好處理的,因?yàn)楫?dāng)成功執(zhí)行select語(yǔ)句,只是執(zhí)行完sql第一步,那select后的數(shù)據(jù)在那呢?你是不是要把數(shù)據(jù)給顯示處理或者說(shuō)讓用戶獲取到,所以還需要在做后面的工作。所以select還有后序獲取數(shù)據(jù)包括把數(shù)據(jù)在交給它的調(diào)用層。而不像增刪改執(zhí)行完就完了。
還有一個(gè)問(wèn)題,剛剛在插入的時(shí)候,name插入都是英文,那我們插入中文會(huì)怎么樣呢?
int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}//string sql="update user set name='jim' where id=2";//string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";string sql="insert into user (name,age,telphone) values ('張三',18,11187654321)";//string sql="delete from user where id=1";//string sql="select * from user";int n=mysql_query(my,sql.c_str());if(n == 0) cout<<sql<<" success"<<endl;else cout<<sql<<" error"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
我們插入中文時(shí)亂碼了!
亂碼問(wèn)題怎么解決?我們的數(shù)據(jù)庫(kù)編碼格式都配過(guò)是utf8的。出現(xiàn)亂碼問(wèn)題,一定是因?yàn)榭蛻舳撕头?wù)端對(duì)編碼問(wèn)題沒(méi)有形成一致。比如說(shuō)服務(wù)端用的是utf8,客戶端用的是latin1。因?yàn)槲覀償?shù)據(jù)庫(kù)編碼都是utf8的,所以一定是客戶端出現(xiàn)了問(wèn)題。
建立好鏈接之后,獲取英文沒(méi)有問(wèn)題,如果獲取中文是亂碼:設(shè)置鏈接的默認(rèn)字符集是utf8,原始默認(rèn)是latin1
int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// 設(shè)置編碼格式mysql_set_character_set(my, "utf8");//string sql="update user set name='jim' where id=2";//string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";string sql="insert into user (name,age,telphone) values ('李四',18,12187654321)";//string sql="delete from user where id=1";//string sql="select * from user";int n=mysql_query(my,sql.c_str());if(n == 0) cout<<sql<<" success"<<endl;else cout<<sql<<" error"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
設(shè)置好編碼,在插入中文就沒(méi)問(wèn)題了。
自此我們就已經(jīng)能夠進(jìn)行mysql對(duì)象初始化,mysql的連接,mysql的關(guān)閉,以及通過(guò)mysql_query向數(shù)據(jù)庫(kù)下達(dá)對(duì)應(yīng)的指令了。下面我們就解決拿到select的數(shù)據(jù)問(wèn)題。
3.查詢的處理細(xì)節(jié)
查sql本身能夠被正確執(zhí)行,但是執(zhí)行之后怎么把查的數(shù)據(jù)交給上層返回給客戶或者把它的數(shù)據(jù)顯示處理呢?那在執(zhí)行select的時(shí)候要把select的結(jié)果通過(guò)mysql_query查完之后把結(jié)果應(yīng)該讓我們?cè)趺传@取到呢?實(shí)際上mysql_query在執(zhí)行select的時(shí)候會(huì)把結(jié)果保存到MYSQL這個(gè)句柄中。這個(gè)句柄是一個(gè)結(jié)構(gòu)體它本身里面會(huì)包含對(duì)應(yīng)的保存數(shù)據(jù)的緩存區(qū)區(qū)域。但是這部分?jǐn)?shù)據(jù)依舊要被我們提取出來(lái)的。我們通過(guò)mysql_store_result這個(gè)函數(shù)來(lái)將結(jié)果進(jìn)行轉(zhuǎn)儲(chǔ)。這個(gè)函數(shù)提取并且轉(zhuǎn)儲(chǔ)到結(jié)果集中。會(huì)把結(jié)果按照條目放置好。
是從MYSQL這個(gè)句柄中把結(jié)果轉(zhuǎn)儲(chǔ)到MYSQL_RES這個(gè)結(jié)構(gòu)中。
把查詢結(jié)果按照行為單位,全部都放進(jìn)結(jié)果集中。
成功返回MYSQL_RES對(duì)象,失敗返回nullptr。也可以采用mysql_errno和mysql_error來(lái)進(jìn)行判斷出錯(cuò)原因。
下面我們看看怎么用這個(gè)函數(shù)
首先先將結(jié)果轉(zhuǎn)儲(chǔ)到MYSQL_RES結(jié)構(gòu)體中。
int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;// 設(shè)置編碼格式mysql_set_character_set(my, "utf8");string sql="select * from user";int n=mysql_query(my,sql.c_str());if(n == 0) {cout<<sql<<" success"<<endl;}else{cout<<sql<<" error"<<endl;return 3;} //轉(zhuǎn)儲(chǔ)MYSQL_RES* res=mysql_store_result(my);if(res == nullptr){cerr<<"mysql_store_result error"<<endl;return 4;}// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
那MYSQL_RES這個(gè)結(jié)構(gòu)體應(yīng)該如何理解呢?
首先我們要知道我們查出來(lái)的是一種表結(jié)構(gòu),表結(jié)構(gòu)中行列關(guān)系是我們打印出來(lái)的,尤其是這些分割符,這些東西我們自己打印出來(lái)的,并不是在mysql在表中真實(shí)存在的。
在mysql表中其實(shí)存在兩類信息,一類是存儲(chǔ)表結(jié)構(gòu)中的列屬性或者列名稱,還有一類是存儲(chǔ)表中的內(nèi)容。
我們先看內(nèi)容,這些內(nèi)容以一定格式先放到MYSQL結(jié)構(gòu)體對(duì)象內(nèi)部,然后在轉(zhuǎn)儲(chǔ)到MYSQL_RES結(jié)構(gòu)中。轉(zhuǎn)儲(chǔ)是為了更好的便于做二次處理。
在表中查出來(lái)的數(shù)據(jù),就一定是在MYSQL內(nèi)部在內(nèi)存中保存這部分內(nèi)容的。mysql將所有數(shù)據(jù),讀取出來(lái)的時(shí)候,全部都當(dāng)作字符串。 雖然以前它們什么int,char等,但是一旦把數(shù)據(jù)讀到自定義緩存區(qū)中的時(shí)候,那么它一定是把所有數(shù)據(jù)都當(dāng)作字符串來(lái)看待。以前約束類型只是在插入的時(shí)候要處理,讀取出來(lái)當(dāng)當(dāng)作字符串處理。
然后MYSQL_RES要把結(jié)果歸置一下,我可以把MYSQL_RES想象成一個(gè)存著char**的指針數(shù)組。數(shù)組大小代表篩選出來(lái)的數(shù)據(jù)記錄有多少行。一條記錄里面有多列,每一列都是字符串。
然后這個(gè)數(shù)組中每一個(gè)char** 指針都指向一個(gè)存訪char類型的指針數(shù)組,指向的是這個(gè)數(shù)組中首個(gè)元素的地址。char** 的指針數(shù)組大小代表的是數(shù)據(jù)記錄有多少行,而每一行char 的指針數(shù)組里的char* 依次指向一條記錄的每一列。
所以未來(lái)想提取一整行,想提取一整行中的列,我可以按照行列去提取!
我們可以把MYSQL_RES當(dāng)作一個(gè)char**xx[]數(shù)組去理解,或者當(dāng)作一個(gè)char*[][]數(shù)組去理解都可以,怎么方便怎么來(lái)??v向去遍歷每一行,橫向去遍歷一行中每一列。就可以找所有查詢結(jié)果都找到。
未來(lái)MYSQL_RES就以這樣的方式把查詢的結(jié)果保存起來(lái)。
那結(jié)果最終怎么遍歷呢?首先我們要知道這個(gè)轉(zhuǎn)儲(chǔ)的結(jié)果有多少行,有多少列!然后按照行列去遍歷。
獲取結(jié)果行數(shù)mysql_num_rows
參數(shù)是MYSQL_RES,從MYSQL_RES結(jié)果集中拿結(jié)果有多少行。
my_ulonglong mysql_num_rows(MYSQL_RES *res);
獲取結(jié)果列數(shù)mysql_num_fields
unsigned int mysql_num_fields(MYSQL_RES *res);
int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;// 設(shè)置編碼格式mysql_set_character_set(my, "utf8");string sql="select * from user";int n=mysql_query(my,sql.c_str());if(n == 0) {cout<<sql<<" success"<<endl;}else{cout<<sql<<" error"<<endl;return 3;} MYSQL_RES* res=mysql_store_result(my);if(res == nullptr){cerr<<"mysql_store_result error"<<endl;return 4;}my_ulonglong row=mysql_num_rows(res);my_ulonglong col=mysql_num_fields(res);cout << "行: " << rows << endl;cout << "列: " << cols << endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
目前這個(gè)表不就是3行4列嗎
那結(jié)果怎么拿?遍歷每一行每一列。一行一列的把結(jié)果都拿到。
mysql為了很好的支持遍歷,它內(nèi)部還提供一種數(shù)據(jù)結(jié)構(gòu)叫做MYSQL_ROW,每次拿到一行。
獲取結(jié)果內(nèi)容mysql_fetch_row
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
這個(gè)函數(shù)就向以前學(xué)過(guò)的迭代器,當(dāng)我們第一次調(diào)用mysql_fetch_row它內(nèi)部會(huì)為我們維護(hù)當(dāng)前遍歷的第一行,把第一行的結(jié)果集以MYSQL_ROW形式返回。當(dāng)遍歷第二行的時(shí)候,只需要在調(diào)用一次mysql_fetch_row,不用自己維護(hù)行下標(biāo),它會(huì)自動(dòng)指向下一行。就如迭代器一樣,只需要按照特定的次數(shù)進(jìn)行遍歷調(diào)用就會(huì)依次把元素遍歷一遍,特別像迭代器。
可以看到MYSQL_ROW其實(shí)就是一個(gè)char指針,因?yàn)橹挥衏har才能指向第一行第二行等等。
int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;// 設(shè)置編碼格式mysql_set_character_set(my, "utf8");string sql = "select * from user";int n = mysql_query(my, sql.c_str());if (n == 0){cout << sql << " success" << endl;}else{cout << sql << " error" << endl;return 3;}MYSQL_RES *res = mysql_store_result(my);if (res == nullptr){cerr << "mysql_store_result error" << endl;return 4;}//下面都是和結(jié)果集有關(guān)的, MYSQL_RESmy_ulonglong rows = mysql_num_rows(res);my_ulonglong cols = mysql_num_fields(res);cout << "行: " << rows << endl;cout << "列: " << cols << endl;//拿結(jié)果 for(int i=0;i<rows;++i){MYSQL_ROW line=mysql_fetch_row(res);for(int j=0;j<cols;++j){cout<<line[j]<<"\t"; }cout<<endl;}// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
現(xiàn)在我們就可以把select的結(jié)果都拿到了!
剛才說(shuō)了在獲取msyql表內(nèi)容的時(shí)候,我們除了獲取表格中內(nèi)容,它的列屬性我們也想獲取。想知道它是那一列的,或者想知道當(dāng)前查的是那張表,那個(gè)數(shù)據(jù)庫(kù)。怎么辦呢?我們有另一個(gè)接口。
獲取列名mysql_fetch_fields
MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res);
與mysql_fetch_fields相對(duì)的還有一個(gè)mysql_fetch_field接口,mysql_fetch_fields以數(shù)組形式返回的是所有的列屬性對(duì)應(yīng)的結(jié)構(gòu)體。每一列屬性都是一個(gè)結(jié)構(gòu)體。
mysql_fetch_field可以讓你依次遍歷所有列,以列為單位,第一次是第一列,第二次是第二列,也就像迭代器。
這里我們就不麻煩了,直接一次獲得所有列屬性。然后可以遍歷所有列把列名稱打印出來(lái)。
int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;// 設(shè)置編碼格式mysql_set_character_set(my, "utf8");string sql = "select * from user";int n = mysql_query(my, sql.c_str());if (n == 0){cout << sql << " success" << endl;}else{cout << sql << " error" << endl;return 3;}MYSQL_RES *res = mysql_store_result(my);if (res == nullptr){cerr << "mysql_store_result error" << endl;return 4;}//下面都是和結(jié)果集有關(guān)的, MYSQL_RESmy_ulonglong rows = mysql_num_rows(res);my_ulonglong cols = mysql_num_fields(res);cout << "行: " << rows << endl;cout << "列: " << cols << endl;//屬性MYSQL_FIELD* fields=mysql_fetch_fields(res);for(int i=0;i<cols;++i){cout<<fields[i].name<<"\t";}cout<<endl;//內(nèi)容for(int i=0;i<rows;++i){MYSQL_ROW line=mysql_fetch_row(res);for(int j=0;j<cols;++j){cout<<line[j]<<"\t"; }cout<<endl;}// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
自此列名稱我們就拿到了。
如果你將來(lái)想獲取什么屬性就直接去獲取。
int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;// 設(shè)置編碼格式mysql_set_character_set(my, "utf8");string sql = "select * from user";int n = mysql_query(my, sql.c_str());if (n == 0){cout << sql << " success" << endl;}else{cout << sql << " error" << endl;return 3;}MYSQL_RES *res = mysql_store_result(my);if (res == nullptr){cerr << "mysql_store_result error" << endl;return 4;}//下面都是和結(jié)果集有關(guān)的, MYSQL_RESmy_ulonglong rows = mysql_num_rows(res);my_ulonglong cols = mysql_num_fields(res);cout << "行: " << rows << endl;cout << "列: " << cols << endl;//屬性MYSQL_FIELD* fields=mysql_fetch_fields(res);for(int i=0;i<cols;++i){cout<<fields[i].name<<"\t";}cout<<endl;//內(nèi)容for(int i=0;i<rows;++i){MYSQL_ROW line=mysql_fetch_row(res);for(int j=0;j<cols;++j){cout<<line[j]<<"\t"; }cout<<endl;}cout<<fields[0].db<<" "<<fields[0].table<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
最終我們就把mysql的查詢結(jié)果獲取到,然后轉(zhuǎn)儲(chǔ)到結(jié)果集,然后從結(jié)果集中提取行列,還有其他屬性和內(nèi)容?,F(xiàn)在我們對(duì)增刪查改就有一個(gè)完整的認(rèn)識(shí)了。
但是還有一個(gè)細(xì)節(jié)mysql_store_result會(huì)把查詢結(jié)果由MYSQL轉(zhuǎn)儲(chǔ)到MYSQL_RES里,MYSQL_RES一定要為我們malloc一大堆空間,所以我們一定要記的 free(result),不然是肯定會(huì)造成內(nèi)存泄漏的。我們不能直接用C的free或者C++的delete,而用的是mysql庫(kù)提供的這個(gè)函數(shù),把結(jié)果集中空間釋放掉。
int main()
{// 1. 初始化mysql,構(gòu)建mysql對(duì)象,得到mysql訪問(wèn)句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數(shù)據(jù)庫(kù)if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;// 設(shè)置編碼格式mysql_set_character_set(my, "utf8");string sql = "select * from user";int n = mysql_query(my, sql.c_str());if (n == 0){cout << sql << " success" << endl;}else{cout << sql << " error" << endl;return 3;}MYSQL_RES *res = mysql_store_result(my);if (res == nullptr){cerr << "mysql_store_result error" << endl;return 4;}//下面都是和結(jié)果集有關(guān)的, MYSQL_RESmy_ulonglong rows = mysql_num_rows(res);my_ulonglong cols = mysql_num_fields(res);cout << "行: " << rows << endl;cout << "列: " << cols << endl;//屬性MYSQL_FIELD* fields=mysql_fetch_fields(res);for(int i=0;i<cols;++i){cout<<fields[i].name<<"\t";}cout<<endl;//內(nèi)容for(int i=0;i<rows;++i){MYSQL_ROW line=mysql_fetch_row(res);for(int j=0;j<cols;++j){cout<<line[j]<<"\t"; }cout<<endl;}cout<<fields[0].db<<" "<<fields[0].table<<endl;//釋放結(jié)果集mysql_free_result(res);// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
可以看到對(duì)于讀取的處理其實(shí)是挺麻煩的。
我們常用的接口就這些,對(duì)于一般數(shù)據(jù)庫(kù)讀取沒(méi)有任何問(wèn)題。
最后,mysql C api還支持事務(wù)等常用操作,大家下來(lái)自行了解:
my_bool STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode);
my_bool STDCALL mysql_commit(MYSQL * mysql);
my_bool STDCALL mysql_rollback(MYSQL * mysql)
目前為止所有連接數(shù)據(jù)庫(kù)的操作,我們已經(jīng)有第二種方案。以前就是mysql命令行式的、剛剛就是mysql的C/C++的連接方案,我們還可以通過(guò)圖形化界面連接數(shù)據(jù)庫(kù)。
4.圖形化界面訪問(wèn)數(shù)據(jù)庫(kù)
這里推薦幾個(gè)好用的圖形化界面。
Nacicat是一個(gè)桌面版的mysql數(shù)據(jù)庫(kù)管理和開(kāi)發(fā)工具,以圖形化界面顯示數(shù)據(jù)庫(kù)的操作。雖然這個(gè)工具非常好用,但遺憾的是,這個(gè)軟件是收費(fèi)的。可以自己在網(wǎng)上找找它的破解版。
SQLyog 也是一個(gè)易于使用的、快速而簡(jiǎn)潔的圖形化管理MYSQL數(shù)據(jù)庫(kù)的工具,它能夠在任何地點(diǎn)有效地管理你的數(shù)據(jù)庫(kù)。但同樣的,雖然它用起來(lái)很舒服,但它也是收費(fèi)的
MYSQL Workbench是mysql官方提供的數(shù)據(jù)庫(kù)管理和開(kāi)發(fā)工具,支持圖形化界面。雖然它在使用上并沒(méi)有上面的Navicat和SQLyog好用,但優(yōu)點(diǎn)就是它是免費(fèi)的。這里我們重點(diǎn)就用它。
4.1下載MYSQL Workbench
我們可以直接去mysql官網(wǎng)上去找到它,然后下載。
往下翻找到這個(gè)然后進(jìn)去
找到MySQL Workbench然后進(jìn)去
選擇對(duì)應(yīng)的操作系統(tǒng),直接下載就行了
下好之后運(yùn)行就是這個(gè)樣子
4.2MYSQL Workbench遠(yuǎn)程連接數(shù)據(jù)庫(kù)
MYSQL Workbench也是一個(gè)客戶端,說(shuō)白了它和我們自己寫(xiě)的C/C++客戶端和曾經(jīng)mysql對(duì)應(yīng)的命令行客戶端是沒(méi)有任何差別的,它也要進(jìn)行網(wǎng)絡(luò)請(qǐng)求,也要進(jìn)行登錄。所以mysql一定要允許這個(gè)用戶進(jìn)行遠(yuǎn)程登錄。
因此我們?cè)趧?chuàng)建一個(gè)允許遠(yuǎn)程登錄的賬號(hào)。
創(chuàng)建一個(gè)允許用戶從任意地點(diǎn)登錄。這個(gè)任意登錄這件事情,雖然我們這樣寫(xiě)了,一般在公司內(nèi)允許那一臺(tái)機(jī)器訪問(wèn)數(shù)據(jù)庫(kù)可以把這個(gè)IP地址確定下來(lái)不用填%,今天我們用的云服務(wù)器,我們內(nèi)網(wǎng)經(jīng)過(guò)NAT路由器轉(zhuǎn)發(fā)IP地址會(huì)發(fā)生改變。本地IP配上去沒(méi)有任何意義,所以這里只能用%。
create user 'workbench'@'%' identified by 'xxx';
然后在給新用戶賦權(quán)。把conn數(shù)據(jù)庫(kù)所有表的權(quán)限都給這個(gè)用戶。
grant all on conn.* to 'workbench'@'%';
然后才能遠(yuǎn)程連接,點(diǎn)擊這里+,配置一下相關(guān)信息
我們主要填的就是IP地址,端口號(hào),和用戶。還可以給這個(gè)連接起個(gè)名字。
配置好,然后點(diǎn)擊下面的,代表測(cè)試一下連接,它會(huì)要求輸入對(duì)應(yīng)的密碼,直接把這個(gè)用戶的密碼輸入就行了。
如果成功連接在點(diǎn)擊ok就是下面這樣,如果連接不上可能是你端口號(hào)沒(méi)有放出來(lái)。自己放一下。如果想登錄的話,點(diǎn)擊一下這個(gè)出來(lái)的東西,然后就登錄進(jìn)去了。
進(jìn)去之后我們可以看到對(duì)應(yīng)表結(jié)構(gòu)、視圖,存儲(chǔ)過(guò)程和函數(shù)這些。我們現(xiàn)在只用關(guān)系表結(jié)構(gòu)就可以了。表結(jié)構(gòu)里面對(duì)應(yīng)的列、索引、外鍵、觸發(fā)器這些。
列里面可以看到對(duì)應(yīng)列的屬性,如id列的主機(jī)和自增長(zhǎng)
下面我們可以在框里寫(xiě)一些sql指令了。寫(xiě)完之后選中或者光標(biāo)放在你寫(xiě)的sql這里然后點(diǎn)擊這個(gè)閃電執(zhí)行它。然后下面就可以看到對(duì)應(yīng)的信息。
雖然光標(biāo)放在后面點(diǎn)擊閃電也可以執(zhí)行,但是它是從上到下開(kāi)始執(zhí)行的,如果只需要執(zhí)行某一個(gè)sql指令,還是要選擇它然后在去執(zhí)行。
接下來(lái)我們可以查一下這個(gè)表,可以看到這里擔(dān)心數(shù)據(jù)太多,默認(rèn)只把0-1000行篩選出來(lái)。
然后可以直接在這里對(duì)數(shù)據(jù)進(jìn)行插入,然后再點(diǎn)擊Apply執(zhí)行一下
Apply會(huì)把剛剛在圖形化界面這里做的動(dòng)作變成sql的語(yǔ)句,然后在點(diǎn)Apply執(zhí)行。
然后在點(diǎn)Finsh,這個(gè)插入就算真正執(zhí)行完成了
然后我們查一下,確實(shí)看到是插入了
接下里我們想把張三這條記錄刪除一下,可以直接在圖形化界面刪除,然后還是和上面一樣的流程Apply。
然后這看到確實(shí)可以刪除。
更新也是一樣在圖形化界面改,然后Apply執(zhí)行一下。我們把李四年齡改成90歲。
看到確實(shí)也是改了
換言之,我們就不用自己寫(xiě)sql了,拿到表結(jié)構(gòu)對(duì)它進(jìn)行增刪查改,直接在里面手改就行了。這就是圖形化界面的好處。
如果想把自己寫(xiě)的一大堆sql指令保存一下,可以ctrl+s保存。形成一個(gè).sql文件。