I have a query that declares a table variable. It seems nanodbc cannot fetch data although the query works fine when directly executed on SQL server. Does it mean complicated queries are not supported by nanodbc?
Environment
nanodbc version: 2.14
DBMS name/version: MS SQL Server 2017
ODBC connection string:
OS and Compiler: Windows, Visual C++ 2019
CMake settings:
Actual behavior
sql queries containing table variables do not work.
Expected behavior
sql queries containing table variables should work.
Minimal Working Example
void show(nanodbc::result& results)
{
const short columns = results.columns();
long rows_displayed = 0;
cout << "\nDisplaying " << results.affected_rows() << " rows "
<< "(" << results.rowset_size() << " fetched at a time):" << endl;
// show the column names
cout << "row\t";
for(short i = 0; i < columns; ++i)
cout << results.column_name(i) << "\t";
cout << endl;
// show the column data for each row
while(results.next())
{
cout << rows_displayed++ << "\t";
for(short col = 0; col < columns; ++col)
cout << "(" << results.get<string>(col, "null") << ")\t";
cout << endl;
}
}
nanodbc::connection db_conn_ = nanodbc::connection(db_conn_string_);
execute(db_conn_, NANODBC_TEXT("DROP TABLE IF EXISTS research.dbo.nanodbc_test"));
execute(db_conn_, NANODBC_TEXT("CREATE TABLE research.dbo.nanodbc_test(Name varchar(20), Age int)"));
execute(db_conn_, NANODBC_TEXT("INSERT INTO research.dbo.nanodbc_test(Name, Age) VALUES('Bar', 20)"));
execute(db_conn_, NANODBC_TEXT("INSERT INTO research.dbo.nanodbc_test(Name, Age) VALUES('Foo', 30)"));
nanodbc::result result_working = execute( db_conn_, NANODBC_TEXT("select * from research.dbo.nanodbc_test"));
show(result_working);
//The following query does not return valid result, result_not_working contains 0 observations.
nanodbc::result result_not_working = execute(
db_conn_,
NANODBC_TEXT(
"declare #names table(n varchar(20) not null primary key);"
" insert into #names select * from string_split('Bar,Foo', ',');"
" select[Name], [Age]"
" from research.dbo.nanodbc_test where Age in(20, 30)"
" and [Name] in(select n from #names)"
)
);
show(result_not_working);
The question above is solved by adding 'set nocount on' at the beginning of the query suggested by David and Dan mentioned below.
What I am trying to solve is actually a bit more complicated. I want to run a parameterized query.
string query = NANODBC_TEXT(
"set nocount on"
" declare #names table(n varchar(20) not null primary key);"
" insert into #names select * from string_split(?, ',');"
" select[Name], [Age]"
" from research.dbo.nanodbc_test where Age in(20, 30)"
" and [Name] in(select n from #names)"
);
nanodbc::statement statement = nanodbc::statement(db_conn__);
prepare(statement, query);
string names = "Bar,Foo";
//The error happens when I try to bind a string parameter.
statement.bind(0, names.c_str());
Can someone help on this? Thanks.
Related
SQLiteCpp is a open sourced library for operating sqlite database. Here's an example.
try
{
// Open a database file
SQLite::Database db("example.db3");
// Compile a SQL query, containing one parameter (index 1)
SQLite::Statement query(db, "SELECT * FROM test WHERE size > ?");
// Bind the integer value 6 to the first parameter of the SQL query
query.bind(1, 6);
// Loop to execute the query step by step, to get rows of result
while (query.executeStep())
{
// Demonstrate how to get some typed column value
int id = query.getColumn(0);
const char* value = query.getColumn(1);
int size = query.getColumn(2);
std::cout << "row: " << id << ", " << value << ", " << size << std::endl;
}
}
catch (std::exception& e)
{
std::cout << "exception: " << e.what() << std::endl;
}
Since the query is passed in the constructor of Statement SQLite::Statement query(db, "SELECT * FROM test WHERE size > ?");, how do I reuse it?
I didn't see a method accepts a string like query.setQuery("select * from table"). Do you know how do I reuse it?
If it bothers you that much you can just reassign it:
query = SQLite::Statement{db, "select * from table"};
QSqlQuery insert_emi_query;
insert_emi_query.prepare("INSERT INTO emi_info (emi-info_id, customer_id, down_payment, emi_start_date, emi_end_date, emi_amount, toatl_emi, intrest_rate, total_emi_amount) "
"VALUES(:emi-info_id, :customer_id, :down_payment, :emi_start_date, :emi_end_date, :emi_amount, :toatl_emi, :intrest_rate, :total_emi_amount)");
insert_emi_query.bindValue(":emi-info_id",emi_id);
insert_emi_query.bindValue(":customer_id",cutomer_id);
insert_emi_query.bindValue(":down_payment",ui->txtEMIDownPayment->text().toInt());
insert_emi_query.bindValue(":emi_start_date",ui->dateEMIStart->date());
insert_emi_query.bindValue(":emi_end_date",ui->dateEMIEnd->date());
insert_emi_query.bindValue(":emi_amount",ui->txtEMIPerMonth->text().toInt());
insert_emi_query.bindValue(":toatl_emi",ui->spinEMI->text().toInt());
insert_emi_query.bindValue(":intrest_rate",ui->txtEMIRate->text().toInt());
insert_emi_query.bindValue(":total_emi_amount",ui->txtEMIAfterPayment->text().toInt());
if(insert_emi_query.exec()){
qDebug() << "EMI Info Added---------------------";
}else{
qDebug() << "EMi not inserted" << insert_emi_query.lastError();
}
:emi-info_id is not a valid parameter name (- is not allowed in unquoted identifiers, and parameter names cannot be quoted).
I was not able to edit your question. May be you want to edit it yourself, using the following snippet, to improve comprehension.
1) Is the dash symbol in "emi-info_id" right? Most databases do not allow a dash in a column name.
2) May be you want to check if you have specified values for all mandatory columns of the table.
QSqlQuery insert_emi_query;
insert_emi_query.prepare(" "
" "
"INSERT INTO emi_info ( "
" emi-info_id, customer_id, down_payment, emi_start_date, "
" emi_end_date, emi_amount, toatl_emi, intrest_rate, "
" total_emi_amount) "
" "
" "
"VALUES( "
" :emi-info_id, :customer_id, :down_payment, :emi_start_date, "
" :emi_end_date, :emi_amount, :toatl_emi, :intrest_rate, "
" :total_emi_amount) "
" "
);
insert_emi_query.bindValue(":emi-info_id",emi_id);
insert_emi_query.bindValue(":customer_id",cutomer_id);
insert_emi_query.bindValue(":down_payment",ui->txtEMIDownPayment->text().toInt());
insert_emi_query.bindValue(":emi_start_date",ui->dateEMIStart->date());
insert_emi_query.bindValue(":emi_end_date",ui->dateEMIEnd->date());
insert_emi_query.bindValue(":emi_amount",ui->txtEMIPerMonth->text().toInt());
insert_emi_query.bindValue(":toatl_emi",ui->spinEMI->text().toInt());
insert_emi_query.bindValue(":intrest_rate",ui->txtEMIRate->text().toInt());
insert_emi_query.bindValue(":total_emi_amount",ui->txtEMIAfterPayment->text().toInt());
if(insert_emi_query.exec()) {
qDebug() << "EMI Info Added---------------------";
}
else{
qDebug() << "EMi not inserted" << insert_emi_query.lastError();
}
It seems that column name emi-info_id is typo in you query. It should be emi_info_id. Because column name with - is invalid.
If you are using Qt5(I didn't know what is there in qt4) or above then you can get error type like
insert_emi_query.lastError().type()
In above case it should be QSqlError::StatementError because there is typo in name of column.
I am trying to get back into programming for a personal project I would like to work on but I am having trouble figuring out the sqlite3 c/c++ api. I am modifying code snippets I found at various tutorials and I'm trying to get them return data and field names from a database. Here is the section of code that I am having difficulty with:
sqlite3 *db;
sqlite3_stmt *ppStmt;
int col_num;
char *zErrMsg = 0;
const char *zSql = "SELECT ?,? FROM basic_table";
string s = "Field1";
string t = "Field2";
rc = sqlite3_prepare_v2(db,zSql,-1,&ppStmt,0);
sqlite3_bind_text(ppStmt,1,s.c_str(),s.length(),SQLITE_TRANSIENT);
sqlite3_bind_text(ppStmt,2,t.c_str(),t.length(),SQLITE_TRANSIENT);
if(rc)
{
cerr << "SQL error: " << sqlite3_errmsg(db) << endl;
}
else
{
col_num = sqlite3_column_count(ppStmt);
do
{
rc = sqlite3_step(ppStmt);
switch(rc)
{
case SQLITE_DONE:
break;
case SQLITE_ROW:
for( int i=0; i<col_num; i++)
{
cout << sqlite3_column_name(ppStmt, i) << " " <<
sqlite3_column_text(ppStmt,i) << endl;
}
break;
default:
cerr << "Inside error " << sqlite3_errmsg(db) << endl;
break;
}
} while( rc==SQLITE_ROW );
sqlite3_finalize(ppStmt);
The column_name return the correct value but the data just comes back with a question mark. I can get the code to work by calling sqlite3_exec with a callback function but the documentation for sqlite3 recommends using sqlite3_prepare_v2, sqlite3_bind, sqlite3_step, and sqlite3_finalize instead of this method so I am just trying to see if I can get that method to work. Is there a simple error which I am making? Any suggestions would be appreciated.
The name of these columns is ?, because that is how they are written in the query.
The values of these columns are Field1 and Field2, because you told the database that you want to have these string values returned by the SELECT.
The database works as designed.
Parameters can be used only to replace the value of an expression; you cannot use them to replace identifiers such as column/table names.
You have to write the column names directly in the SQL statement:
const char *zSql = "SELECT Field1, Field2 FROM basic_table";
Consider the following interraction with the postgres database:
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
db.setHostName("acidalia");
db.setDatabaseName("customdb");
db.setUserName("mojito");
db.setPassword("J0a1m8");
bool ok = db.open();
QSqlQuery query(db);
QSqlQuery query(db);
QVector<int> byteArray(2);
byteArray[0] = 0;
byteArray[1] = 7;
QVariant v = QVariant::fromValue(byteArray);
cout << "dropping a table: " << query.exec("drop table aaa;") << endl; //gives 1
cout << "creating a table: " << query.exec("create table aaa (gid integer, pos integer[])") << endl; // gives 0
query.prepare("INSERT INTO aaa (gid) VALUES (:gid, :pos)");
query.bindValue(0, 1);
query.bindValue(1, v);
cout << "inserting: " << query.exec() << endl; // gives 0 :-(
Of course, one way to do that would be to send the data with a manually built sql statement, and execute the query as a normal query on the server (where the byte array would be inserted as a string), but I am looking for a somewhat nicer solution..
The INSERT has 3 destination columns declared but 4 bind values.
query.prepare("INSERT INTO geo (gid, bboxx, bboxy) "
"VALUES (:gid, :bboxx, :bboxy, :pos)");
Once the bytea column is added after bboxy, this should work.
In the following code for retrieving data via a SQL query from the db, is there a way I can replace row[0] with fieldname. I am only enclosing the relevant part of the code
MYSQL_RES *resptr;
MYSQL_ROW row;
while ( ( row = mysql_fetch_row(resptr)) != NULL )
{
for (int i=0;i<8;i++)
string key = row[0];
}
row[0] is the trans_date column. the code works fine as it is right now, but is there a way to assign key by using the fieldname vs. having to remember all the field numbers.
thanks!
You can retrieve field names by doing
field = mysql_fetch_field(resptr);
std::cout << field->name;
(Put in a while loop to loop through all field)
But you can't call row[fieldName]. What you can do though is define variables to map column names and numbers
int id = 0;
int trans_date = 1;
// Code happening here
std::cout << row[id] << " " << row[trans_date];
UPDATE: You could always do something like this (using STL's map):
map<string, int> columns;
int i = 0;
while(field = mysql_fetch_field(resptr)){
columns.insert(pair<string,int>(field->name, i++));
}
And then use row[columns["name"]];.
MySQL++ solves this problem. You can say things like:
string key = row["key"];
or by use of its SSQLS feature, this:
MyTableStructure foo = result[i];
cout << "The key is " << foo.key << endl;