Add not null DateTime column to SQLite without default value? - sqlite

I can't add a not null constraint or remove a default constraint. I would like to add a datetime column to a table and have all the values set to anything (perhaps 1970 or year 2000) but it seems like i cant use not null without a default and I cant remove a default once added in. So how can i add this column? (once again just a plain datetime not null)

Instead of using ALTER TABLE ADD COLUMN, create a new table that has the extra column, and copy your old data. This will free you from the restrictions of ALTER TABLE and let you have a NOT NULL constraint without a default value.
ALTER TABLE YourTable RENAME TO OldTable;
CREATE TABLE YourTable (/* old cols */, NewColumn DATETIME NOT NULL);
INSERT INTO YourTable SELECT *, '2000-01-01 00:00:00' FROM OldTable;
DROP TABLE OldTable;
Edit: The official SQLite documentation for ALTER TABLE now warns against the above procedure because it “might corrupt references to that table in triggers, views, and foreign key constraints.” The safe alternative is to use a temporary name for the new table, like this:
CREATE TABLE NewTable (/* old cols */, NewColumn DATETIME NOT NULL);
INSERT INTO NewTable SELECT *, '2000-01-01 00:00:00' FROM YourTable;
DROP TABLE YourTable;
ALTER TABLE NewTable RENAME TO YourTable;

Related

How to create a Teradata table by copying and modifying another Table?

