I created a table in the database that has the data like this:
Now i have written a query that updates the contact field by concatinating name and email fields:
UPDATE MyContacts SET contact=(SELECT name||'--'||email FROM MyContacts);
Here the problem is after executing the query the table is as below:
Why is it happening like this? In oracle i never faced this problem. Please help me. Thank you
Right now you're not specifying the correct row to retrieve the values from. Try something like this:
UPDATE MyContacts SET contact = name||'--'||email;
EDIT: Glad it worked. Your first issue was that your sub-select uses a SELECT statement with no WHERE clause (SELECT name||'--'||email FROM MyContacts will return 3 rows). One possible solution would be for SQLite to throw an error and say You've tried to set a column to the result of an expression that returns more than 1 row: I've seen this with MySQL and SQL Server. However, in this case SQLite appears to just use only the very first value returned. However, your second error then kicks in: since you don't narrow your UPDATE statement with a WHERE clause, it uses that first value returned to update EVERY single row, which is what you see.
Related
Interesting issue while using SQLite in Android. I am seeing an inconsistency in the string length and quoting of a string between what is stored in the database and the materialized value seen in Java.
We are using an ORM called SugarORM to query the DB, but I've traced the offending code to the internal android.database.sqlite.SQLiteCursor class used within SugarORM, specifically the cursor.getString(columnIndex) method.
I have a string in the database that is an ISO data string 2019-03-25T19:19:39.664Z and is stored in a VARCHAR column . I have confirmed using DB Browser for SQLite that the length of the string as its stored in the database is indeed 24 characters. SELECT LENGTH(MyStringColumn) FROM MyTable WHERE ...
When I get the value of this string via cursor.getString(columnIndex), it is returning the string "2019-03-25T19:19:39.664Z". Notice the leading and trailing quotes. Java reports to me that the string is 26 characters long.
Any value that I store in this column that is not an ISO data does not have this behavior. I tried tracing the SQLiteCursor source back, but ultimately it ends up being a Native method and that's where my skill set stops.
Can anyone explain what might be going on here? I am probably just going to write a wrapper around my queries to get rid of the quotes, but its all very perplexing. The date string is being fed to a JavaScript interpreter and causing it to fail when creating a JavaScript Date object.
If it helps, I have replicated the behavior on both my S7 physical device and a Pixel 6 emulator.
As a quick get around you could use :-
SELECT length(replace(mystringcolumn,'"','')) FROM mytable;
or before using the original SELECT use :-
UPDATE mytable SET mystringcolumn = replace(mystringcolumn,'"','');
If this doesn't fix the issue, then for some reason it is the code that retrieves the data that is at fault.
e.g. consider :-
DROP TABLE IF EXISTS mytable;
CREATE TABLE IF NOT EXISTS mytable (mystringcolumn VARCHAR);
INSERT INTO mytable VALUES('2019-03-25T19:19:39.664Z'),('"2019-03-25T19:19:39.664Z"');
SELECT length(mystringcolumn), length(replace(mystringcolumn,'"','')) FROM mytable;
which results in :-
i.e. The 2nd row, 2nd column retrieves the appropriate value by using the replace function to strip of the quotes, if they exist.
As to why the quotes exist could depend upon either the way that the data is inserted (perhaps you have inadvertenly coded the quotes but the db being looked at isn't the actual database as copied from the App) or the way in which the data is being retrieved that for some reason adds them.
I don't believe it likely that the Cursor getString method has a bug in which the quotes are added, otherwise such an issue would likely be a recurring issue.
I've got two SQLite databases, each with a table that I need to keep synchronized by merging rows that have the same key. The tables are laid out like this:
CREATE TABLE titles ( name TEXT PRIMARY KEY,
chapter TEXT ,
page INTEGER DEFAULT 1 ,
updated INTEGER DEFAULT 0 );
I want to be able to run the same commands on each of the two tables, with the result that for pairs of rows with the same name, whichever row has the greater value in updated will overwrite the other row completely, and rows which do not have a match are copied across, so both tables are identical when finished.
This is for an Android app, so I could feasibly do the comparisons in Java, but I'd prefer an SQLite solution if possible. I'm not very experienced with SQL, so the more explanation you can give, the more it'll help.
EDIT
To clarify: I need something I can execute at an arbitrary time, to be invoked by other code. One of the two databases is not always present, and may not be completely intact when operations on the other occur, so I don't think a trigger will work.
Assuming that you have attached the other database to your main database:
ATTACH '/some/where/.../the/other/db-file' AS other;
you can first delete all records that are to be overwritten because their updated field is smaller than the corresponding updated field in the other table:
DELETE FROM main.titles
WHERE updated < (SELECT updated
FROM other.titles
WHERE other.titles.name = main.titles.name);
and then copy all newer and missing records:
INSERT INTO main.titles
SELECT * FROM other.titles
WHERE name NOT IN (SELECT name
FROM main.titles);
To update in the other direction, exchange the main/other database names.
For this, you can use a trigger.
i.e.
CREATE TRIGGER sync_trigger
AFTER INSERT OR UPDATE OF updated ON titles
REFERENCING NEW AS n
FOR EACH ROW
DECLARE updated_match;
DECLARE prime_name;
DECLARE max_updated;
BEGIN
SET prime_name = n.name;
ATTACH database2name AS db2;
SELECT updated
INTO updated_match
FROM db2.titles t
WHERE t.name=prime_name)
IF updated_match is not null THEN
IF n.updated > updated_match THEN
SET max_updated=n.updated;
ELSE
SET max_updated=updated_match;
END IF;
UPDATE titles
SET updated=max_updated
WHERE name=prime_name;
UPDATE db2.titles
SET updated=max_updated
WHERE name=prime_name;
END IF;
END sync_trigger;
The syntax may be a little off. I don't use triggers all that often and this is a fairly complex one, but it should give you an idea of where to start at least. You will need to assign this to one database, exchanging "database2name" for the other database's name and then assign it again to the other database, swapping the "database2name" out for the other database.
Hope this helps.
I successfully used the following BEFORE INSERT trigger to limit the number of rows stored in the SQLite database table locations. The database table acts as a cache in an Android application.
CREATE TRIGGER 'trigger_locations_insert'
BEFORE INSERT ON 'locations'
WHEN ( SELECT count(*) FROM 'locations' ) > '100'
BEGIN
DELETE FROM 'locations' WHERE '_id' NOT IN
(
SELECT '_id' FROM 'locations' ORDER BY 'modified_at' DESC LIMIT '100'
);
END
Meanwhile, I added a second trigger that allows me to INSERT OR UPDATE rows. - The discussion on that topic can be found in another thread. The second trigger requires a VIEW on which each INSERTis executed.
CREATE VIEW 'locations_view' AS SELECT * FROM 'locations';
Since an INSERT is no longer executed on the TABLE locations but on the VIEW locations_view, the above trigger does no longer work. If I apply the trigger on the VIEW the following error message is thrown.
Failure 1 (cannot create BEFORE trigger on view: main.locations_view)
Question:
How can I change the above trigger to observe each INSERT on the VIEW - or do you recommend another way to limit the number of rows? I would prefer to handle this kind of operation within the database, rather then running frontend code on my client.
Performance issues:
Although, the limiter (the above trigger) works in general - it performs less then optimal! Actually, the database actions take so long that an ANR is raised. As far as I can see, the reason is, that the limiter is called every time an INSERT happens. To optimize the setup, the bulk INSERT should be wrapped into a transaction and the limiter should perform right after. Is this possible? If you like to help, please place optimization comments concerning the bulk INSERT into the original question. Comments regarding the limiter are welcome here.
This type of trigger should work fine in conjunction with the other one. The problem appears to be that the SQL is unnecessarily quoting the _id field. It is selecting the literal string "_id" for every row and comparing that to the same literal string.
Removing the quotes around '_id' (both in the DELETE and in the sub-SELECT) should fix the problem.
I'm having an issue performing normal SELECT statements (that is, not the 'searches' they were designed to do- I'm not using MATCH). What I'm trying to do is query the table for all rows of an INTEGER column. However, the SELECT statement always returns no rows. I've inspected the database with an SQLite browser and the query should work. Here it is:
Here's the MAKE TABLE statement:
CREATE VIRTUAL TABLE FTS_journal USING fts3(journal_id INTEGER, journal_text_col TEXT)
And here's the SELECT:
SELECT journal_id FROM FTS_journal
Does it have something to do with my running a 'normal' query over a virtual table? I can't really think of any other reason
EDIT: I'm using Android's version of SQLite, meaning SQLite3. I know it supports FTS properly...
If any further information I can provide will help, please tell me and I will post it.
What you've written should work. Are you 100% positive you've actually inserted something into the table? Have you ran insert statements, and if so, are you sure they were successful?
I tested your statements, running the create statement, then the following statement:
insert into FTS_journal (journal_id, journal_text_col) values (1, 'test');
... then your select statement, and sqlite returned 1 row (journal_id, value 1) just as it should.
I verified using SQLite Database Browser that the table contains no rows. I stepped through the query builder code to get the generated query and ran the query in SQLite Database Broswer. The query returned zero rows. But still, the cursor returned by Android's SQLiteQueryBuilder.query method returns true on cursor.moveToFirst() call and returns null values.
Anyone seen something like this before?
OK: I figured this is because I'm using a MAX aggregation function in the query. This could be a bug, may be? I now use a sort with a limit clause instead of MAX and worked around.
I think you should do something like:
if (c.getCount()>0) {
c.moveToFirst();
moveToFirst is actually implemented as moveToPosition(0). moveToPosition is a final method defined in AbstractCursor. Looking at the code, you can see that the result is partially depentandt on getCount. and it seems that in your case getCount returns a non-zero value.
See what's the value of the '_id' column, and try to delete it. Alternatively, try to call that code after re-creating the database (calling 'drop table' and then 'create table').