Warning, /sdk/codevis/thirdparty/soci/docs/statements.md is written in an unsupported language. File is not indexed.
0001 # Statements
0002
0003 ## Prepared statement
0004
0005 Consider the following examples:
0006
0007 ```cpp
0008 // Example 1.
0009 for (int i = 0; i != 100; ++i)
0010 {
0011 sql << "insert into numbers(value) values(" << i << ")";
0012 }
0013
0014 // Example 2.
0015 for (int i = 0; i != 100; ++i)
0016 {
0017 sql << "insert into numbers(value) values(:val)", use(i);
0018 }
0019 ```
0020
0021 Both examples will populate the table `numbers` with the values from `0` to `99`.
0022
0023 The problem is that in both examples, not only the statement execution is repeated 100 times, but also the statement parsing and preparation.
0024 This means unnecessary overhead, even if some of the database servers are likely to optimize the second case.
0025 In fact, more complicated queries are likely to suffer in terms of lower performance, because finding the optimal execution plan is quite expensive and here it would be needlessly repeated.
0026
0027 ### Statement preparation
0028
0029 The following example uses the class `statement` explicitly, by preparing the statement only once and repeating its execution with changing data (note the use of `prepare` member of `session` class):
0030
0031 ```cpp
0032 int i;
0033 statement st = (sql.prepare <<
0034 "insert into numbers(value) values(:val)",
0035 use(i));
0036 for (i = 0; i != 100; ++i)
0037 {
0038 st.execute(true);
0039 }
0040 ```
0041
0042 The `true` parameter given to the `execute` method indicates that the actual data exchange is wanted, so that the meaning of the whole example is
0043
0044 > "prepare the statement and exchange the data for each value of variable `i`".
0045
0046 ### Portability note:
0047
0048 The above syntax is supported for all backends, even if some database server does not actually provide this functionality - in which case the library will internally execute the query in a single phase, without really separating the statement preparation from execution.
0049
0050 ## Rowset and iterator
0051
0052 The `rowset` class provides an alternative means of executing queries and accessing results using STL-like iterator interface.
0053
0054 The `rowset_iterator` type is compatible with requirements defined for input iterator category and is available via `iterator` and `const_iterator` definitions in the `rowset` class.
0055
0056 The `rowset` itself can be used only with select queries.
0057
0058 The following example creates an instance of the `rowset` class and binds query results into elements of `int` type - in this query only one result column is expected.
0059 After executing the query the code iterates through the query result using `rowset_iterator`:
0060
0061 ```cpp
0062 rowset<int> rs = (sql.prepare << "select values from numbers");
0063
0064 for (rowset<int>::const_iterator it = rs.begin(); it != rs.end(); ++it)
0065 {
0066 cout << *it << '\n';
0067 }
0068 ```
0069
0070 Another example shows how to retrieve more complex results, where `rowset` elements are of type `row` and therefore use [dynamic bindings](types.md#dynamic-binding):
0071
0072 ```cpp
0073 // person table has 4 columns
0074
0075 rowset<row> rs = (sql.prepare << "select id, firstname, lastname, gender from person");
0076
0077 // iteration through the resultset:
0078 for (rowset<row>::const_iterator it = rs.begin(); it != rs.end(); ++it)
0079 {
0080 row const& row = *it;
0081
0082 // dynamic data extraction from each row:
0083 cout << "Id: " << row.get<int>(0) << '\n'
0084 << "Name: " << row.get<string>(1) << " " << row.get<string>(2) << '\n'
0085 << "Gender: " << row.get<string>(3) << endl;
0086 }
0087 ```
0088
0089 The `rowset_iterator` can be used with standard algorithms as well:
0090
0091 ```cpp
0092 rowset<string> rs = (sql.prepare << "select firstname from person");
0093
0094 std::copy(rs.begin(), rs.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
0095 ```
0096
0097 Above, the query result contains a single column which is bound to `rowset` element of type of `std::string`.
0098 All records are sent to standard output using the `std::copy` algorithm.
0099
0100 If you need to use the Core interface with `rowset`, the following example shows how:
0101
0102 ```cpp
0103 row r;
0104
0105 statement st(sql);
0106 st.alloc();
0107 st.prepare("select values from numbers");
0108 st.define_and_bind();
0109
0110 // after define_and_bind and before execute
0111 st.exchange_for_rowset(into(r));
0112
0113 st.execute(false);
0114
0115 rowset_iterator<row> it(st, r);
0116 rowset_iterator<row> end;
0117 for (; it != end; ++it) {
0118 // ... access *it
0119 }
0120 ```
0121
0122 ## Bulk operations
0123
0124 When using some databases, further performance improvements may be possible by having the underlying database API group operations together to reduce network roundtrips.
0125 SOCI makes such bulk operations possible by supporting `std::vector` based types.
0126
0127 The following example presents how to insert 100 records in 4 batches.
0128 It is also important to note, that size of vector remains equal in every batch interaction.
0129 This ensures vector is not reallocated and, what's crucial for the bulk trick, new data should be pushed to the vector before every call to `statement::execute`:
0130
0131 ```cpp
0132 // Example 3.
0133 void fill_ids(std::vector<int>& ids)
0134 {
0135 for (std::size_t i = 0; i < ids.size(); ++i)
0136 ids[i] = i; // mimics source of a new ID
0137 }
0138
0139 const int BATCH_SIZE = 25;
0140 std::vector<int> ids(BATCH_SIZE);
0141
0142 statement st = (sql.prepare << "insert into numbers(value) values(:val)", use(ids));
0143 for (int i = 0; i != 4; ++i)
0144 {
0145 fill_ids(ids);
0146 st.execute(true);
0147 }
0148 ```
0149
0150 Given batch size is 25, this example should insert 4 x 25 = 100 records.
0151
0152 (Of course, the size of the vector that will achieve optimum performance will vary, depending on many environmental factors, such as network speed.)
0153
0154 It is also possible to read all the numbers written in the above examples:
0155
0156 ```cpp
0157 int i;
0158 statement st = (sql.prepare << "select value from numbers order by value", into(i));
0159 st.execute();
0160 while (st.fetch())
0161 {
0162 cout << i << '\n';
0163 }
0164 ```
0165
0166 In the above example, the `execute` method is called with the default parameter `false`.
0167 This means that the statement should be executed, but the actual data exchange will be performed later.
0168
0169 Further `fetch` calls perform the actual data retrieval and cursor traversal.
0170 The *end-of-cursor* condition is indicated by the `fetch` function returning `false`.
0171
0172 The above code example should be treated as an idiomatic way of reading many rows of data, *one at a time*.
0173
0174 It is further possible to select records in batches into `std::vector` based types, with the size of the vector specifying the number of records to retrieve in each round trip:
0175
0176 ```cpp
0177 std::vector<int> valsOut(100);
0178 sql << "select val from numbers", into(valsOut);
0179 ```
0180
0181 Above, the value `100` indicates that no more values should be retrieved, even if it would be otherwise possible.
0182 If there are less rows than asked for, the vector will be appropriately down-sized.
0183
0184 The `statement::execute()` and `statement::fetch()` functions can also be used to repeatedly select all rows returned by a query into a vector based type:
0185
0186 ```cpp
0187 const int BATCH_SIZE = 30;
0188 std::vector<int> valsOut(BATCH_SIZE);
0189 statement st = (sql.prepare <<
0190 "select value from numbers",
0191 into(valsOut));
0192 st.execute();
0193 while (st.fetch())
0194 {
0195 std::vector<int>::iterator pos;
0196 for(pos = valsOut.begin(); pos != valsOut.end(); ++pos)
0197 {
0198 cout << *pos << '\n';
0199 }
0200
0201 valsOut.resize(BATCH_SIZE);
0202 }
0203 ```
0204
0205 Assuming there are 100 rows returned by the query, the above code will retrieve and print all of them.
0206 Since the output vector was created with size 30, it will take (at least) 4 calls to `fetch()` to retrieve all 100 values.
0207 Each call to `fetch()` can potentially resize the vector to a size less than its initial size - how often this happens depends on the underlying database implementation.
0208 This explains why the `resize(BATCH_SIZE)` operation is needed - it is there to ensure that each time the `fetch()` is called, the vector is ready to accept the next bunch of values.
0209 Without this operation, the vector *might* be getting smaller with subsequent iterations of the loop, forcing more iterations to be performed (because *all* rows will be read anyway), than really needed.
0210
0211 Note the following details about the above examples:
0212
0213 * After performing `fetch()`, the vector's size might be *less* than requested, but `fetch()` returning true means that there was *at least one* row retrieved.
0214 * It is forbidden to manually resize the vector to the size *higher* than it was initially (this can cause the vector to reallocate its internal buffer and the library can lose track of it).
0215
0216 Taking these points under consideration, the above code example should be treated as an idiomatic way of reading many rows by bunches of requested size.
0217
0218 ### Portability note
0219
0220 Actually, all supported backends guarantee that the requested number of rows will be read with each fetch and that the vector will never be down-sized, unless for the last fetch, when the end of rowset condition is met.
0221 This means that the manual vector resizing is in practice not needed - the vector will keep its size until the end of rowset.
0222 The above idiom, however, is provided with future backends in mind, where the constant size of the vector might be too expensive to guarantee and where allowing `fetch` to down-size the vector even before reaching the end of rowset might buy some performance gains.
0223
0224 ## Statement caching
0225
0226 Some backends have some facilities to improve statement parsing and compilation to limit overhead when creating commonly used query.
0227 But for backends that does not support this kind optimization you can keep prepared statement and use it later with new references.
0228 To do such, prepare a statement as usual, you have to use `exchange` to bind new variables to statement object, then `execute` statement and finish by cleaning bound references with `bind_clean_up`.
0229
0230 ```cpp
0231 sql << "CREATE TABLE test(a INTEGER)";
0232
0233 {
0234 // prepare statement
0235 soci::statement stmt = (sql.prepare << "INSERT INTO numbers(value) VALUES(:val)");
0236
0237 {
0238 // first insert
0239 int a0 = 0;
0240
0241 // update reference
0242 stmt.exchange(soci::use(a0));
0243
0244 stmt.define_and_bind();
0245 stmt.execute(true);
0246 stmt.bind_clean_up();
0247 }
0248
0249 {
0250 // come later, second insert
0251 int a1 = 1;
0252
0253 // update reference
0254 stmt.exchange(soci::use(a1));
0255
0256 stmt.define_and_bind();
0257 stmt.execute(true);
0258 stmt.bind_clean_up();
0259 }
0260 }
0261
0262 {
0263 std::vector<int> v(10);
0264 sql << "SELECT value FROM numbers", soci::into(v);
0265 for (int i = 0; i < v.size(); ++i)
0266 std::cout << "value " << i << ": " << v[i] << std::endl;
0267 }
0268 ```