0、說明
QSqlQuery提供了執行SQL代碼的方法。
QSqlQuery封裝了在QSqlDatabase中查詢、檢索數據的相關函數。它可以用來執行如SELECT、INSERT、UPDATE、DELETE等方法,也可以執行如CREATE TABLE等語句。
順利執行SQL語句后,調用isActive()就會返回true。一個激活的Query一定會產生一個合法的記錄(isValid()返回true),從這個記錄中可以提取到需要的數據。
對某些數據庫而言,如果有一個諸如SELECT的Query是激活狀態,那么當我們調用commit()、rollback()時,相關事務操作會失敗。
歷史查詢記錄可以通過如下函數進行查詢:
next()、previous()、first()、last()、seek()
這些函數允許編輯器隨意瀏覽上一個、下一個、任意一個查詢記錄。如果我們只需要往后查看結果(調用next()),那么可以調用setForwardOnly(),這樣相比隨機存儲就可以存儲大量信息,並且提高查詢效率。
一旦一個激活的Query返回了一個合法記錄,可以通過value()來提取其中的數據。所有SQL查詢到的數據都是QVariant類型。
Query成功后,每個Query都是一條記錄(即一行),可以用while(query.next())來實現查詢每行的目的,每條記錄有多少個value是與SELECT語句中的查詢項數相關的,通過value(i)查詢對應的項,每次查詢到的值都是QVariant類型,通過QVariant的toT()方法轉換為對應的類型(這里T是泛型而不是實際類型),具體見QVaraint官方文檔。
例如:
QSqlQuery query("SELECT country FROM artist"); while(query.next()){ QString country = query.value(0).toString(); doSomething(country); }
為了訪問Query得到的數據,可以使用value( int )方法。Query的結果數據中,索引是從0開始的,因此調用SELECT *進行查詢是不可用的,因為返回順序是不確定的。
為了提高效率,不存在通過name訪問數據的方法(除非預先在SELECT語句中通過names進行查詢)。為了將數據name轉換為索引,需要使用record().indexOf(),例如:
QSqlQuery query(SELECT * FROM artist); int fieldNo = query.record().indexOf("country"); while(query.next()){ QString country = query.value(fieldNo).toString; doSomething(country); }
調用numRowsAffected()可以知道有多少行是被非SELECT查詢影響,調用size()可以知道有多少行是被SELECT查詢影響的。
需要注意的是,在創建QSqlQuery之前,必須先加載SQL Driver並且構建連接。此外,當Query存在時,連接必須維持open狀態,否則QSqlQuery的行為將不會執行。
預定義查詢與帶占位符的查詢
預定義查詢:事先定義好查詢語句,但暫時並不執行,通常是因為其中的項沒有設置,通常與占位符結合使用;
什么是帶占位符的查詢呢?就是在查詢語句中,並不把語句寫完整,而是某些地方填入占位符后,之后把占位符補充完整,就構成了一個完整的查詢。就像下邊這樣:
query.prepare("INSERT INTO person (id, forename, surname) " "VALUES (:id, :forename, :surname)"); query.bindValue(":id", 1001); query.bindValue(":forename", "Bart"); query.bindValue(":surname", "Simpson"); query.exec();
幾種占位符查詢的方式:
①name占位符與name填充
QSqlQuery query; query.prepare("INSERT INTO person (id, forename, surname) " "VALUES (:id, :forename, :surname)"); query.bindValue(":id", 1001); query.bindValue(":forename", "Bart"); query.bindValue(":surname", "Simpson"); query.exec();
②name占位符與pos填充
QSqlQuery query; query.prepare("INSERT INTO person (id, forename, surname) " "VALUES (:id, :forename, :surname)"); query.bindValue(0, 1001); query.bindValue(1, "Bart"); query.bindValue(2, "Simpson"); query.exec();
③?占位符與pos填充
QSqlQuery query; query.prepare("INSERT INTO person (id, forename, surname) " "VALUES (?, ?, ?)"); query.bindValue(0, 1001); query.bindValue(1, "Bart"); query.bindValue(2, "Simpson"); query.exec();
④?占位符與順序填充
QSqlQuery query; query.prepare("INSERT INTO person (id, forename, surname) " "VALUES (?, ?, ?)"); query.addBindValue(1001); query.addBindValue("Bart"); query.addBindValue("Simpson"); query.exec();
1、模塊和載入項
Header: | #include <QSqlQuery> |
qmake: | QT += sql |
2、構造
QSqlQuery(QSqlQuery other) | 構造一個Query的副本 |
QSqlQuery(QSqlDatabase db) | 在db上構造一個Query,如果db不存在則用默認db |
QSqlQuery(QString query = QString(), QSqlDatabase db = QSqlDatabase()) | 用query和db構造一個Query,其中query是SQL語句對應的字符串 如果db為空或者非法,會使用默認db,如果query非空,那么在構造之后它會被執行 |
QSqlQuery(QSqlResult *result) | 用某個查詢結果QSqlResult構造一個QSqlQuery |
3、靜態字段
類型 |
字段 |
取值 |
說明 |
enum | BatchExecutionMode | { ValuesAsRows, ValuesAsColumns } |
Constant | Value | Description |
---|---|---|
QSqlQuery::ValuesAsRows |
0 |
- Updates multiple rows. Treats every entry in a QVariantList as a value for updating the next row. |
QSqlQuery::ValuesAsColumns |
1 |
- Updates a single row. Treats every entry in a QVariantList as a single value of an array type. |
4、成員方法
返回值類型 |
方法 |
說明 |
QSqlQuery & | operator=(QSqlQuery other) | 賦值 |
void | addBindValue(QVariant val, QSql::ParamType paramType = QSql::In) | 用於第四種情況的?占位符 |
int | at() | 返回當前查詢到的記錄的索引,第一個記錄的索引為0 失敗時返回一個負數 |
void | bindValue(QString placeholder, QVariant val, QSql::ParamType paramType = QSql::In) bindValue(int pos,QVariant val, QSql::ParamType paramType = QSql::In) |
①、②、③中占位符,第一個參數是占位符,第二個參數是它的值 |
QVariant QVariant QMap<QString, QVariant> |
boundValue(QString placeholder) boundValue(int pos) |
返回占位符對應的值 返回占位符及其值構成的QMap<QString , QVariant> |
void | clear() | 清除result集合,釋放該Query占有的資源。設置Query狀態為非激活,我們幾乎不需要調用該方法。 |
QSqlDriver * | driver() | 該Query相關的driver |
bool | exec(QString query) |
參數是一個SQL Query語句執行該語句。 Query成功時返回true,並設置query為激活狀態。 當Query執行后,需要不斷調用next()來讓該Query定位到一個合法的記錄處,之后才能提取數據。 當調用exec()時,該Query的last error將被重置。 例子: QSqlQuery query; query.exec("INSERT INTO employee (id, name, salary) " "VALUES (1001, 'Thad Beaumont', 65000)"); |
bool | 執行用prepare()預定義的查詢語句,成功時返回true。 執行時會重置該query的last error。 |
|
bool | execBatch(QSqlQuery::BatchExecutionMode mode = ValuesAsRows) |
|
QString | executedQuery() | 返回上一次成功執行的Query String。 如果是帶占位符的預定義查詢,會在占位符處填充真實值。 |
void | finish() | 告訴Driver不需要再從Query中查詢數據了。 通常不需要調用該方法,但是在重用該Query前調用該方法會釋放資源以保證資源安全。 設置Query為非激活狀態,占位符綁定的值將會維持不變。 |
bool | first() | 提取Query結果的第一條記錄,並將Query定位到該條記錄上。 Query結果必須是激活狀態,並且isSelect()必須返回true,之后才能調用該方法,否則將會返回false並不做任何事情。 |
bool | isActive() | 如果Query是激活狀態則返回true。激活的Query是指成功調用了exec()並且沒有調用finish()之前的Query。 完成查詢之后,可以通過finish()或clear()修改Query為非激活狀態,或者也能直接刪除該QSqlQuery實例。 在提交、回滾事務之前,必須用以上方法來使Query變為非激活狀態。 |
bool | isForwardOnly() | 如果只能在結果集中向前單向查詢,返回true。 |
bool | isNull(int field) isNull(QString name) |
以下幾種情況返回true:1、Query未激活;2、Query未定位到一個合法記錄上;3、結果上沒有指定field;4、field是null。 |
bool | isSelect() | 如果當前Query是SELECT語句返回true |
bool | isValid() | 如果當前Query定位到一個合法記錄上,返回true |
bool | last() | 定位當前Query到最后一條記錄上。 |
QSqlError | lastError() | 返回該Query最近的錯誤信息 |
QVariant | lastInsertId() | 返回最近插入行對應的object ID。 失敗時返回一個非法的QVariant。 如果一次插入了多行,結果將是Undefined。 |
QString | lastQuery() | 返回上一次查詢語句,如果沒有則返回空String。 |
bool | next() | 定位到查詢結果的下一條記錄上。 以下規則將會應用: ①如果當前定位到第一條記錄之前——常用於執行后立即查詢,那么結果會使Query定位到第一條記錄; ②如果定位到最后一條記錄,那么什么都不做並返回false; ③如果定位到中間,則定位到下一條記錄。 |
bool | nextResult() | 刪除當前記錄並定位到下一條記錄上。 |
int | numRowsAffected() | 返回SQL語句將會影響的行數,不確定時返回-1。 如果是SELECT語句,不能用該方法,應該用size()。 Query未激活時返回-1。 |
QSql::NumericalPrecisionPolicy | numericalPrecisionPolicy() | 返回當前的數字精度策略。 |
bool | prepare(QString query) | 預定義查詢。 |
bool | previous() | 定位當前Query為上一條記錄。 |
QSqlRecord | record() | 返回當前Query的結果QSqlRecord,其中包含了Query結果的字段信息。 如果Query指向一個合法的行,那么QSqlRecord將用指定行值填充,當Query未激活時,會返回一個空的記錄。 為了從Query中提取值,調用基於索引的value()進行檢索將會速度更快。 下邊的例子中,使用了SELECT * FROM查詢語句。由於結果列的順序並不確定,所以通過QSqlRecord::indexOf()來獲取這些列的索引: QSqlQuery q("select * from employees"); QSqlRecord rec = q.record(); qDebug() << "Number of columns: " << rec.count(); int nameCol = rec.indexOf("name"); // index of the field "name" while (q.next()) qDebug() << q.value(nameCol).toString(); // output all names |
QSqlResult * | result() | 返回該Query的Result |
bool | seek(int index, bool relative = false) | 定位到指定記錄處,第二個參數指定相對、絕對偏移,即以當前位置為基准還是以開頭為基准。 |
void | setForwardOnly(bool forward) | 只允許前向查詢。當forward為true時,next()、seek()方法只能用正數。 這個方法應當在Query exec前主動調用,默認情況下前向查詢是未激活的。 |
void | setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy precisionPolicy) | 設置數值精度 |
int | size() | 返回查詢結果的size,即行數。失敗時返回-1。 非SELECT語句會返回-1,應該調用numRowsAffected()。 |
QVariant | value(int index) value(QString name) |
返回當前記錄中,索引字段index的值,一個字段就是一列,索引從0開始。不推薦使用SELECT *因為字段順序會被打亂。失敗時返回一個非法QVariant。 用列名的方式訪問當前記錄中某個字段的值。 |
7、用法
1)在某個已經open的QSqlDatabase連接上構造QSqlQuery對象,方便后續查詢:
QSqlQuery query(db);//db是QSqlDatabase且已經Open
2)構造SQL語句QString,進行查詢:
QSqlQuery query(db); QString sql = "SELECT str.id,str.imei,str.table_name,"; sql+="sed.location_x,sed.location_y "; sql+="FROM sensor_table_relation str , sensor_end_data sed "; sql+="WHERE str.imei = sed.imei "; sql+="ORDER BY str.id"; query.exec(sql);
3)看看一共查詢到了多少項:
如果用SELECT 列名1,列名2,...查詢,用size()
如果用SELECT *查詢,應當用numRowsAffected()
qDebug()<<query.size();//68
4)不斷地提取某條記錄的所有字段:
在上文中,我們SELECT了5項:
- str.id
- str.imei
- str.table_name
- sed.location_x
- sed.location_y
可以用value(i)的方式提取每條記錄中的每項,一共5項,那么i的取值就為0~4,跟這5項一一對應。需要注意的是value(i)提取的項是QVariant類型,要用toT()轉化為指定的Qt類型。
不斷地調用next(),可以依次將QSqlQuery定位到下一條記錄上,結合value(i),就能實現提取所有查找記錄的所有項的功能:
while(query.next()){ QString imei = query.value(1).toString(); QString table_name = query.value(2).toString(); float locationX = query.value(3).toFloat(); float locationY = query.value(4).toFloat(); Info info( imei , table_name , locationX , locationY);//自定義存儲查詢結果的類,類似於Java中的ORM機制 infolist.append(info);//把所有記錄存入一個QList<Info>中 }
注意:查詢完畢后,query並沒有指向返回的第一條記錄處,需要先調用next()才能正確訪問第一條記錄的各項(這點十分重要)。