I am trying to create a new Teradata table by copying another table, but also need to add one new column, based on a condition of another column from the old table while copying, can you help me on the code?
create Table new_table as
(select *
from old_table) with data
ALTER TABLE new_table ADD new_col varchar(20) check(new_col in ('National', 'Local')
-- there is a column in the old_table with value ( 'Y', 'N'), how can i create the new column in the new_table with this condition: if Y new_col=national, if N, new_col=local?
Thank you.
You can't create a check constraint that will immediately be violated. Also note that CREATE TABLE AS (SELECT ...) results in all columns being nullable, and if you don't explicitly state a Primary Index the new table will use system default, e.g. first column alone as PI. A CASE expression can be used to populate the new column.
One possible sequence:
create Table new_table as old_table with no data; -- copy index definitions, NOT NULL attributes
ALTER TABLE new_table ADD new_col varchar(20) check(new_col in ('National', 'Local'));
INSERT new_table SELECT o.*,
CASE WHEN o.old_col = 'Y' THEN 'National' ELSE 'Local' END
FROM old_table o;

cannot drop NOT NULL constraint on a DEFAULT ON NULL column

I have a table A with schema.
Name Null? Type
-------- -------- ------------
NAME VARCHAR2(10)
TABLE_ID NOT NULL NUMBER
I wanted Table_ID to be an auto-incrementing column so I created a sequence.
create sequence table_id minvalue 1 start with 1 cache 10;
and I modified the Table_ID column as
alter table A
modify table_id default on null table_id.nextval;
now I cannot drop the table or the column or the constraint.
It is giving me this error.
An error was encountered performing the requested operation:
ORA-30667: cannot drop NOT NULL constraint on a DEFAULT ON NULL column
30667.0000 - "cannot drop NOT NULL constraint on a DEFAULT ON NULL column"
*Cause: The NOT NULL constraint on a DEFAULT ON NULL column could not be
dropped.
*Action: Do not drop the NOT NULL constraint on a DEFAULT ON NULL column.
The only way to drop the constraint is to remove the ON NULL
property of the column default.
Vendor code 30667
I have tried to purge the recyclebin but it is not working either.
I read other posts but none of this seems to make sense.
Please help.
If you just need to remove the constraint, just set a default value on that column. The constraint will be removed automatically:
alter table a modify table_id default 0;
Full example:
create table a(name varchar2(10), table_id number not null);
create sequence table_id minvalue 1 start with 1 cache 10;
alter table a modify table_id default on null table_id.nextval;
select * from user_constraints where table_name = 'A'; -- one constraint "TABLE_ID" IS NOT NULL
alter table a drop constraint SYS_C0026367; -- ORA-30667: cannot drop NOT NULL constraint on a DEFAULT ON NULL column
alter table a modify table_id default 0;
alter table a drop constraint SYS_C0026367; -- ORA-02443: Cannot drop constraint - nonexistent constraint
select * from user_constraints where table_name = 'A'; -- the constraint was dropped automatically
I ran this code on Oracle Database 19c Standard Edition 2 Release 19.0.0.0.0 - Production and it worked fine:
create table a(name varchar2(10), table_id number not null);
create sequence table_id minvalue 1 start with 1 cache 10;
alter table a modify table_id default on null table_id.nextval;
drop table a;
Table A created.
Sequence TABLE_ID created.
Table A altered.
Table A dropped.

How to have an automatic timestamp in SQLite?

I have an SQLite database, version 3 and I am using C# to create an application that uses this database.
I want to use a timestamp field in a table for concurrency, but I notice that when I insert a new record, this field is not set, and is null.
For example, in MS SQL Server if I use a timestamp field it is updated by the database and I don't have to set it by myself. Is this possible in SQLite?
Just declare a default value for a field:
CREATE TABLE MyTable(
ID INTEGER PRIMARY KEY,
Name TEXT,
Other STUFF,
Timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);
However, if your INSERT command explicitly sets this field to NULL, it will be set to NULL.
You can create TIMESTAMP field in table on the SQLite, see this:
CREATE TABLE my_table (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name VARCHAR(64),
sqltime TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
INSERT INTO my_table(name, sqltime) VALUES('test1', '2010-05-28T15:36:56.200');
INSERT INTO my_table(name, sqltime) VALUES('test2', '2010-08-28T13:40:02.200');
INSERT INTO my_table(name) VALUES('test3');
This is the result:
SELECT * FROM my_table;
Reading datefunc a working example of automatic datetime completion would be:
sqlite> CREATE TABLE 'test' (
...> 'id' INTEGER PRIMARY KEY,
...> 'dt1' DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime')),
...> 'dt2' DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now', 'localtime')),
...> 'dt3' DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%d %H:%M:%f', 'now', 'localtime'))
...> );
Let's insert some rows in a way that initiates automatic datetime completion:
sqlite> INSERT INTO 'test' ('id') VALUES (null);
sqlite> INSERT INTO 'test' ('id') VALUES (null);
The stored data clearly shows that the first two are the same but not the third function:
sqlite> SELECT * FROM 'test';
1|2017-09-26 09:10:08|2017-09-26 09:10:08|2017-09-26 09:10:08.053
2|2017-09-26 09:10:56|2017-09-26 09:10:56|2017-09-26 09:10:56.894
Pay attention that SQLite functions are surrounded in parenthesis!
How difficult was this to show it in one example?
Have fun!
you can use triggers. works very well
CREATE TABLE MyTable(
ID INTEGER PRIMARY KEY,
Name TEXT,
Other STUFF,
Timestamp DATETIME);
CREATE TRIGGER insert_Timestamp_Trigger
AFTER INSERT ON MyTable
BEGIN
UPDATE MyTable SET Timestamp =STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') WHERE id = NEW.id;
END;
CREATE TRIGGER update_Timestamp_Trigger
AFTER UPDATE On MyTable
BEGIN
UPDATE MyTable SET Timestamp = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') WHERE id = NEW.id;
END;
To complement answers above...
If you are using EF, adorn the property with Data Annotation [Timestamp], then
go to the overrided OnModelCreating, inside your context class, and add this Fluent API code:
modelBuilder.Entity<YourEntity>()
.Property(b => b.Timestamp)
.ValueGeneratedOnAddOrUpdate()
.IsConcurrencyToken()
.ForSqliteHasDefaultValueSql("CURRENT_TIMESTAMP");
It will make a default value to every data that will be insert into this table.
you can use the custom datetime by using...
create table noteTable3
(created_at DATETIME DEFAULT (STRFTIME('%d-%m-%Y %H:%M', 'NOW','localtime')),
title text not null, myNotes text not null);
use 'NOW','localtime' to get the current system date else it will show some past or other time in your Database after insertion time in your db.
Thanks You...
If you use the SQLite DB-Browser you can change the default value in this way:
Choose database structure
select the table
modify table
in your column put under 'default value' the value: =(datetime('now','localtime'))
I recommend to make an update of your database before, because a wrong format in the value can lead to problems in the SQLLite Browser.

SQLite storing default timestamp as unixepoch

When defining a relation, I want to update an attribute to the timestamp at insert. For example, a working table that I have right now
CREATE TABLE t1(
id INTEGER PRIMARY KEY AUTOINCREMENT,
time TIMESTAMP
DEFAULT CURRENT_TIMESTAMP,
txt TEXT);
This is updating a timestamp on insert, for example, insert into t1 (txt) values ('hello') adds the row 1|2012-07-19 08:07:20|hello|. However, I want to have this date formatted in unixepoch format.
I read the docs but this wasn't clear. For example, I modified the table relation to time TIMESTAMP DEFAULT DATETIME('now','unixepoch') but I get an error. Here, as in the docs, now was my time string and unixepoch was the modifier but it didn't work. Could someone help me how to format it as a unixepoch timestamp?
Use strftime:
sqlite> select strftime('%s', 'now');
1342685993
Use it in CREATE TABLE like this:
sqlite> create table t1 (
...> id integer primary key,
...> time timestamp default (strftime('%s', 'now')),
...> txt text);
sqlite> insert into t1 (txt) values ('foo');
sqlite> insert into t1 (txt) values ('bar');
sqlite> insert into t1 (txt) values ('baz');
sqlite> select * from t1;
1|1342686319|foo
2|1342686321|bar
3|1342686323|baz
See https://www.sqlite.org/lang_createtable.html#tablecoldef
If the default value of a column is an expression in parentheses, then the expression is evaluated once for each row inserted and the results used in the new row.
Note 'timestamp' is not a data type known to SQLite (see list here). The default value generated by strftime() would actually be stored as Text.
If it is important to store the value as a number instead of as a string, declare the field as an Integer and add a CAST() into the mix, like so:
create table t1(
...
ts_field integer(4) default (cast(strftime('%s','now') as int)),
...
);
Indeed strftime, which can also be used like so:
SELECT strftime('%s', timestamp) as timestamp FROM ... ;
Gives you:
1454521888
'timestamp' table column can be a text field even, using the current_timestamp as DEFAULT.
Without strftime:
SELECT timestamp FROM ... ;
Gives you:
2016-02-03 17:51:28

INSERT IF NOT EXISTS ELSE UPDATE?

I've found a few "would be" solutions for the classic "How do I insert a new record or update one if it already exists" but I cannot get any of them to work in SQLite.
I have a table defined as follows:
CREATE TABLE Book
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Name VARCHAR(60) UNIQUE,
TypeID INTEGER,
Level INTEGER,
Seen INTEGER
What I want to do is add a record with a unique Name. If the Name already exists, I want to modify the fields.
Can somebody tell me how to do this please?
Have a look at http://sqlite.org/lang_conflict.html.
You want something like:
insert or replace into Book (ID, Name, TypeID, Level, Seen) values
((select ID from Book where Name = "SearchName"), "SearchName", ...);
Note that any field not in the insert list will be set to NULL if the row already exists in the table. This is why there's a subselect for the ID column: In the replacement case the statement would set it to NULL and then a fresh ID would be allocated.
This approach can also be used if you want to leave particular field values alone if the row in the replacement case but set the field to NULL in the insert case.
For example, assuming you want to leave Seen alone:
insert or replace into Book (ID, Name, TypeID, Level, Seen) values (
(select ID from Book where Name = "SearchName"),
"SearchName",
5,
6,
(select Seen from Book where Name = "SearchName"));
You should use the INSERT OR IGNORE command followed by an UPDATE command:
In the following example name is a primary key:
INSERT OR IGNORE INTO my_table (name, age) VALUES ('Karen', 34)
UPDATE my_table SET age = 34 WHERE name='Karen'
The first command will insert the record. If the record exists, it will ignore the error caused by the conflict with an existing primary key.
The second command will update the record (which now definitely exists)
You need to set a constraint on the table to trigger a "conflict" which you then resolve by doing a replace:
CREATE TABLE data (id INTEGER PRIMARY KEY, event_id INTEGER, track_id INTEGER, value REAL);
CREATE UNIQUE INDEX data_idx ON data(event_id, track_id);
Then you can issue:
INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 2, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 5);
The "SELECT * FROM data" will give you:
2|2|2|3.0
3|1|2|5.0
Note that the data.id is "3" and not "1" because REPLACE does a DELETE and INSERT, not an UPDATE. This also means that you must ensure that you define all necessary columns or you will get unexpected NULL values.
INSERT OR REPLACE will replace the other fields to default value.
sqlite> CREATE TABLE Book (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT,
TypeID INTEGER,
Level INTEGER,
Seen INTEGER
);
sqlite> INSERT INTO Book VALUES (1001, 'C++', 10, 10, 0);
sqlite> SELECT * FROM Book;
1001|C++|10|10|0
sqlite> INSERT OR REPLACE INTO Book(ID, Name) VALUES(1001, 'SQLite');
sqlite> SELECT * FROM Book;
1001|SQLite|||
If you want to preserve the other field
Method 1
sqlite> SELECT * FROM Book;
1001|C++|10|10|0
sqlite> INSERT OR IGNORE INTO Book(ID) VALUES(1001);
sqlite> UPDATE Book SET Name='SQLite' WHERE ID=1001;
sqlite> SELECT * FROM Book;
1001|SQLite|10|10|0
Method 2
Using UPSERT (syntax was added to SQLite with version 3.24.0 (2018-06-04))
INSERT INTO Book (ID, Name)
VALUES (1001, 'SQLite')
ON CONFLICT (ID) DO
UPDATE SET Name=excluded.Name;
The excluded. prefix equal to the value in VALUES ('SQLite').
Firstly update it. If affected row count = 0 then insert it. Its the easiest and suitable for all RDBMS.
Upsert is what you want. UPSERT syntax was added to SQLite with version 3.24.0 (2018-06-04).
CREATE TABLE phonebook2(
name TEXT PRIMARY KEY,
phonenumber TEXT,
validDate DATE
);
INSERT INTO phonebook2(name,phonenumber,validDate)
VALUES('Alice','704-555-1212','2018-05-08')
ON CONFLICT(name) DO UPDATE SET
phonenumber=excluded.phonenumber,
validDate=excluded.validDate
WHERE excluded.validDate>phonebook2.validDate;
Be warned that at this point the actual word "UPSERT" is not part of the upsert syntax.
The correct syntax is
INSERT INTO ... ON CONFLICT(...) DO UPDATE SET...
and if you are doing INSERT INTO SELECT ... your select needs at least WHERE true to solve parser ambiguity about the token ON with the join syntax.
Be warned that INSERT OR REPLACE... will delete the record before inserting a new one if it has to replace, which could be bad if you have foreign key cascades or other delete triggers.
If you have no primary key, You can insert if not exist, then do an update. The table must contain at least one entry before using this.
INSERT INTO Test
(id, name)
SELECT
101 as id,
'Bob' as name
FROM Test
WHERE NOT EXISTS(SELECT * FROM Test WHERE id = 101 and name = 'Bob') LIMIT 1;
Update Test SET id='101' WHERE name='Bob';
I believe you want UPSERT.
"INSERT OR REPLACE" without the additional trickery in that answer will reset any fields you don't specify to NULL or other default value. (This behavior of INSERT OR REPLACE is unlike UPDATE; it's exactly like INSERT, because it actually is INSERT; however if what you wanted is UPDATE-if-exists you probably want the UPDATE semantics and will be unpleasantly surprised by the actual result.)
The trickery from the suggested UPSERT implementation is basically to use INSERT OR REPLACE, but specify all fields, using embedded SELECT clauses to retrieve the current value for fields you don't want to change.
I think it's worth pointing out that there can be some unexpected behaviour here if you don't thoroughly understand how PRIMARY KEY and UNIQUE interact.
As an example, if you want to insert a record only if the NAME field isn't currently taken, and if it is, you want a constraint exception to fire to tell you, then INSERT OR REPLACE will not throw and exception and instead will resolve the UNIQUE constraint itself by replacing the conflicting record (the existing record with the same NAME). Gaspard's demonstrates this really well in his answer above.
If you want a constraint exception to fire, you have to use an INSERT statement, and rely on a separate UPDATE command to update the record once you know the name isn't taken.

Resources