PostgreSQL insert multiple on conflict targets [duplicate] - sql

I have two columns in table col1, col2, they both are unique indexed (col1 is unique and so is col2).
I need at insert into this table, use ON CONFLICT syntax and update other columns, but I can't use both column in conflict_targetclause.
It works:
INSERT INTO table
...
ON CONFLICT ( col1 )
DO UPDATE
SET
-- update needed columns here
But how to do this for several columns, something like this:
...
ON CONFLICT ( col1, col2 )
DO UPDATE
SET
....

ON CONFLICT requires a unique index* to do the conflict detection. So you just need to create a unique index on both columns:
t=# create table t (id integer, a text, b text);
CREATE TABLE
t=# create unique index idx_t_id_a on t (id, a);
CREATE INDEX
t=# insert into t values (1, 'a', 'foo');
INSERT 0 1
t=# insert into t values (1, 'a', 'bar') on conflict (id, a) do update set b = 'bar';
INSERT 0 1
t=# select * from t;
id | a | b
----+---+-----
1 | a | bar
* In addition to unique indexes, you can also use exclusion constraints. These are a bit more general than unique constraints. Suppose your table had columns for id and valid_time (and valid_time is a tsrange), and you wanted to allow duplicate ids, but not for overlapping time periods. A unique constraint won't help you, but with an exclusion constraint you can say "exclude new records if their id equals an old id and also their valid_time overlaps its valid_time."

A sample table and data
CREATE TABLE dupes(col1 int primary key, col2 int, col3 text,
CONSTRAINT col2_unique UNIQUE (col2)
);
INSERT INTO dupes values(1,1,'a'),(2,2,'b');
Reproducing the problem
INSERT INTO dupes values(3,2,'c')
ON CONFLICT (col1) DO UPDATE SET col3 = 'c', col2 = 2
Let's call this Q1. The result is
ERROR: duplicate key value violates unique constraint "col2_unique"
DETAIL: Key (col2)=(2) already exists.
What the documentation says
conflict_target can perform unique index inference. When performing
inference, it consists of one or more index_column_name columns and/or
index_expression expressions, and an optional index_predicate. All
table_name unique indexes that, without regard to order, contain
exactly the conflict_target-specified columns/expressions are inferred
(chosen) as arbiter indexes. If an index_predicate is specified, it
must, as a further requirement for inference, satisfy arbiter indexes.
This gives the impression that the following query should work, but it does not because it would actually require a together unique index on col1 and col2. However such an index would not guarantee that col1 and col2 would be unique individually which is one of the OP's requirements.
INSERT INTO dupes values(3,2,'c')
ON CONFLICT (col1,col2) DO UPDATE SET col3 = 'c', col2 = 2
Let's call this query Q2 (this fails with a syntax error)
Why?
Postgresql behaves this way is because what should happen when a conflict occurs on the second column is not well defined. There are number of possibilities. For example in the above Q1 query, should postgresql update col1 when there is a conflict on col2? But what if that leads to another conflict on col1? how is postgresql expected to handle that?
A solution
A solution is to combine ON CONFLICT with old fashioned UPSERT.
CREATE OR REPLACE FUNCTION merge_db(key1 INT, key2 INT, data TEXT) RETURNS VOID AS
$$
BEGIN
LOOP
-- first try to update the key
UPDATE dupes SET col3 = data WHERE col1 = key1 and col2 = key2;
IF found THEN
RETURN;
END IF;
-- not there, so try to insert the key
-- if someone else inserts the same key concurrently, or key2
-- already exists in col2,
-- we could get a unique-key failure
BEGIN
INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col1) DO UPDATE SET col3 = data;
RETURN;
EXCEPTION WHEN unique_violation THEN
BEGIN
INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col2) DO UPDATE SET col3 = data;
RETURN;
EXCEPTION WHEN unique_violation THEN
-- Do nothing, and loop to try the UPDATE again.
END;
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
You would need to modify the logic of this stored function so that it updates the columns exactly the way you want it to. Invoke it like
SELECT merge_db(3,2,'c');
SELECT merge_db(1,2,'d');

In nowadays is (seems) impossible. Neither the last version of the ON CONFLICT syntax permits to repeat the clause, nor with CTE is possible: not is possible to breack the INSERT from ON CONFLICT to add more conflict-targets.

