SQLite3 generated column does not show up in QSqlRelationalTableModel - sqlite

I have a QTableView connected to a QSqlRelationalTableModel. Everything works as expected, however, it seems to be impossible to have a sqlite3 generated column in my model:
bhd [INTEGER] GENERATED ALWAYS AS (round(d_mess * 130 / bhd_hoehe)) STORED
The above statement is the default way to add a generated column in sqlite3. But it does not appear in my model. When I change the column to a standard integer column, it's there.
It does not matter if the generated column is of type STORED or VIRTUAL or if I omit the GENERATED ALWAYS keyword.
Is this by design? Or a sqlite3 driver problem?

Views seem not to be affected by this issue. I'm aware it's more a workaround than a solution, but you could create a dummy view just with your foreign key and the generated field.
For example, I have a simple table User like this:
CREATE TABLE User (
ID INTEGER PRIMARY KEY AUTOINCREMENT
UNIQUE,
name TEXT,
surname TEXT,
title TEXT,
full_name GENERATED ALWAYS AS (title || ' ' || surname || ' ' || name) STORED
);
As is, no QSqlRelation pointing to User.full_name will work, as you pointed out. However, I've created a simple view like
CREATE VIEW UserFullNameView AS
SELECT ID,
full_name
FROM User;
Now the following binding works fine
model: QSqlRelationalTableModel
model.setRelation(foreign_field, QSqlRelation('UserFullNameView', 'ID', 'full_name')

Related

SQLite Copy Table Content from one Table to another Table and Update

I have two SQLite files, each of them has one table and the same table design. One Column is set as Primary Key. I want to copy all data from ItemsB into ItemsA. All data should be updated. The ItemsB Table is the newer one.
I've tried:
ATTACH DATABASE ItemsB AS ItemsB;
INSERT INTO ItemsA.PMItem (ItemID,VarID,Name1) SELECT ItemID,VarID,Name1 FROM ItemsB.PMItem;
Obviously this can't work due the Primary Key (which is the column VarID).
Then I tried it with ON CONFLICT:
ON CONFLICT (VarID) DO UPDATE SET Name1=excluded.Name1
But this won't work either.
Example Table:
CREATE TABLE PMItem (
ItemID INTEGER,
VarID INTEGER PRIMARY KEY,
Name1 TEXT
);
You need a WHERE clause with an always true condition, to overcome the ambiguity that is raised when ON CONFLICT is used after a SELECT statement:
INSERT INTO PMItem (ItemID,VarID,Name1)
SELECT ItemID,VarID,Name1
FROM ItemsB.PMItem
WHERE 1
ON CONFLICT(VarID) DO UPDATE
SET Name1 = EXCLUDED.Name1;

using an expression as table name in sqlite

I am trying to check if a table exists prior to send a SELECT query on that table.
The table name is composed with a trailing 2 letters language code and when I get the full table name with the user's language in it, I don't know if the user language is actually supported by my database and if the table for that language really exists.
SELECT name FROM sqlite_master WHERE name = 'mytable_zz' OR name = 'mytable_en' ORDER BY ( name = 'mytable_zz' ) DESC LIMIT 1;
and then
SELECT * FROM table_name_returned_by_first_query;
I could have a first query to check the existence of the table like the one above, which returns mytable_zz if that table exists or mytable_en if it doesn't, and then make a second query using the result of the first as table name.
But I would rather have it all in one single query that would return the expected results from either the user's language table or the english one in case his language is not supported, without throwing a "table mytable_zz doesn't exist" error.
Anyone knows how I could handle this ?
Is there a way to use the result of the first query as a table name in the 2nd ?
edit : I don't have the hand of the database itself which is generated automatically, I don't want to get involved in a complex process of manually updating any new database that I get. Plus this query is called multiple times and having to retrieve the result of a first query before launching a second one is too long. I use plain text queries that I send through a SQLite wrapper. I guess the simplest would rather be to check if the user's language is supported once for all in my program and store a string with either the language code of the user or "en" if not supported, and use that string to compose my table name(s). I am going to pick that solution unless someone has a better idea
Here is a simple MRE :
CREATE TABLE IF NOT EXISTS `lng_en` ( key TEXT, value TEXT );
CREATE TABLE IF NOT EXISTS `lng_fr` ( key TEXT, value TEXT );
INSERT INTO `lng_en` ( key , value ) VALUES ( 'question1', 'What is your name ?');
INSERT INTO `lng_fr` ( key , value ) VALUES ( 'question1', 'Quel est votre nom ?');
SELECT `value` FROM lng_%s WHERE `key` = 'question1';
where %s is to be replaced by the 2 letters language code. This example will work if the provided code is 'en' or 'fr' but will throw an error if the code is 'zh', in this case I would like to have the same result returned as with 'en' ....
Not in SQL, without executing it dynamically.. But if this is your front end that is running this SQL then it doesn't matter so much. Because your table name came out of the DB there isn't really any opportunity for SQL injection hacking with it:
var tabName = db.ExecuteScalar("SELECT name FROM sqlite_master WHERE name = 'mytable_zz' OR name = 'mytable_en' ORDER BY ( name = 'mytable_zz' ) DESC LIMIT 1;")
var results = db.ExecuteQuery("SELECT * FROM " + tabName);
Yunnosch's comment is quite pertinent; you're essentially storing in a table name information that really should be in a column.. You could consider making a single table and then a bunch of views like mytable_zz the definition of which is SELECT * FROM mytable WHERE lang = 'zz' etc, and make instead-of triggers if you want to cater for a legacy app that you cannot change; the legacy app would select from / insert into the views thinking they are tables, but in reality your data is single table and easier to manage

Cassandra Select Query using Collection Primary Key in Where Clause

I'm new to Cassandra and I created a table with a frozen collection as the primary key
cqlsh> create table rick_morty (id uuid, name text, adventure text, instigator frozen<set<text>>, PRIMARY KEY((instigator), adventure, name, id));
Now I want to query based on the primary key (instigator) for all of the values held in the collection. I have found that if I just wanted to query on 1 value, I can use CONTAINS 'contained_value', but I want to query on the entire collection.
I've been looking all over to figure out how to do this but I can't find the answer.
Doing something like
const query = 'SELECT name from rick_morty';
retrieves all results but I want to do something like...
const query = 'SELECT name from rick_morty where instigator = ["Rick", "Morty", "Beth"]';
to retrieve all list of names associated with that array of instigators.
Is this possible?? Did I just create my table in an improper way?
Is this possible??
Yes. See #8 here.
"Filter data on a column of a user-defined type. Create an index and then run a conditional query. In Cassandra 2.1.x, you need to list all components of the name column in the WHERE clause."
This should work:
SELECT name from rick_morty where instigator = { 'Rick', 'Morty', 'Beth'};
The following query should work,
SELECT name from rick_morty where instigator contains 'Rick' AND contains 'Morty';
But, This may not be an efficient/proper way to implement as Sets are meant to be used to store/get a set of data for a given primary key.
So, I would recommend you to re-design the data model by denormolise the query into a an additional table in case if this requirement is one of your primary use case.

SQLite Insert or Ignore/Replace with lastInsertID in one statement

I feel like this should be easy...
I have a table like this:
CREATE TABLE table_name (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
UNIQUE (name COLLATE NOCASE)
)
No two names are the same, case insensitive. Right now I have users adding names and it does this:
INSERT INTO table_name (name) VALUES ("my name");
And I need to get the id of the row, which is easy with PHP PDO's lastInsertID(). But I also want, if the user is adding a name that's already in the database, for nothing to be added to the database, but still get that id without having to do another database call. I was hoping for something like
INSERT OR REPLACE INTO table_name (name) VALUES ("my name");
And have it just overwrite the same data into the cell and return the lastInsertID (even though it wasn't inserted?). But that doesn't work. What are my other options? Will I have to do a separate database query to see if the name field already exists?
With the OR REPLACE clause, the statement always deletes any old row.
Just use two statements. (There is no technical reason for doing this in a single statement.)

Why two column created of same name in angular + ionic

I am creating a table inside the DB.I am a successfully created a table and able to insert data on table my issue is that in my table I have two "ID" columns but I only created one .why two ID column generate in table.
Follow this step to generate issue :-
Click the bottom right button (enter the text in pop up screen ).Press "add" button .It generate the row in a "cases" table but when you inspect it show two column of "ID" why ?
Here is my code
http://codepen.io/anon/pen/OVPgoP
db.transaction(function(tx) {
tx.executeSql('CREATE TABLE "Cases" ("ID" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , "CaseName" VARCHAR NOT NULL )');
})
It appears that this is the expected behaviour for SQLite, the technology underpinning Web SQL (which is a deprecated technology, btw).
From the docs:
If a table contains a column of type INTEGER PRIMARY KEY, then that column becomes an alias for the ROWID. You can then access the ROWID using any of four different names, the original three names described above or the name given to the INTEGER PRIMARY KEY column. All these names are aliases for one another and work equally well in any context.
Therefore, my guess is that the Chrome devtools are showing the rowid column using its alias (ID), in addition to the alias column explicitly added (ID).
It seems like you don't actually need to explicitly add an ID column with Web SQL/SQLite. It will add one for you, which you can refer to using rowid, _rowid_ or oid in any statement.
EDIT: Here is a fork of your CodePen, with updates and deletes all correctly working.
Totally new to webSQL, so this answer is very useful to me as well. You can't expect primary keys to work properly with webSQL because internally it tracks something called "rowid" as the primary key. Use unique instead, as seen in a useful example here. You can also use this error code from the spec to figure out if a non-unique column value was inserted.

Resources