I have this sql query. But row with id=1 won't replace.
CREATE TABLE IF NOT EXISTS `vergi_testler` (
`_id` INTEGER PRIMARY KEY AUTOINCREMENT,
`sual` text,
`cavab1` text,
`cavab2` text,
`cavab3` text,
`cavab4` text,
`d_cavab` INTEGER,
`verilib` INTEGER
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_neyse ON vergi_testler (_id);
REPLACE INTO `vergi_testler` VALUES (1, 'aaaa', 'aaa1', 'aaa1', 'aaa3', 'aaa4', 2, 0);
Supply column name INSERT OR REPLACE INTOvergi_testler(_id,sual,cavab1,cavab2,cavab3,cavab4,d_cavab,verilib) with table name in Query
so try
CREATE UNIQUE INDEX IF NOT EXISTS idx_neyse ON vergi_testler (_id,sual,cavab1,cavab2,cavab3,cavab4,d_cavab,verilib);
INSERT OR REPLACE INTO `vergi_testler`(_id,sual,cavab1,cavab2,cavab3,cavab4,d_cavab,verilib) VALUES (1, 'aaaa', 'aaa1', 'aaa1', 'aaa3', 'aaa4', 2, 0);
I found what is the problem. If you want to change something in your database you must do it in onUpgrade() method.
Related
I have an application, where I am detecting the type of a particular column at run-time, on page load. Please refer the below code:
public String fncCheckColumnType(String strColumnName){
db = this.getWritableDatabase();
String strColumnType = "";
Cursor typeCursor = db.rawQuery("SELECT typeof (" + strColumnName +") from tblUsers, null);
typeCursor.moveToFirst();
strColumnType = typeCursor.getString(0);
return strColumnType;
}
The above method simply detects the type of column with column Name 'strColumnName'. I am getting the type of column in this case.
Now, I want to change the column type to TEXT if I am receiving INTEGER as the column type. For this, I tried the below code:
public String fncChangeColumnType(String strColumnName){
db = this.getWritableDatabase();
String newType = "";
Cursor changeCursor = db.rawQuery("ALTER TABLE tblUsers MODIFY COLUMN " + strColumnName + " TEXT", null);
if (changeCursor != null && changeCursor.moveToFirst()){
newType = changeCursor.getString(0);
}
return newType;
}
But while executing the 'fncChangeColumnType' method, I am getting this error, android.database.sqlite.SQLiteException: near "MODIFY": syntax error (code 1): , while compiling: ALTER TABLE tblUsers MODIFY COLUMN UserID TEXT
NOTE: I also replaced 'MODIFY' with 'ALTER', but still getting the same error.
Please check if this is the right method to change the type dynamically.
Please respond back if someone has a solution to this.
Thanks in advance.
In brief, the solution could be :-
Do nothing (i.e. take advantage of SQLite's flexibility)
you could utilise CAST e.g. CAST(mycolumn AS TEXT) (as used below)
Create a new table to replace the old table.
Explanations.
With SQLite there are limitations on what can be altered. In short you cannot change a column. Alter only allows you to either rename a table or to add a column. As per :-
SQL As Understood By SQLite - ALTER TABLE
However, with the exception of a column that is an alias of the rowid column
one defined with ?? INTEGER PRIMARY KEY or ?? INTEGER PRIMARY KEY AUTOINCREMENT or ?? INTEGER ... PRIMARY KEY(??) (where ?? represents a valid column name)
you can store any type of value in any type of column. e.g. consider the following (which stores an INTEGER, a REAL, a TEXT, a date that ends up being TEXT and a BLOB) :-
CREATE TABLE IF NOT EXISTS example1_table (col1 BLOB);
INSERT INTO example1_table VALUES (1),(5.678),('fred'),(date('now')),(x'ffeeddccbbaa998877665544332211');
SELECT *, typeof(col1) FROM example1_table;
The result is :-
As such is there a need to change the column type at all?
If the above is insufficient then your only option is to create a new table with the new column definitions, populate it if required from the original table, and to then replace the original table with the new table ( a) drop original and b)rename new or a) rename original, b) rename new and c) drop original)
e.g. :-
DROP TABLE IF EXISTS original;
CREATE TABLE IF NOT EXISTS original (mycolumn INTEGER);
INSERT INTO original VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(0);
-- The original table now exists and is populated
CREATE TABLE IF NOT EXISTS newtable (mycolumn TEXT);
INSERT INTO newtable SELECT CAST(mycolumn AS TEXT) FROM original;
ALTER TABLE original RENAME TO old_original;
ALTER TABLE newtable RENAME TO original;
DROP TABLE IF EXISTS old_original;
SELECT *,typeof(mycolumn) FROM original;
The result being :-
i think the sql query statement is wrong ,try
ALTER TABLE tblUsers MODIFY COLUMN id TYPE integer USING (id::integer);
instead of id use column name....
hope this helps....
EDIT:
"ALTER TABLE tblUsers MODIFY COLUMN "+strColumnName+" TYPE integer USING ("+strColumnName+"::integer);"
We have a requirement where some fields in a table need to have the same value as their ID. Unfortunately, we currently have to insert a new record and then, if needed, run another update to set the duplicate field (ID_2) value to equal the ID.
Here is the Android Sqlite code:
mDb.beginTransaction();
// ... setting various fields here ...
ContentValues contentValues = new ContentValues();
contentValues.put(NAME, obj.getName());
// now insert the record
long objId = mDb.insert(TABLE_NAME, null, contentValues);
obj.setId(objId);
// id2 needs to be the same as id:
obj.setId2(objId);
// but we need to persist it so we update it in a SECOND call
StringBuilder query = new StringBuilder();
query.append("update " + TABLE_NAME);
query.append(" set " + ID_2 + "=" + objId);
query.append(" where " + ID + "=" + objId);
mDb.execSQL(query.toString());
mDb.setTransactionSuccessful();
As you can see, we are making a second call to set ID_2 to the same value of ID. Is there any way to set it at INSERT time and avoid the second call to the DB?
Update:
The ID is defined as follows:
ID + " INTEGER PRIMARY KEY NOT NULL ," +
The algorithm used for autoincrementing columns is documented, so you could implement it manually in your code, and then use the new value for the INSERT.
This is quite a ugly hack, but it may be possible :
with id_table as (
select coalesce(max(seq), 0) + 1 as id_column
from sqlite_sequence
where name = 'MY_TABLE'
)
insert into MY_TABLE(ID_1, ID_2, SOME, OTHER, COLUMNS)
select id_column, id_column, 'SOME', 'OTHER', 'VALUES'
from id_table
It only works if the table ID is an AUTOINCREMENT, and is therefore managed via the documented sqlite_sequence table.
I also have no idea what happen in case of concurrent executions.
You could use an AFTER INSERT TRIGGER e.g.
Create your table (at least for this example) so that ID_2 is defined as INTEGER DEFAULT -1 (0 or any negative value would be ok)
e.g. CREATE TABLE IF NOT EXISTS triggertest (_id INTEGER PRIMARY KEY ,name TEXT ,id_2 INTEGER DEFAULT -1);
Then you could use something like (perhaps when straight after the table is created, perhaps create it just before it's to be used and drop it after done with it ) :-
CREATE TRIGGER triggertesting001
AFTER INSERT ON triggertest
BEGIN
UPDATE triggertest SET id_2 = `_id`
WHERE id_2 = -1;
END;
Drop using DROP TRIGGER IF EXISTS triggertesting001;
Example usage (testing):-
INSERT INTO triggertest (name) VALUES('Fred');
INSERT INTO triggertest (name) VALUES('Bert');
INSERT INTO triggertest (name) VALUES('Harry');
Result 1 :-
Result 2 (trigger dropped inserts run again ):-
Result 3 (created trigger) same as above.
Result 4 (ran inserts for 3rd time) catch up i.e. 6 rows updated id_2 with _id.
I'd strongly suggest reading SQL As Understood By SQLite - CREATE TRIGGER
Alternative solution
An alternative approach could be to simply use :-
Before starting transaction, retrieve mynextid from table described below
ContentValues contentValues = new ContentValues();
contentValues.put(ID,mynextid);
contentvalues.put(ID_2,mynextid++);
contentValues.put(NAME, obj.getName());
Then at end of the transactions update/store the value of mynextid in a simple single column, single row table.
i.e. you are managing the id's (not too dissimilar to how SQLite manages id's when 'AUTOINCREMENT' is specified)
I am building an Android app with an internal SQLite DB.
Here is the schema:
CREATE TABLE IF NOT EXISTS [tblImageVideoLink] (
[LinkID] INTEGER NOT NULL PRIMARY KEY,
[ImageID] INTEGER UNIQUE NOT NULL,
[VideoID] INTEGER NULL
);
CREATE TABLE IF NOT EXISTS [tblImages] (
[ID] INTEGER PRIMARY KEY NOT NULL,
[ImageName] VARCHAR(50) NOT NULL,
[ImageDescription] VARCHAR(100) NULL,
[Page] INTEGER NULL
);
CREATE TABLE IF NOT EXISTS [tblPages] (
[ID] INTEGER NOT NULL PRIMARY KEY,
[PageName] VARCHAR(30) NULL
);
CREATE TABLE IF NOT EXISTS [tblVideos] (
[ID] INTEGER NOT NULL PRIMARY KEY,
[VideoName] VARCHAR(150) NOT NULL,
[VideoDescription] VARCHAR(100) NULL,
[VideoType] VARCHAR(10) NULL
);
CREATE INDEX IF NOT EXISTS [IDX_TBLIMAGEVIDEOLINK_IMAGEID] ON [tblImageVideoLink](
[ImageID] DESC,
[VideoID] DESC
);
CREATE INDEX IF NOT EXISTS [IDX_TBLIMAGES_PAGE] ON [tblImages](
[Page] DESC
);
Here's the relevant data I have in the tables:
INSERT INTO tblImages (ID, ImageName, Page) VALUES (1, 'Beach.jpg', 1);
INSERT INTO tblImages (ID, ImageName, Page) VALUES (2, 'Bowling.jpg', 1);
INSERT INTO tblImages (ID, ImageName, Page) VALUES (3, 'Car.jpg', 1);
INSERT INTO tblVideos (ID, VideoName) VALUES (2, 'Bowling.3gp');
INSERT INTO tblVideos (ID, VideoName) VALUES (3, 'Car.3gp');
INSERT INTO tblImageVideoLink (LinkID, ImageID, VideoID) VALUES (1, 2, 2);
INSERT INTO tblImageVideoLink (LinkID, ImageID, VideoID) VALUES (2, 3, 3);
INSERT INTO tblPages (ID, PageName) VALUES (1, 'Misc');
I am trying to run this query to get all the images with a certain page, and their related videos:
SELECT DISTINCT I.ID AS 'Image ID', I.ImageName, V.ID AS 'Video ID', V.VideoName
FROM tblImages I
LEFT JOIN tblImageVideoLink L ON L.VideoID=V.ID
LEFT JOIN tblVideos V ON L.ImageID=I.ID
WHERE I.Page=1;
When I test it in SQLite Administrator, I am getting the desired result set, which is:
When I test it in the App (or in SQLiteSpy) I am getting a different result set:
I have tried everything I know, including GROUP BY, removing the DISTINCT, different JOIN types, etc.
BTW, SQLiteSpy writes at the bottom: SQLite 3.7.8 while SQLite Administrator writes SQLite 3.5.1. I don't know if it matters.
Please help, and also kindly explain why there's a difference between two SQLite tools...
What you see in SQLLite Admin is (considering your output) same as
SELECT DISTINCT(I.ImageName), V.VideoName
What you actual seeing in app is (considering your output) same as
SELECT DISTINCT(I.ImageName, V.VideoName)
Try brackets
My dear friend and DBA helped me spot my issue. Here's the working query to JOIN the two tables and get the data set I wanted:
SELECT DISTINCT I.ID AS 'Image ID', I.ImageName, V.ID AS 'Video ID', V.VideoName
FROM tblImages I
LEFT JOIN tblImageVideoLink L ON L.ImageID=I.ID
LEFT JOIN tblVideos V ON L.VideoID=V.ID
WHERE I.Page=1;
Thank you all for trying to help.
I have a 'created' column which is INTEGER and this is how I insert sqlite date into it:
ContentValues vals = new ContentValues();
vals.put(Column._CREATED_DATE, "strftime('%s','now')");
db.insert(Table.ARTICLE.name, null, vals);
Unbelievably he inserts strftime('%s','now') string into INTEGER column!
What the heck?
EDIT: when I run insert into article (created) values (strftime("%s","now")) in an SQLite browser, proper integer value is inserted...
Add a default value to your table instead, e.g.
CREATE TABLE article (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
_created_date INTEGER DEFAULT (strftime('%s', 'now'))
);
This will put unix epoch in _created_date if you do not set its column value when creating the row. If you want a timestamp (e.g. 2012-05-07 13:07:58) you define it as
_created_date INTEGER DEFAULT CURRENT_TIMESTAMP
Pretend I have a table with 2 columns. _id and name. _id is the primary key and I do not want to set this value manually. I want to perform an insert of name="john," and let the program create my own _id. I am unclear what "index" to use when inserting and how many question marks to use. Does this code do the job? Should the index for john be 1 or 2?
String TABLENAME = "table";
SQLiteStatement statement = db.compileStatement("INSERT INTO "+TABLENAME+" VALUES(?);");
statement.bindString(1,"john");
statement.executeInsert();
Next, say I want to manually set my own _id value. Would I change the code to:
String TABLENAME = "table";
SQLiteStatement statement = db.compileStatement("INSERT INTO "+TABLENAME+" VALUES(?,?);");
statement.bindLong(1,666); //Manual _id.
statement.bindString(2,"john");
statement.executeInsert();
Your first example where you provide only the name will not work:
sqlite> create table test (i integer primary key autoincrement, j text);
sqlite> insert into test values ('asd');
Error: table test has 2 columns but 1 values were supplied
sqlite> insert into test values (null, 'asd');
sqlite> select * from test;
1|asd
sqlite> insert into test (j) values ('asd');
sqlite> select * from test;
1|asd
2|asd
so you need to identify the name column as the destination of the sole value this way, (or as you mentioned in your comment pass null):
SQLiteStatement statement = db.compileStatement("INSERT INTO "+TABLENAME+" (name) VALUES(?);");
Your second example should work fine.
This would apply to some table created this way:
create table SomeTable (_id integer primary key autoincrement, name text)
Then
SQLiteStatement statement = db.compileStatement("INSERT INTO "+TABLENAME+" VALUES(null,?);");
statement.bindString(1,"john");
Should also work.