I have a column with a NOT NULL constraint. I need to alter the database to remove the constraint. But when I try I get a duplicate column exception from android
db.execSQL("ALTER TABLE " + AnimalContract.DogEntry.TABLE_NAME
+ " ADD COLUMN " + AnimalContract.DogEntry.COLUMN_NAME+" TEXT DEFAULT NULL");
I also tried without DEFAULT
The constraint was UNIQUE NOT NULL
There's no direct way to ALTER COLUMN in SQLite.
I believe your only option is to:
Rename the table to a temporary name
Create a new table without the NOT NULL constraint
Copy the content of the old table to the new one
Remove the old table
This other Stackoverflow answer explains the process in details
Furthermore,
MODIFY COLUMN IN TABLE
You can not use the ALTER TABLE statement to modify a column in SQLite. Instead you will need to rename the table, create a new table, and copy the data into the new table.
Syntax
The syntax to MODIFY A COLUMN in a table in SQLite is:
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
ALTER TABLE table1 RENAME TO _table1_old;
CREATE TABLE table1 (
( column1 datatype [ NULL | NOT NULL ],
column2 datatype [ NULL | NOT NULL ],
...
);
INSERT INTO table1 (column1, column2)
SELECT column1, column2
FROM _table1_old;
COMMIT;
PRAGMA foreign_keys=on;
Example
Let's look at an example that shows how to modify a column in a SQLite table.
For example, if we had an employees table that had a column called last_name that was defined as a CHAR datatype:
CREATE TABLE employees
( employee_id INTEGER PRIMARY KEY AUTOINCREMENT,
last_name CHAR NOT NULL,
first_name VARCHAR,
hire_date DATE
);
And we wanted to change the datatype of the last_name field to VARCHAR, we could do the following:
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
ALTER TABLE employees RENAME TO _employees_old;
CREATE TABLE employees
( employee_id INTEGER PRIMARY KEY AUTOINCREMENT,
last_name VARCHAR NOT NULL,
first_name VARCHAR,
hire_date DATE
);
INSERT INTO employees (employee_id, last_name, first_name, hire_date)
SELECT employee_id, last_name, first_name, hire_date
FROM _employees_old;
COMMIT;
PRAGMA foreign_keys=on;
This example will rename our existing employees table to _employees_old.
Then it will create the new employees table with the last_name field defined as a VARCHAR datatype. Then it will insert all of the data from the _employees_old table into the employees table.
Related
I'm trying to copy the data from one table to other. Please find the below code:
//Dummy table for reference
database.execSQL("CREATE TABLE IF NOT EXISTS `New_Temp1` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `data1` TEXT DEFAULT '', `data2` INTEGER NOT NULL, `data3` TEXT DEFAULT '', `data4` TEXT DEFAULT '', `data5` TEXT DEFAULT '')");
database.execSQL("INSERT INTO New_Temp1 SELECT * FROM Temp1");
database.execSQL("DROP TABLE Temp1");
database.execSQL("ALTER TABLE New_Temp1 RENAME TO Temp1");
The Temp1 table is already there in database. I don't know the schema of that. Means I don't know the columns of that table. So I am creating a new table New_temp1. When I try to copy the data from Temp1 to New_Temp1, application crashes.
**Caused by: android.database.sqlite.SQLiteException: table New_Temp1 has 33 columns but 28 values were supplied (code 1): , while compiling: INSERT INTO New_Temp1 SELECT * FROM Temp1**
How to solve this, I don't know which columns are missing in Temp1 table.
This statement raises the error:
INSERT INTO New_Temp1 SELECT * FROM Temp1
It looks like the target table does not have the same columns as the original (there are more columns in the target).
If you want to copy the table without knowing the structure, you can combine the table creation and the copy in a single statement like that:
CREATE TABLE New_Temp1 AS
SELECT * FROM Temp1
On the other hand, if you don't want to copy all the columns, and you know the corresponding column names in the source table, then you can enumerate them in the select clause:
INSERT INTO New_Temp1 (data1, data2, data3)
SELECT data1, data2, data3 FROM Temp1
You can get the column names of the table Temp1 with this query:
SELECT name FROM pragma_table_info('Temp1')
You can execute the above statement with rawQuery() and by iterating through the rows of the cursor you will have all the column names and you can use them to create the new table.
If you execute:
SELECT * FROM pragma_table_info('Temp1')
you will get additional info for each column, like:
cid name type notnull dflt_value pk
0 column1 INTEGER 0 0
1 column2 INTEGER 0 0
...........................................
I migrate my database from SQLiteOpenHelper to Room.
I have a table that I want to change, lets call it "my_table".
Its simplified create statement:
CREATE TABLE `my_table`
(`_id` INTEGER PRIMARY KEY AUTOINCREMENT,
`title` TEXT
)
During an upgrade among other changes I add the new column type INTEGER NOT NULL (I'm adding Foreign Key aswell and doing other significant changes, that's the reason to create a new table instead of altering the existing one):
CREATE TABLE "new_table"
(`_id` INTEGER PRIMARY KEY AUTOINCREMENT,
`title` TEXT,
`type` INTEGER NOT NULL
)
Then I want to copy data from the my_table to the new_table and set type column's values.
SQL statement:
INSERT INTO new_table (title)
SELECT title FROM my_table;
UPDATE new_table SET type = 1;
DROP TABLE my_table;
ALTER TABLE new_table RENAME TO my_table;
Android migration:
public static final Migration MIGRATION_TEST = new Migration(1, 2) {
#Override
public void migrate(#NonNull SupportSQLiteDatabase database) {
// Create new table
database.execSQL("CREATE TABLE new_table (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT, `type` INTEGER NOT NULL)");
// Copy some data
database.execSQL("INSERT INTO new_table (title) SELECT title FROM old_table"); // constraint violation
// Insert default value into the measures column
database.execSQL("UPDATE new_table SET type = 1");
// Delete old table
database.execSQL("DROP TABLE old_table");
// Rename new table
database.execSQL("ALTER TABLE new_table RENAME TO my_table");
}
};
Obviously I get NOT NULL constraint failed: new_table.type error:
android.database.sqlite.SQLiteConstraintException: NOT NULL constraint failed: new_table.type (code 1299)
Error Code : 1299 (SQLITE_CONSTRAINT_NOTNULL)
Caused By : Abort due to constraint violation.
(NOT NULL constraint failed: new_table.type (code 1299))
I can avoid it by changing new table's create statement and setting default value for the type column.
CREATE TABLE "new_table"
(`_id` INTEGER PRIMARY KEY AUTOINCREMENT,
`title` TEXT,
`type` INTEGER NOT NULL DEFAULT 1
)
But I don't want to do this as Room doesn't suport default values out of the box and in order to avoid future mistakes when inserting new values into tables.
Are there any workarounds to avoid this error while inserting data to a new table?
I think the following may work :-
database.execSQL("INSERT INTO new_table (title,type) SELECT title, 1 FROM old_table");
That is, you are now saying to INSERT 2 columns as per the SELECT statement. The SELECT returns 2 values the title from the old_table and the literal value 1.
That is SELECT actual returns the result of expressions result-column which aren't limited to just columns. An Expression can be literal value, result of functions, results of operations and other expr
As per
The list of expressions between the SELECT and FROM keywords is known
as the result expression list.
SQL As Understood By SQLite - SELECT - 3. Generation of the set of result rows.
You then wouldn't need database.execSQL("UPDATE new_table SET type = 1").
I have a SQLite database with one table which I need to update in a new version of my database.
In the first version of the table I have a id and a txt value like this:
db.execSQL("CREATE TABLE table_name id INTEGER PRIMARY KEY AUTOINCREMENT, txt TEXT")
But in the next version of the database I wand to add a lastchanged value which must be a TIMESTAMP with default value current time in epoch which I do with (strftime('%s', 'now')).
But since I can't add a column with default current time, according to this error:
Cannot add a column with non-constant default (code 1): , while compiling: ALTER TABLE table_name ADD COLUMN edited_time TIMESTAMP DEFAULT (strftime('%s', 'now'))
I tought I could best recreate the data and copy it to the new database
// copy all data to single value something like a Cursor res maybe??
db.execSQL("DROP TABLE IF EXISTS table_name );
db.execSQL("CREATE TABLE table_name id INTEGER PRIMARY KEY AUTOINCREMENT, txt TEXT, edited_time TIMESTAMP DEFAULT (strftime('%s', 'now'))");
// now insert the values into the recreated database
But for this i need to copy all data into a new table that does not exist simultanious. Is it posible to do this without looping and having to save every value separate and then inserting them seperately again?
Yes. The general procedure is:
BEGIN;
ALTER TABLE table_name RENAME TO table_name_old;
CREATE TABLE table_name( ... );
INSERT INTO table_name(col1, ..., coln)
SELECT col1, ..., coln FROM table_name_old;
DROP table_name_old;
COMMIT;
Could anybody tell me how I can change the datatype in SQLite from android?
For example, I want to change varchar(2000) of a column diary_content in table diary to TEXT. How it is possible?
Trick that might solve your issue,
You can try something like
Ex:
To change the datatype of the last_name field to VARCHAR, could do the following:
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
ALTER TABLE employees RENAME TO _employees_old;
CREATE TABLE employees
( employee_id INTEGER PRIMARY KEY AUTOINCREMENT,
last_name VARCHAR NOT NULL,
first_name VARCHAR,
hire_date DATE
);
INSERT INTO employees (employee_id, last_name, first_name, hire_date)
SELECT employee_id, last_name, first_name, hire_date
FROM _employees_old;
COMMIT;
PRAGMA foreign_keys=on;
Rename existing employees table to _employees_old. Then it will create the new employees table with the last_name field defined as a VARCHAR datatype. Then it will insert all of the data from the _employees_old table into the employees table.
Check SQLite for more details.
I copied an SQLite Database example, where I created a table with 3 fields/columns and inserted one record with 3 values, as follows:
>SQLiteDatabase db = openOrCreateDatabase("MyDB",MODE_PRIVATE,null);
>db.execSQL("CREATE TABLE IF NOT EXISTS FunnyNames(Email VARCHAR(255), FirstName VARCHAR(255), LastName VARCHAR(255));");
>db.execSQL("INSERT INTO FunnyNames VALUES('abath#aol.com','Anita','Bath')");
..and I received this error message
>12-28 11:35:00.896: E/SQLiteLog(8857): (1) table FunnyNames has 1 columns but 3 values were supplied
didn't I define the columns correctly?
IF NOT EXISTS only creates the table if it didn't exist. Likely you already have a table with that given name in the database file, and that table only has 1 column.
Uninstall your app or clear its data in the app manager to remove the old database file. Or add DROP TABLE IF EXISTS FunnyNames before the CREATE TABLE.
You've missed the primary key of the table. Every table needs an primary key (which is an unique id) for referencing data. Modify your create statement as follow:
CREATE TABLE IF NOT EXISTS FunnyNames(_id INTEGER PRIMARY KEY AUTOINCREMENT, Email VARCHAR(255), FirstName VARCHAR(255), LastName VARCHAR(255));