If you are using postgres 9.5, you can use the EXCLUDED space.
Example taken from What's new in PostgreSQL 9.5:
INSERT INTO user_logins (username, logins)
VALUES ('Naomi',1),('James',1)
ON CONFLICT (username)
DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins;

Vlad got the right idea.
First you have to create a table unique constraint on the columns col1, col2 Then once you do that you can do the following:
INSERT INTO dupes values(3,2,'c')
ON CONFLICT ON CONSTRAINT dupes_pkey
DO UPDATE SET col3 = 'c', col2 = 2

ON CONFLICT ( col1, col2 )
DO UPDATE
SET
works fine. but you should not update col1, col2 in the SET section.

Create a constraint (foreign index, for example).
OR/AND
Look at existing constraints (\d in psq).
Use ON CONSTRAINT(constraint_name) in the INSERT clause.

You can typically (I would think) generate a statement with only one on conflict that specifies the one and only constraint that is of relevance, for the thing you are inserting.
Because typically, only one constraint is the "relevant" one, at a time. (If many, then I'm wondering if something is weird / oddly-designed, hmm.)
Example:
(License: Not CC0, only CC-By)
// there're these unique constraints:
// unique (site_id, people_id, page_id)
// unique (site_id, people_id, pages_in_whole_site)
// unique (site_id, people_id, pages_in_category_id)
// and only *one* of page-id, category-id, whole-site-true/false
// can be specified. So only one constraint is "active", at a time.
val thingColumnName = thingColumnName(notfificationPreference)
val insertStatement = s"""
insert into page_notf_prefs (
site_id,
people_id,
notf_level,
page_id,
pages_in_whole_site,
pages_in_category_id)
values (?, ?, ?, ?, ?, ?)
-- There can be only one on-conflict clause.
on conflict (site_id, people_id, $thingColumnName) <—— look
do update set
notf_level = excluded.notf_level
"""
val values = List(
siteId.asAnyRef,
notfPref.peopleId.asAnyRef,
notfPref.notfLevel.toInt.asAnyRef,
// Only one of these is non-null:
notfPref.pageId.orNullVarchar,
if (notfPref.wholeSite) true.asAnyRef else NullBoolean,
notfPref.pagesInCategoryId.orNullInt)
runUpdateSingleRow(insertStatement, values)
And:
private def thingColumnName(notfPref: PageNotfPref): String =
if (notfPref.pageId.isDefined)
"page_id"
else if (notfPref.pagesInCategoryId.isDefined)
"pages_in_category_id"
else if (notfPref.wholeSite)
"pages_in_whole_site"
else
die("TyE2ABK057")
The on conflict clause is dynamically generated, depending on what I'm trying to do. If I'm inserting a notification preference, for a page — then there can be a unique conflict, on the site_id, people_id, page_id constraint. And if I'm configuring notification prefs, for a category — then instead I know that the constraint that can get violated, is site_id, people_id, category_id.
So I can, and fairly likely you too, in your case?, generate the correct on conflict (... columns ), because I know what I want to do, and then I know which single one of the many unique constraints, is the one that can get violated.

Kind of hacky but I solved this by concatenating the two values from col1 and col2 into a new column, col3 (kind of like an index of the two) and compared against that. This only works if you need it to match BOTH col1 and col2.
INSERT INTO table
...
ON CONFLICT ( col3 )
DO UPDATE
SET
-- update needed columns here
Where col3 = the concatenation of the values from col1 and col2.

I get I am late to the party but for the people looking for answers I found this:
here
INSERT INTO tbl_Employee
VALUES (6,'Noor')
ON CONFLICT (EmpID,EmpName)
DO NOTHING;

ON CONFLICT is very clumsy solution, run
UPDATE dupes SET key1=$1, key2=$2 where key3=$3
if rowcount > 0
INSERT dupes (key1, key2, key3) values ($1,$2,$3);
works on Oracle, Postgres and all other database

Related

postgresql insert into from select

I have two tables table1 and test_table1 which have the same schema.
Both tables have rows/data and pk id's starting from 1.
I would like to do:
insert into test_table1 select * from table1;
but this fails due to the pk values from table1 existing in test_table1.
Way around it would be to specify columns and leave the pk column out, but for some reason thats not working either:
e.g.
NOTE - no pk columns in query below
insert into test_table1 (col1, col2,..., coln) select col1,col2,...,coln from table1;
returns
ERROR: duplicate key value violates unique constraint "test_table1_pkey"
DETAIL: Key (id)=(1) already exists.
I know this works in MySql, is this just due to Postgresql? Anyway around it?
EDIT:
Both tables have primary keys and sequence set.
Since it wasn't clear - tables don't have the same data.
I would just like to add rows from table1 to test_table1.
For answers telling me to exclude primary key from the query - I did as I said before.
Just remove pk column from columns of query
insert into test_table1 (col2,..., coln) select col2,...,coln from table1;
If it still fails maybe you have not sequence on pk columns.
Create sequence on already existing pk column
create sequence test_table1_seq;
ALTER TABLE test_table1
ALTER COLUMN col1 SET DEFAULT nextval('test_table1_seq'::regclass);
And update sequence value to current
SELECT setval('test_table1_seq', (SELECT MAX(col1) FROM test_table1));
This post helped me solve my problem, not sure what went wrong:
How to fix PostgreSQL error "duplicate key violates unique constraint"
If you get this message when trying to insert data into a PostgreSQL database:
ERROR: duplicate key violates unique constraint
That likely means that the primary key sequence in the table you're working with has somehow become out of sync, likely because of a mass import process (or something along those lines). Call it a "bug by design", but it seems that you have to manually reset the a primary key index after restoring from a dump file. At any rate, to see if your values are out of sync, run these two commands:
SELECT MAX(the_primary_key) FROM the_table;
SELECT nextval('the_primary_key_sequence');
If the first value is higher than the second value, your sequence is out of sync. Back up your PG database (just in case), then run thisL
SELECT setval('the_primary_key_sequence', (SELECT MAX(the_primary_key) FROM the_table)+1);
That will set the sequence to the next available value that's higher than any existing primary key in the sequence.
You rather would want to do a UPDATE JOIN like
UPDATE test_table1 AS v
SET col1 = s.col1,
col2 = s.col2,
col3 = s.col3,
.....
colN = s.colN
FROM table1 AS s
WHERE v.id = s.id;
what you want to do is an upsert.
with upsert as (
update test_table1 tt
set col1 = t.col1,
col2 = t.col2,
col3 = t.col3
from table1 t
where t.id = tt.id
returning *
)
insert into test_table1(id, col1, col2, col3)
select id, col1,col2,col3
from table1
where not exists (select * from upsert)

Oracle sql query with complex constraint

Table named T1 with following values
Col1 Col2 Col3
Rs1 S S2
Rs2 SX S3
Rs3 S S2
From a csv, I need to insert some values into the table, having values Rs4, SX and S3 respectively to each column.
I need to apply a check with following constraints.
One S3 can belong to only one SX, but S3 and SX as pair can belong can belong to multiple columns1's values.
What will be the oracle query for this? And if the above condition is true then I need to run an insertion query which is prepared. How can it validated?
PS: we can't create another table.
Had to do a little discovery after I was informed that I totally missed the ORACLE tag. Knowing what you do not know is very important to me. This post should be sufficiently different.
THE BASIC PROBLEM WITH ORACLE'S CHECK
A check constraint can NOT be defined on a SQL View. The check constraint defined on a table must refer to only columns in that
table. It can not refer to columns in other tables.
A check constraint can NOT include a SQL Subquery.
A check constraint can be defined in either a SQL CREATE TABLE statement or a SQL ALTER TABLE statement.
REVISITING THE PROBLEM
We know that (Col2,Col3)| #(Col2,COl3) >= 1.
We know that {Col1}∩(Col2,Col3)
However, the #Cardinality of Col1? Can it be more than 1?
Clearly, the business requirements are not fully explained.
REVISITING THE SOLUTIONS
Adding Objects to the database.
While adding additional tables has been voted down, is it possible to add an ID column? Assuming Col1 is NOT unique to the subsets of (Col2,COl3), then you can add a true ID Column that fulfills the need for normalization while providing true indexing power in your query.
Col1 Col2 Col3 Col4
Rs1 S S2 1
Rs2 SX S3 2
Rs3 S S2 1
To be clear, Col4 would still be an ID since the values of Col2, Col3 are determined by Col4. (Col2,Col3) 1:1 Col4.
CHECKS
Multiple CHECK constraints, each with a simple condition enforcing a
single business rule, are preferable to a single CHECK constraint with
a complicated condition enforcing multiple business rules ORACLE - Constraint
A single column can have multiple CHECK constraints that reference the
column in its definition. There is no limit to the number of CHECK
constraints that you can define on a column. ORACLE - Data Integrity
If you can add a column...by the love of monkeys, please do...not only will it make your life much easier, but you can also make QUERYING the table very efficient. However, for the rest of this post, I will assume you cannot add columns:
RESTATING THE PROBLEM IN CONSTRAINTS
Col2 may not appear with a different Col3. Vice Versa.
(Col2,Col3) may have multiple Co1...what is the possible Cardinality of Col1? Can it be repetitive? I read no.
WRITING OUT THE THEORY ON CHECKS
IF Col1 truly is unique in {(col2,col3)}, then the following already works:
ALTER TABLE EXAMPLE3
ADD CONSTRAINT ch_example3_3way UNIQUE (C, D, X) --only works if these valus never repeat.
The other main constraint #(Col2,Col3) > 1 simply cannot work unless you knew what value was being entered so as to enforce a real SARG. Any Col1 = Col1 or Col1 IN Col1 is the same thing as writing 1 = 1.
ON TRIGGERS
As tempting as the idea sounds, a quick glance through ORACLE lane left me warning against the use. Some reasons from ORACLE:
ORACLE - USING TRIGGERS
Do not create recursive triggers.
For example, if you create an AFTER UPDATE statement trigger on the
employees table, and the trigger itself issues an UPDATE statement on
the employees table, the trigger fires recursively until it runs out
of memory.
Use triggers on DATABASE judiciously. They are executed for every user every time the event occurs on which the trigger is created
Other problems include: TOADWORLD - ORACLE WIKI
Not Compiled -STORED PROCs can reuse a cached plan
No SELECT Trigger Support
Complete Trigger Failure
Disabled Triggers
No Version Control
Update OF COLUMN
No Support of SYS Table Triggers
Mutating Triggers
Hidden Behavior
Still, there are advantages of TRIGGERs, and you could still enforce data integrity by using a query where the first result of
SELECT Col2, Col3 FROM T1 WHERE ROWNUM = 1
Is compared to the inserted value *new.*Col2, *new.*Col3, but this would require the trigger to fire EVERY TIME a row was inserted...recompiled and everything,..I STRONGLY URGE AVOIDANCE.
STORED PROCS
Whatever you may think of STORED PROCEDURES, I suggest you consider them again. Everything from Functions, DML, DDL, database management, RECURSIVE LOGIC, sp_executesql, and beyond can be accomplished through a PROC.
Easily managed, provides encapsulation from accidental or malicious disabling or mutilization of coding.
PROCs are compiled once and can be reuse query plan caches, providing improved performances.
Provides superior portability, can be embedded into TRIGGERS, ORM framework, applications and beyond.
Can literally automate almost any function in a database including ETL, Resource management, security, and discovery. Views are commonly run through stored Procs.
THE UNIQUE ADVANTAGE OF ORACLE
Perhaps forgotten, consider that this is ORACLE which allows you to suspend CONSTRAINTS by inserting in the CONSTRAINT DEFFERABLE. From an ETL specialist perspective, this is essentially making a staging table out of your only table...which is pretty sweet in your predicament of having limited DDL rights.
CONCLUDING COMMENTS
There are a few efficient methods to delete duplicates in your data.
DELETE FROM T1
WHERE rowid NOT IN
(SELECT MAX(rowid)
FROM T1
GROUP BY Col1, Col2, Col3);
NOTE: rowid is the physical location of the row, while rownum represents the logical position in the query.
Lastly, my last attempt at rowid. Unfortunately, time is running late, and the free COMPILER from ORACLE is unhelpful. But I think the idea is what is important.
CREATE TABLE Example3 (MUT VARCHAR(50), D VARCHAR(50), X VARCHAR(50) );
INSERT INTO Example3 (MUT, D, X) VALUES('MUT', 'T', 'M' );
INSERT INTO Example3 (MUT, D, X) VALUES('MUT', 'T', 'P' );
INSERT INTO Example3 (MUT, D, X) VALUES('MUT', 'X', 'LP');
INSERT INTO Example3 (MUT, D, X) VALUES('MUT', 'X', 'Z');
INSERT INTO Example3 (MUT, D, X) VALUES('MUT', 'Y', 'POP');
SELECT C.D, B.X, B.rowid
FROM EXAMPLE3 A
LEFT OUTER JOIN (
SELECT DISTINCT X, C.rowid
FROM EXAMPLE3) B ON B.rowid = A.rowid
LEFT OUTER JOIN (
SELECT DISTINCT D, MAX(rowid) AS [rowid]
FROM Example3) C ON C.rowid = B.rowid
Finally, I'm able to resolve the question with a some select queries and few if conditions being applied. I have done this in a stored procedure.
SELECT count(col3)
INTO V_exist_value
FROM T3
WHERE col3's value = Variable_col3
AND col1's value <> Variable_col1
AND col2's value = Variable_col2;
IF (V_exist_value >= 1) THEN
INSERT INTO T3 (col1, col2, col3)
VALUES (Variable_col1, Variable_col2, Variable_col3);
ELSE
SELECT count(col3)
INTO V_exist_value1
FROM T3
WHERE col3's value = Variable_col3;
IF (V_exist_value1 = 0) THEN
INSERT INTO T3 (col1, col2, col3)
VALUES (Variable_col1, Variable_col2, Variable_col3);
ELSE
RAISE Exception_col3_value_exists;
END IF;
END IF;
If you don't want to use a trigger then you must normalize your tables.
Create a second table - say T1_PAIRS - that will store all permitted pairs of (col2, col3).
Create an unique constraint on col2 column in table T1_PAIRS - this constraint allows only for unique values of COL2 - for example no more than one S3 value can be used in all pairs ==> this enforces the rule: "One S3 can belong to only one SX"
Create a primary key on ( col2, col3 ) columns in this table T1_PAIRS.
Create a foreign key constraint on ( col2, col3 ) in T1 table that references the primary key of T1_PAIRS table.
In the end create an unique constraint on (col1, col2, col3) columnt to enforce a rule ==> S3 and SX as pair can belong can belong to multiple columns1's values (but no more than one value of column1)"
An example:
CREATE TABLE T1_PAIRS (
Col2 varchar2(10), Col3 varchar2(10),
CONSTRAINT T1_PAIRS_PK PRIMARY KEY( col2, col3 ),
CONSTRAINT T1_col2_UQ UNIQUE( col2 )
);
INSERT ALL
INTO T1_PAIRS( col2, col3 ) VALUES( 'S', 'S2' )
INTO T1_PAIRS( col2, col3 ) VALUES( 'SX', 'S3' )
SELECT 1 FROM dual;
ALTER TABLE T1
ADD CONSTRAINT col2_col3_pair_fk
FOREIGN KEY ( col2, col3 ) REFERENCES T1_pairs( col2, col3 );
ALTER TABLE T1
ADD CONSTRAINT pair_can_belong_to_multi_col1 UNIQUE( col1, col2, col3 );

What happens with duplicates when inserting multiple rows?

I am running a python script that inserts a large amount of data into a Postgres database, I use a single query to perform multiple row inserts:
INSERT INTO table (col1,col2) VALUES ('v1','v2'),('v3','v4') ... etc
I was wondering what would happen if it hits a duplicate key for the insert. Will it stop the entire query and throw an exception? Or will it merely ignore the insert of that specific row and move on?
The INSERT will just insert all rows and nothing special will happen, unless you have some kind of constraint disallowing duplicate / overlapping values (PRIMARY KEY, UNIQUE, CHECK or EXCLUDE constraint) - which you did not mention in your question. But that's what you are probably worried about.
Assuming a UNIQUE or PK constraint on (col1,col2), you are dealing with a textbook UPSERT situation. Many related questions and answers to find here.
Generally, if any constraint is violated, an exception is raised which (unless trapped in subtransaction like it's possible in a procedural server-side language like plpgsql) will roll back not only the statement, but the whole transaction.
Without concurrent writes
I.e.: No other transactions will try to write to the same table at the same time.
Exclude rows that are already in the table with WHERE NOT EXISTS ... or any other applicable technique:
Select rows which are not present in other table
And don't forget to remove duplicates within the inserted set as well, which would not be excluded by the semi-anti-join WHERE NOT EXISTS ...
One technique to deal with both at once would be EXCEPT:
INSERT INTO tbl (col1, col2)
VALUES
(text 'v1', text 'v2') -- explicit type cast may be needed in 1st row
, ('v3', 'v4')
, ('v3', 'v4') -- beware of dupes in source
EXCEPT SELECT col1, col2 FROM tbl;
EXCEPT without the key word ALL folds duplicate rows in the source. If you know there are no dupes, or you don't want to fold duplicates silently, use EXCEPT ALL (or one of the other techniques). See:
Using EXCEPT clause in PostgreSQL
Generally, if the target table is big, WHERE NOT EXISTS in combination with DISTINCT on the source will probably be faster:
INSERT INTO tbl (col1, col2)
SELECT *
FROM (
SELECT DISTINCT *
FROM (
VALUES
(text 'v1', text'v2')
, ('v3', 'v4')
, ('v3', 'v4') -- dupes in source
) t(c1, c2)
) t
WHERE NOT EXISTS (
SELECT FROM tbl
WHERE col1 = t.c1 AND col2 = t.c2
);
If there can be many dupes, it pays to fold them in the source first. Else use one subquery less.
Related:
Select rows which are not present in other table
With concurrent writes
Use the Postgres UPSERT implementation INSERT ... ON CONFLICT ... in Postgres 9.5 or later:
INSERT INTO tbl (col1,col2)
SELECT DISTINCT * -- still can't insert the same row more than once
FROM (
VALUES
(text 'v1', text 'v2')
, ('v3','v4')
, ('v3','v4') -- you still need to fold dupes in source!
) t(c1, c2)
ON CONFLICT DO NOTHING; -- ignores rows with *any* conflict!
Further reading:
How to use RETURNING with ON CONFLICT in PostgreSQL?
How do I insert a row which contains a foreign key?
Documentation:
The manual
The commit page
The Postgres Wiki page
Craig's reference answer for UPSERT problems:
How to UPSERT (MERGE, INSERT ... ON DUPLICATE UPDATE) in PostgreSQL?
Will it stop the entire query and throw an exception? Yes.
To avoid that, you can look on the following SO question here, which describes how to avoid Postgres from throwing an error for multiple inserts when some of the inserted keys already exist on the DB.
You should basically do this:
INSERT INTO DBtable
(id, field1)
SELECT 1, 'value'
WHERE
NOT EXISTS (
SELECT id FROM DBtable WHERE id = 1
);

Referencing another column in DEFAULT definition in SQL Server 2005

I want to define a column in my table with following requirements:
The column should be insertable. If the value is provided in the INSERT statement, then it should be inserted.
If the column is not referenced in the INSERT statement, then it should be set to the sum of two other columns.
Because of the first requirement, I cannot user computed columns, since they are not insertable. Because of the second, I cannot use DEFAULT, because it doesn't allow referencing other columns in the definition. What other options do I have?
BTW, the column should be NOT NULL.
Here you go, I'm demonstrating this with an example schema since you've not provided your real table/column names.
Table:
CREATE TABLE test
(
id INT NOT NULL PRIMARY KEY IDENTITY, --made up key
col1 INT, --first column to add, wasn't sure if this was nullable or not
col2 INT, --second column to add, wasn't sure if this was nullable or not
col3 INT NOT NULL --this is the column to optionally insert into
)
Here is the trigger definition:
CREATE TRIGGER demo
ON test
INSTEAD OF INSERT
AS
INSERT INTO test (col1,col2,col3)
SELECT inserted.col1,
inserted.col2,
CASE
WHEN inserted.col3 IS NULL THEN COALESCE(inserted.col1, 0) + COALESCE(inserted.col2, 0)
ELSE inserted.col3
END
FROM inserted
Basically it replaces any insert statement done on the table with the one in the trigger, so I check using the inserted temporary table to see if the value that is trying to be inserted into our non-nullable optional column, col3, is NULL. If it is, I replace it with the addition of col1 and col2 (I'm coalescing with zero as you didn't mention if the two source columns are nullable or not).
You can then run insert statements which either include it or not, despite the fact col3 is not nullable:
INSERT INTO test(col1,col2)
SELECT 12, 31
GO
INSERT INTO test(col1, col2, col3)
SELECT 1, 2, 89
GO
Results are:
ID COL1 COL2 COL3
------------------
1 12 31 43
2 1 2 89
If the trigger wasn't there, you could have got an error trying to run that first insert statement, telling you it couldn't insert NULL into col3.
Notice also that the second insert statement that specifies a value has not been replaced by the addition, as requested.
Here's a working SQL Fiddle.

How to create a "unique" constraint on a boolean MySQL column?

I would like to add a BOOLEAN column to a MySQL table which will be named is_default. In this column, only one record can have is_default set to true.
How can I add this constraint to my column with MySQL?
Thanks!
UPDATE
If it is not a constraint that I should add. How are we dealing with this type of problem on DBs?
I think this is not the best way to model the situation of a single default value.
Instead, I would leave the IsDefault column out and create a separate table with one row and only the column(s) that make(s) up the primary key of your main table. In this table you place the PK value(s) that identify the default record.
This results in considerably less storage and avoids the update issue of temporarily not having a default value (or, alternatively, temporarily having two default values) when you update.
You have numerous options for ensuring that there is one-and-only-one row in the default table.
You can't have such a constraint in MySQL.
However if instead of TRUE and FALSE you use the values TRUE and NULL then it will work because a UNIQUE column can have multiple NULL values. Note that this doesn't apply to all databases, but it will work in MySQL.
CREATE TABLE table1(b BOOLEAN UNIQUE);
INSERT INTO table1 (b) VALUES (TRUE); // Succeeds
INSERT INTO table1 (b) VALUES (TRUE); // Fails: duplicate entry '1' for key 'b'
INSERT INTO table1 (b) VALUES (FALSE); // Succeeds
INSERT INTO table1 (b) VALUES (FALSE); // Fails: duplicate entry '0' for key 'b'
INSERT INTO table1 (b) VALUES (NULL); // Succeeds
INSERT INTO table1 (b) VALUES (NULL); // Succeeds!
How are we dealing with this type of problem on DBs?
In some DBMS you can create a partial index.
In PostgreSQL this would look like this:
CREATE UNIQUE INDEX only_one_true
ON the_table (is_default)
WHERE is_default
SQL Server 2008 has a very similar syntax.
On Oracle it's a bit more complicated but doable as well:
CREATE UNIQUE INDEX only_one_true
ON the_table (CASE
WHEN is_default = 1 THEN 1
ELSE null
END)
The Oracle solution might work on any DBMS that supports expression for an index definition.
Check out triggers. They were introduced in version 5.0.2, I believe. You want a "before insert" trigger. If there is already a row with is_default=true, raise an error. I don't know what problems you might with concurrency and so on, but hopefully this is enough to you started.
I don't think it is a problem with the database as much as it is a problem with your model. It is hard for me to come up with a good example of how to solve it since you haven't mentioned what type of data you are representing, but a XXXType or XXXConfiguration table would be able to hold a defaultXXXId column.
Think of it like this: Should the color blue know that it is default or should something else know that the color blue is default when used in a given context?
Changing the way you model your data is often a much better approach to cross-database compatibility than trying to use specific features of one database flavor to represent data in a way that is not necessarily natural to your problem domain if you think about it.
Check constraints are not supported in MySQL, this is the solution using trigger:
create table if not exists myTable (
id int not null auto_increment primary key,
is_default bit not null
) engine=innodb;
select 'create trigger tbi_myTable';
drop trigger if exists tbi_myTable;
delimiter //
create trigger tbi_myTable
before insert on myTable
for each row
begin
if (select count(1) from myTable where is_default=true) > 0 && NEW.is_default then
-- Signal is only in 5.6 and above use another way to raise an error: if less than 5.6
SIGNAL SQLSTATE '50000' SET MESSAGE_TEXT = 'Cannot insert into myTable only one row with is_default true is allowed!';
end if;
END //
delimiter ;
insert into myTable (is_default) values (false);
insert into myTable (is_default) values (true);
insert into myTable (is_default) values (false);
insert into myTable (is_default) values (false);
-- This will generate an error
insert into myTable (is_default) values (true);
insert into myTable (is_default) values (false);
select * from myTable;
-- will give
/*
id is_default
1 false
2 true
3 false
4 false
*/