Hi
I have a sqlite db which I am manipulating using qts built in sqlite database driver.
I have a small test app that allows me to run an sql query from a line edit and it will be executed and the results are then updated in a view of the relevant model.
I have created a table which uses autoincremented primary key values, but if I execute an insert statement without providing the key, I get two rows inserted, each with an autoincremented value.
If I provide the key value, only one row is created. Any ideas why this is?
Table is simple enough, e.g
CREATE TABLE GroupNames ( ID integer PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, Name varchar(50))
and when I run the query
insert into groupnames (name) values ("testName");
I get two new rows with autoincremented ids. However, if I run
insert into groupnames (id, name) values (100, "testName");
I get one row as expected, with the correct id 100.
Also of note is that if I try
insert into table groupnames (id, name) values (100, "testName");
insert into table groupnames (name) values ("testName");
the query does not run.
The qt code to run the query could not be simpler:
QSqlQuery *DbCore::run_query(const QString &query_string)
{
QSqlDatabase db = QSqlDatabase::database(defConnectionName);
if(!db.isOpen())
return NULL;
QSqlQuery *q = new QSqlQuery(query_string, db);
q->exec();
return q;
}
I have added some logging code to check that the query is executed once:
QSqlDatabase db = QSqlDatabase::database(defConnectionName);
if(!db.isOpen())
return NULL;
qDebug() << "Running query:" << query_string;
QSqlQuery *q = new QSqlQuery(query_string, db);
if(!q->exec())
qDebug() << "Error running query:" << q->lastError();
return q;
The log confirms that I'm only executing once:
Running query: "insert into groupnames (name) values ("hello")"
If i then check the database using sqlite3 shell (to remove any doubt about qt views etc):
sqlite> select * from groupnames;
1|hello
2|hello
question was answered above in a comment:
As i see in the documentation, when you create a QSqlQuery the way you do, the query, if not empty, is executed. To create the QSqlQuery and execute the query, use this: QSqlQuery *q = new QSqlQuery(db); q->exec(query_string) To see the last executed query, use QSqlQuery::lastQuery() And for the last query that was successfully executed QSqlQuery::executedQuery() Hope this helps. – Hector Mar 16 at
Related
In my db-driven app I need to perform insert into queries in which the value for one or more field comes from a subquery.
The insert into statement may look like the following example:
INSERT INTO MyTable (field_1, field_2)
VALUES('value for field 1', (SELECT field_x FROM AnotherTable WHERE ...))
At present I am doing it manually building the query:
String MyQuery = "INSERT INTO mytable (field_1, field_2)
VALUES('value for field 1', (SELECT field_x FROM AnotherTable WHERE ...))"; // Of course my query is far more complex and is built in several steps but the concept is safe, I end up with a SQL String
SQLiteDatabase= db = getWritableDatabase();
db.execSQL(MyQuery); // And it works flawlessy as it was a Swiss Clock
What i would like to do instead is:
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put("field_1", "value for field 1");
values.put("field_2", ThisIsAQuery("(SELECT field_x FROM AnotherTable WHERE ...)"));
db.insert("MyTable", null, values);
db.close();
Where the fake method ThisIsAQuery(...) is the missing part, something that should tell the query builder that "SELECT.." is not a value but a query that should be embedded in the insert statement.
Is there a way to achieve this?
The whole point of the ContentValues container is to be able to safely use strings without interpreting them as SQL commands.
It is not possible to use subqueries with insert(). The only way to get a value from another table is by executing a separate query; in this case, ThisIsAQuery() would be stringForQuery() or longForQuery().
I am trying to get the primary key of an inserted row within a transaction scope, because I do not want to leave the db in a logically inconsistent state.
My problem is I cannot find a way to retrieve the ID value of a previously executed query, which I want to use for the next insert query. Querying the PostgreSQL database while the transaction is in effect shows no results in the non-foreign-key table(the row is not yet committed?). I believe this is due to the transaction's isolation level.
Below is what I'm trying to do with production code, albeit slightly edited and narrowed down to one function for clarity. const int lastInsertId is always 0, which in this context should mean no value was found (technically that toInt() function failed). I tried manually inserting a valid non-foreign-key row, and then calling LASTVAL() which produced the expected result - the ID of the inserted row.
So, what am I doing wrong? What am I missing or misunderstanding here?
void createEntityWithoutForiegnKeyConstraint(const QString &nameOfEntity)
{
db_.transaction();
QSqlQuery insertQuery(db_);
insertQuery.prepare("INSERT INTO \"EntityWithoutForeignKey\" (\"name\") VALUES (:name);");
insertQuery.bindValue(":name", nameOfEntity);
execQuery(__LINE__, insertQuery);
QSqlQuery lastIdQuery("SELECT LASTVAL();", db_); // auto executes
const int lastInsertId = lastIdQuery.value(0).toInt();
if (lastInsertId <= 0) // 0 is not a valid ID
throw exception("Oh noes.");
createEntityWithForeignKeyConstraint(lastInsertId, someData);
if (!db_.commit())
db_.rollback();
}
I realise this is an old question but in Qt 5.10 (and likely earlier) there is a function QSqlQuery::lastInsertId() which can be called after QSqlQuery::exec().
It's quite useful if you are using a database such as SQLite which doesn't support the RETURNING clause on an INSERT statement.
QSqlQuery::lastInsertId() documentation.
Usage is something along the lines of the following:
QSqlQuery q;
q.prepare("INSERT INTO table_name VALUES(:some_column_name)");
q.bindValue(":some_column_name", "FooBar");
q.exec();
qDebug() << "Last ID was:" << q.lastInsertId();
#Kasheen is perfectly right but I want to set the focus on one important aspect you should keep in mind: Don't forget to encapsulate everything in one single transaction.
Why? It saves time and avoids database corruption if the second insert using the generated primary key (you got with q.lastInsertId()) fails.
So your insert should look like this (this is basically #Kasheen's answer with some additions):
db_.transaction(); // Starts a transaction
QSqlQuery q;
// first insert
q.prepare("INSERT INTO table_name VALUES(:some_column_name)");
q.bindValue(":some_column_name", "FooBar");
q.exec();
auto pk == q.lastInsertId().toInt;
// second insert
q.prepare("INSERT INTO other_table VALUES(:other_column_name, :fk)");
q.bindValue(":other_column_name", "OtherText");
q.bindValue(":fk", pk);
q.exec();
db_.commit(); // Commits transaction
#average joe does the transaction handling correctly but you might forget it if you look on the solution only.
I need to get column names and their tables in a SQLite database. What I need is a resultset with 2 columns: table_name | column_name.
In MySQL, I'm able to get this information with a SQL query on database INFORMATION_SCHEMA. However the SQLite offers table sqlite_master:
sqlite> create table students (id INTEGER, name TEXT);
sqlite> select * from sqlite_master;
table|students|students|2|CREATE TABLE students (id INTEGER, name TEXT)
which results a DDL construction query (CREATE TABLE) which is not helpful for me and I need to parse this to get relevant information.
I need to get list of tables and join them with columns or just get columns along with table name column. So PRAGMA table_info(TABLENAME) is not working for me since I don't have table name. I want to get all column metadata in the database.
Is there a better way to get that information as a result set by querying database?
You've basically named the solution in your question.
To get a list of tables (and views), query sqlite_master as in
SELECT name, sql FROM sqlite_master
WHERE type='table'
ORDER BY name;
(see the SQLite FAQ)
To get information about the columns in a specific table, use PRAGMA table_info(table-name); as explained in the SQLite PRAGMA documentation.
I don't know of any way to get tablename|columnname returned as the result of a single query. I don't believe SQLite supports this. Your best bet is probably to use the two methods together to return the information you're looking for - first get the list of tables using sqlite_master, then loop through them to get their columns using PRAGMA table_info().
Recent versions of SQLite allow you to select against PRAGMA results now, which makes this easy:
SELECT
m.name as table_name,
p.name as column_name
FROM
sqlite_master AS m
JOIN
pragma_table_info(m.name) AS p
ORDER BY
m.name,
p.cid
where p.cid holds the column order of the CREATE TABLE statement, zero-indexed.
David Garoutte answered this here, but this SQL should execute faster, and columns are ordered by the schema, not alphabetically.
Note that table_info also contains
type (the datatype, like integer or text),
notnull (1 if the column has a NOT NULL constraint)
dflt_value (NULL if no default value)
pk (1 if the column is the table's primary key, else 0)
RTFM: https://www.sqlite.org/pragma.html#pragma_table_info
There are ".tables" and ".schema [table_name]" commands which give kind of a separated version to the result you get from "select * from sqlite_master;"
There is also "pragma table_info([table_name]);" command to get a better result for parsing instead of a construction query:
sqlite> .tables
students
sqlite> .schema students
create table students(id INTEGER, name TEXT);
sqlite> pragma table_info(students);
0|id|INTEGER|0||0
1|name|TEXT|0||0
Hope, it helps to some extent...
Another useful trick is to first get all the table names from sqlite_master.
Then for each one, fire off a query "select * from t where 1 = 0". If you analyze the structure of the resulting query - depends on what language/api you're calling it from - you get a rich structure describing the columns.
In python
c = ...db.cursor()
c.execute("select * from t where 1=0");
c.fetchall();
print c.description;
Juraj
PS. I'm in the habit of using 'where 1=0' because the record limiting syntax seems to vary from db to db. Furthermore, a good database will optimize out this always-false clause.
The same effect, in SQLite, is achieved with 'limit 0'.
FYI, if you're using .Net you can use the DbConnection.GetSchema method to retrieve information that usually is in INFORMATION_SCHEMA. If you have an abstraction layer you can have the same code for all types of databases (NOTE that MySQL seems to swich the 1st 2 arguments of the restrictions array).
Try this sqlite table schema parser, I implemented the sqlite table parser for parsing the table definitions in PHP.
It returns the full definitions (unique, primary key, type, precision, not null, references, table constraints... etc)
https://github.com/maghead/sqlite-parser
The syntax follows sqlite create table statement syntax: http://www.sqlite.org/lang_createtable.html
This is an old question but because of the number of times it has been viewed we are adding to the question for the simple reason most of the answers tell you how to find the TABLE names in the SQLite Database
WHAT DO YOU DO WHEN THE TABLE NAME IS NOT IN THE DATABASE ?
This is happening to our app because we are creating TABLES programmatically
So the code below will deal with the issue when the TABLE is NOT in or created by the Database Enjoy
public void toPageTwo(View view){
if(etQuizTable.getText().toString().equals("")){
Toast.makeText(getApplicationContext(), "Enter Table Name\n\n"
+" OR"+"\n\nMake Table First", Toast.LENGTH_LONG
).show();
etQuizTable.requestFocus();
return;
}
NEW_TABLE = etQuizTable.getText().toString().trim();
db = dbHelper.getWritableDatabase();
ArrayList<String> arrTblNames = new ArrayList<>();
Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE
type='table'", null);
if (c.moveToFirst()) {
while ( !c.isAfterLast() ) {
arrTblNames.add( c.getString( c.getColumnIndex("name")) );
c.moveToNext();
}
}
c.close();
db.close();
boolean matchFound = false;
for(int i=0;i<arrTblNames.size();i++) {
if(arrTblNames.get(i).equals(NEW_TABLE)) {
Intent intent = new Intent(ManageTables.this, TableCreate.class
);
startActivity( intent );
matchFound = true;
}
}
if (!matchFound) {
Toast.makeText(getApplicationContext(), "No Such Table\n\n"
+" OR"+"\n\nMake Table First", Toast.LENGTH_LONG
).show();
etQuizTable.requestFocus();
}
}
Hey what i want to do is update a table if the field exsits or insert the new value as given by the user.
For this what i do is select everything present in the table and check if the name entered by the user exists den update it or else insert it.
However the problem is when there is no data in the table the query fails and no data is inserted in the table.
NSString *query = #"select * from PPM";
sqlite_stmt *statement;
if(sqlite_prepare_v2(database,[query UTF8string],-1,&statement,NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
//select data from table
//if value entered is same as entered by user update the table
//else insert in the table
}
How about INSERT OR REPLACE ?
You can use it with the UNIQUE constraints in your database structure to replace a value if it already exists, and insert it if it not there already.
shell> sqlite3
SQLite version 3.6.23
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> CREATE TABLE user(name UNIQUE, value);
sqlite> INSERT OR REPLACE INTO user VALUES("foo", 123);
sqlite> SELECT * FROM user;
foo|123
sqlite> INSERT OR REPLACE INTO user VALUES("foo", 321);
sqlite> SELECT * FROM user;
foo|321
If you use the INSERT OR REPLACE variation of the INSERT command, you don't need to check for existing data, as SQLite will do it for you, as long as there is a column with a UNIQUE constraint.
For example:
CREATE TABLE db.table(id UNIQUE, name);
INSERT OR REPLACE INTO db.table (id, name) VALUES (5, 'exep');
If an id of 5 exists in the table, this command will behave like an update, else it will be an insert.
See http://www.sqlite.org/lang_conflict.html for more details.
I currently have this code:
// Construct query
QString const statement = QString("drop database if exists %1")
.arg(databaseName_);
QSqlQuery query(db);
query.exec(statement);
Is there a better way to code than the above?
Specifically, I dont like how I use QString for SQL statement. It'd be nice if Qt has some class so that I could do something like:
// Construct query
QSomeClass statement = "drop database if exists %1";
statement.setArg(1, databaseName_); // Replace all %1 in the original string.
QSqlQuery query(db);
query.exec(statement);
I think you are basically describing query placeholders:
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();
The only difference between the code snipped above and QSomeClass you described is the fact that you have to specify database when creating the query.