FEATURE REQUEST: use of DEFAULT in INSERTs
(1) By anonymous on 2020-11-23 09:57:58 [link] [source]
Hello sqlite community
I would like to be able to specify the use of DEFAULT values within INSERT statements (on column level):
Here is an example:
CREATE TABLE t1 (
a INTEGER DEFAULT 1 NOT NULL
, b INTEGER DEFAULT 2 NOT NULL
, c INTEGER DEFAULT 3 NOT NULL
);
INSERT INTO t1
(a , b , c ) VALUES
(10 , DEFAULT, DEFAULT)
, (DEFAULT, 20 , DEFAULT)
, (DEFAULT, DEFAULT, 30 )
;
would be working with this feature.
I know that 3 different INSERT statements would work:
INSERT INTO t1 (a) VALUES (10);
INSERT INTO t1 (b) VALUES (20);
INSERT INTO t1 (C) VALUES (30);
or as well this statement:
INSERT INTO t1 DEFAULT VALUES;
I found this thread here, which explains more. (but is wrong in some parts)
Is there a chance to include this feature?
Thanks a lot!
Philipp
(2) By Gunter Hick (gunter_hick) on 2020-11-23 11:57:26 in reply to 1 [source]
Naming a column in the INSERT statement is a promise that you will provide a value and a request that SQL provide default values for the columns not mentioned; or an error message stating that such defaults are missing in the table declaration. You cannot just change your mind.
(5) By anonymous on 2020-11-23 12:27:46 in reply to 2 [link] [source]
I agree with you. I see it less about changing my mind, more about having another option.
Using the word DEFAULT would allow me to specify the column for the insert, but choosing the default value from the table definition.
Mention col and a value -> use it
Mention col and DEFAULT OR not Mentioning col -> use the default from the CREATE TABLE definition
It helps to write bigger INSERTS, instead of several INSERTS.
In the example it seems silly, but that's due to its simplicity.
Thank you very much!
(3.2) By Keith Medcalf (kmedcalf) on 2020-11-23 12:12:14 edited from 3.1 in reply to 1 [link] [source]
Do you mean perhaps to define the table as follows:
CREATE TABLE t1
(
a INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 1,
b INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 2,
c INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 3
);
so that if you try to insert a NULL value you get a default instead?
sqlite> CREATE TABLE t1
...> (
...> a INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 1,
...> b INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 2,
...> c INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 3
...> );
sqlite> insert into t1 values (58,null,null);
sqlite> insert into t1 values (null,36,null);
sqlite> insert into t1 values (null,38,1748);
sqlite> select * from t1;
┌────┬────┬──────┐
│ a │ b │ c │
├────┼────┼──────┤
│ 58 │ 2 │ 3 │
│ 1 │ 36 │ 3 │
│ 1 │ 38 │ 1748 │
└────┴────┴──────┘
sqlite>
(4) By anonymous on 2020-11-23 12:20:14 in reply to 3.2 [link] [source]
Exactly, this allows to write one INSERT statement and change the DEFAULTs with NULLs:
INSERT INTO t1
(a , b , c ) VALUES
(10 , NULL , NULL)
, (NULL , 20 , NULL)
, (NULL , NULL , 30 )
;
I didn't know about the "NOT NULL ON CONFLICT REPLACE" part. Thank you very much!
(7) By cj (sqlitening) on 2023-06-21 14:55:24 in reply to 4 [link] [source]
Thank you! Makes entering default entries easy with or without binding. create table t1(c1 integer primary key,c2 text not null on conflict replace default 'Me'); insert into t1(c2) values(null); insert into t1 values(null,null); insert into t1 values(?,?) insert into t1(c2) values(?) insert into t1 values(null,?)
(8) By Keith Medcalf (kmedcalf) on 2023-06-21 21:20:41 in reply to 4 [link] [source]
Technically there are three parts.
NOT NULL is a constraint.
ON CONFLICT REPLACE is a "conflict resolution method"
DEFAULT value specifies the default value.
The ON CONFLICT REPLACE is applied to resolve the NOT NULL conflict. It says to "REPLACE" the value (with the default) if the NOT NULL constraint is violated.
You could declare the column as:
col datatype utterance on conflict replace not null default 7
for example. Each phrase stands by itself. You could also do something like:
col datatype utterance on conflict rollback not null default 7
In which case if you did not provide a value for col
then the default value of 7
would be stored. If you specified NULL you would have a conflict and the conflict resolution method is rollback
so the insert into ... (col) values (null)
would cause the transaction to be cancelled and rolled back.
(9) By anonymous on 2023-06-21 22:53:08 in reply to 8 [link] [source]
You could declare the column as:
col datatype utterance on conflict replace not null default 7
for example. Each phrase stands by itself.
Nope. As the docs clearly show, a conflict clause can only appear after the keyword(s) specifying what kind of column constraint it is.
(10) By Keith Medcalf (kmedcalf) on 2023-06-22 01:35:13 in reply to 9 [link] [source]
Yes, that is so.
(11) By Holger J (holgerj) on 2023-06-22 19:34:03 in reply to 3.2 [link] [source]
This (pseudo) solution would always replace NULL values by some default value, but in practice it might happen that within in INSERT statement one sometimes wants to really have NULL values and sometimes the default value. Of course this is only possible when there is no NOT NULL constraint.
Therefore, the practice proven solution with DEFAULT as a similar keyword as NULL would be prefereable. Clearer, cleaner and more versatile.
(6) By Richard Hipp (drh) on 2020-11-23 14:20:21 in reply to 1 [link] [source]
I see that all of PostgreSQL, MySQL, SQL Server, and Oracle support this. So SQLite probably should too.
However, due to technical reasons, this is a substantial change to SQLite. It would introduce unnecessary risk to add such an enhancement this close to a scheduled release. Maybe something can be done during the next release cycle.
(12) By anonymous on 2025-01-25 06:23:30 in reply to 6 [link] [source]
Hi, I would like to mention this would indeed be a nice-to-have feature.
(13) By anonymous on 2025-02-06 13:00:38 in reply to 12 [link] [source]
++
(14) By anonymous on 2025-02-12 14:14:11 in reply to 12 [link] [source]
It's more than just a nice-to-have, AFAICT there's no principled way right now to construct new record that is partially filled in and otherwise defaults based on a SELECT
clause. The REPLACE
conflict resolution strategy approach doesn't work for nullable columns or when a different strategy is required. COALESCE(NULL, DEFAULT)
or similar would address these edge cases.
Btw I'd love to be wrong. I would love for this to already be possible.
(16.1) By Aask (AAsk1902) on 2025-02-13 18:20:42 edited from 16.0 in reply to 14 [link] [source]
there's no principled way right now to construct new record that is partially filled in
Why insert into a column that has a default value only for it to inherit that value? (Substituting 100 for DEFAULT in the SQL Statement for column f in my example)
What would be the value of a column that has no default value but whose insert value is specified as DEFAULT?
In order to get records that are partially filled - that is, inherit the default value - I insert only into columns that have non-default values. Something like this:
sqlite> create table tblxy(f int not null default 100,g text not null default 'sqlite',thisrec int);
sqlite> insert into tblxy(thisrec) Values(23),(24);
sqlite> select * from tblxy;
f g thisrec
--- ------ -------
100 sqlite 23
100 sqlite 24
(17) By Richard Hipp (drh) on 2025-02-13 18:31:13 in reply to 16.1 [link] [source]
What would be the value of a column that has no default value
Every column has a default value, usually NULL with the exception of an INTEGER PRIMARY KEY column where the default is the next available integer.
(18.4) By SeverKetor on 2025-02-13 19:51:50 edited from 18.3 in reply to 16.1 [link] [source]
Your example does not work if f
or g
can sensibly be null. Perhaps someone has a date column with a default of CURRENT_TIMESTAMP, which could be null if rows are added for something that happened at an unknown time.
Edit: it took me a bit to figure out the usefulness of this, and it doesn't (currently) seem to work in the default-in-values branch. If you want to conditionally use the default while maintaining the ability to use null, you're out of luck.
CREATE TABLE t (
DefaultableColumn TEXT DEFAULT 'Whatever' -- can be null
);
INSERT INTO t (DefaultableColumn) SELECT IIF(<condition>, 'Some Value', DEFAULT);
Parse error: near "DEFAULT": syntax error
INSERT INTO t (DefaultableColumn) VALUES (IIF(<condition>, 'Some Value', DEFAULT));
Parse error: near "DEFAULT": syntax error
(19.1) By Aask (AAsk1902) on 2025-02-13 20:42:51 edited from 19.0 in reply to 18.4 [link] [source]
INSERT INTO t (DefaultableColumn) SELECT IIF(<condition>, 'Some Value', DEFAULT);
If the INSERT statement specified several columns, the SQL parser will have to be pretty smart to determine the appropriate DEFAULT) value.
Assume that table t has, say, 3 columns; consider:
INSERT INTO t select 'arbitraryValue', IIF(<condition>, 'Some Value', DEFAULT1,IIF(<condition>, 'Other Value', DEFAULT2)); -- column names not enumerated
1 Comes from the 2nd column -- being the 2nd value being selected
2 Comes from the 3rd column -- being the 3rd value being selected
Sure it is possible (probably at the cost of slowing the queries) but:
- There is nothing that keeps the SQL statement (probably buried in application code) in-line with the database schema (specifically the table definition) in the event that the table definition is altered.
- If the column names are enumerated i.e. INSERT INTO t(cx,xy,cz) (and the table had n, say 50, columns), the second and third values in the select statement will correspond to different ordinals depending on how the table is defined.
In short, won't this feature introduce a whole set of likely run time errors?
Will this apply to CTEs?
(20) By SeverKetor on 2025-02-13 20:49:05 in reply to 19.0 [link] [source]
I think both points 1 and 2 are covered by statements automatically recompiling themselves when the schema changes (provided sqlite_prepare_v2/v3 was used).
As for the first and last sentences, I'm with you on both. It might be fine though.
As for CTEs, I don't think there'd be sense in that?
(21) By Kees Nuyt (knu) on 2025-02-13 23:37:44 in reply to 6 [link] [source]
I only seldomly had the need for that feature.
If I really need that, there is a simple work-around:
BEGIN TRANSACTION;
INSERT INTO table1 DEFAULT VALUES;
UPDATE table1 SET (col1,col2) = (val,val2);
COMMIT TRANSACTION;
Where val1 and val2 can be expressions of course.
(15) By SeverKetor on 2025-02-13 17:51:51 in reply to 1 [link] [source]
DRH has been working on this, and it apparently seems to be working now. You'll have to build from the default-in-values branch if you would like to test it yourself.
The first commit on the branch says "There is a lot of extra code space consumed for such an obscure feature" so it could quite easily not make it into trunk. You may want to bring out your best, most convincing, arguments if you actually want this (although I know nothing, so maybe it'll be added no matter what for one reason or another).
(22) By Richard Hipp (drh) on 2025-02-14 10:54:49 in reply to 15 [link] [source]
My changes are on a branch. I'm reluctant to merge them, yet. Concerns:
The current implementation follows Postgres syntax, which means that the DEFAULT keyword can only occur as an isolated keyword in the VALUES clause of an INSERT. In particular, DEFAULT can not be part of a larger expression, such as suggested in post 19 above, for example.
I have only just now discovered that Postgres also allows the DEFAULT keyword on the RHS of the "fieldname=" operation in an UPDATE statement. My implementation does not currently support that.
I still don't see how this extra DEFAULT capability is especially useful. But I am acutely aware of the extra complication it adds to the code, which inclines me to leave it out.
Regarding item 1 above: An alternative implementation, which would actually be somewhat simpler to implement, it to say that DEFAULT can be used anywhere that NULL can appear, and it means the same as NULL, except that if it ends up being used as the value to insert into or update a column, the NULL magically changes into the default value of that column. This would allow DEFAULT to be used in lots of crazy places. But that is not Postgres compatible.
It also seems like people will soon be clamoring for a new "sqlite3_bind_default()" API at the C-language level. When will the madness end?
(23) By Aask (AAsk1902) on 2025-02-14 12:05:34 in reply to 22 [link] [source]
When will the madness end?
For me this is a brilliant workaround and will be using that instead of this sort of construction.
- It is more robust.
- Eliminates the possibility of errors arising from the underlying table being re-factored.
(24) By cj (sqlitening) on 2025-02-15 12:44:46 in reply to 23 [link] [source]
Method 3 inserts a bind 2-dimensional text array. A conversion into C might be useful. slopen ":memory:" slexe "CREATE TABLE t1 (a INTEGER DEFAULT 1 NOT NULL, b INTEGER DEFAULT 2 NOT NULL, c INTEGER DEFAULT 3 NOT NULL)" a="1" 'default value b="2" 'default value c="3" 'default value '// Method 1 always pass 3-values slexebind "INSERT INTO t1(a,b,c) VALUES (?,?,?)", bt("10") + bt(b) + bt(c) slexebind "INSERT INTO t1(a,b,c) VALUES (?,?,?)", bt(a) + bt("20") + bt(c) slexebind "INSERT INTO t1(a,b,c) VALUES (?,?,?)", bt(a) + bt(b) + bt("30") '---------------------------------------------------------------------------- '// Method 2 use defaults slexebind "INSERT INTO t1(a) VALUES (?)", bi(10) slexebind "INSERT INTO t1(b) VALUES (?)", bi(20) slexebind "INSERT INTO t1(c) VALUES (?)", bi(30) '---------------------------------------------------------------------------- '// Method 3 array of bind text records columns = 3 rows = 3 slexe "begin immediate" changes = slGetChangeCount("T") 'optional error checking REDIM s(1 TO columns, 1 TO rows) AS STRING ARRAY ASSIGN s()= "10",b,c, a,"20",c, a,b,"30" slexebind "INSERT INTO t1(a,b,c) VALUES (?,?,?)",bindarray(s()),"V"+FORMAT$(columns) changes = slGetChangeCount("T")-changes IF changes = rows THEN slexe "end" ELSE slexe "rollback"
(25) By Richard Hipp (drh) on 2025-02-15 23:22:45 in reply to 22 [link] [source]
The original branch for this has been closed and will be abandoned. I found a new way to implement the DEFAULT behavior that is cleaner (read: easier to maintain long-term) and faster. And as far as I can see, it sticks closer to Postgres syntax.
On the default-in-values-2 branch there is code that allows the DEFAULT keyword to appear in following places:
- As a complete term in a VALUES clause on an INSERT or REPLACE statement.
- On the right-hand side of a SET term in an UPDATE statement.
Hence you can say things like:
INSERT INTO t1(e,a,x) VALUES(1,2,3),(4,
DEFAULT,5);
UPDATE t2 SET e=1, a=
DEFAULT, x=random() WHERE id=22;
If the DEFAULT keyword appears anywhere else (other than in the column definition of a CREATE TABLE or ALTER TABLE ADD COLUMN statement of course), then it is a syntax error. DEFAULT cannot be part of a larger expression. This is how Postgres behaves, so I'm guessing that is what the SQL standards require. Correct me if you think the previous sentence is wrong.
I feel like the new branch has a much greater chance of landing on trunk someday. Meanwhile, y'all are all encouraged to try to break it, or to find differences in what the branch does and what PostgreSQL does.
(26) By anonymous on 2025-02-16 15:28:26 in reply to 25 [link] [source]
Thanks for the quick brainstorming, much appreciated!
Regarding use-case, I'm trying to model conditional inserts, something akin to:
INSERT INTO t1 SELECT DEFAULT VALUES WHERE <condition>;
or more selectively
INSERT INTO t1 SELECT (DEFAULT AS c1, 42 AS c2) WHERE <condition>;
the condition could for example be an ACL check, e.g. in the same transaction check a user id parameter against a user/permissions table and only then insert. I don't control the conflict resolution strategy, since it's user defined. Today, my program first does an ACL check and then the insert. Doing it in the same transaction would avoid a race.
(27.1) By Aask (AAsk1902) on 2025-02-16 18:55:19 edited from 27.0 in reply to 25 [link] [source]
Compiled successfully on Windows 11
SQLite version 3.50.0 2025-02-15 23:47:25
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> drop table if exists t1;
sqlite> .mode columns
sqlite> .headers on
sqlite> CREATE TABLE t1 (e INTEGER DEFAULT 1 NOT NULL, a INTEGER DEFAULT 2 NOT NULL, x INTEGER DEFAULT 3 NOT NULL);
sqlite> INSERT INTO t1(e,a,x) VALUES(1,2,3),(4,DEFAULT,5);
sqlite> select * from t1;
e a x
- - -
1 2 3
4 2 5
sqlite>CREATE TABLE t2 (e INTEGER DEFAULT 1 NOT NULL, a INTEGER DEFAULT 2 NOT NULL, x INTEGER DEFAULT 3 NOT NULL);
sqlite> insert into t2 select * from t1;
sqlite> UPDATE t2 SET e=1, a=DEFAULT, x=random() WHERE rowid=2;
sqlite> select * from t2;
e a x
- - --------------------
1 2 3
1 2 -5184498451065793091
Why is the random() value negative?
(28) By anonymous on 2025-02-16 19:30:32 in reply to 27.1 [link] [source]
Why is the random() value negative?
See random() which says:
The random() function returns a pseudo-random integer between -9223372036854775808 and +9223372036854775807.