"Insert if not exists" in both MySQL and HSQLDB - mysql

Is there a SQL statement (or atomic sequence of statements) supported by both MySQL and HSQLDB to insert values if they aren't already there?
I'm working on an app that uses MySQL as its production database and HSQLDB for unit tests; I'd like to have a single "initial data import when the tables are empty" script.
MySQL supports INSERT IGNORE, REPLACE INTO and INSERT INTO ... ON DUPLICATE KEY UPDATE ..., but HSQLDB doesn't; conversely, HSQLDB supports MERGE but MySQL doesn't.

HSQLDb from version 2.3.4 adds support for insert ignore.
http://hsqldb.org/
Version 2.3.4 added the UUID type for columns, SYNONYM for tables and
functions, PERIOD predicates, and auto-updated TIMESTAMP columns on
row updates. Other new features included the ability to cancel
long-running statements from JDBC as well as from admin sessions, and
UTF-16 file support for text table sources, in addition to 8-bit text
files. MySQL compatibility for REPLACE, INSERT IGNORE and ON
DUPLICATE KEY UPDATE statements.
And
http://hsqldb.org/doc/guide/guide.pdf (page 260).
HyperSQL supports and translates INSERT IGNORE, REPLACE and ON
DUPLICATE KEY UPDATE variations of INSERT into predictable and
error-free operations. When INSERT IGNORE is used, if any of the
inserted rows would violate a PRIMARY KEY or UNIQUE constraint, that
row is not inserted. With multi-row inserts, the rest of the rows are
then inserted only if there is no other violation such as long strings
or type mismatch, otherwise the appropriate error is returned. When
REPLACE or ON DUPLICATE KEY UPDATE is used, the rows that need
replacing or updating are updated with the given values. This works
exactly like an UPDATE statement for those rows. Referential
constraints and other integrity checks are enforced and update
triggers are activated. The row count returned is simply the total
number of rows inserted and updated.

If someone still has this problem you can enable syntax support for MySQl by adding the following to your script
SET DATABASE SQL SYNTAX MYS TRUE

Related

HSQLDB: REPLACE INTO

From:
HyperSQL User Guide
HyperSQL Database Engine 2.4.0
Chapter 12. Compatibility With Other DBMS :
HyperSQL supports and translates INSERT IGNORE, REPLACE and ON
DUPLICATE KEY UPDATE variations of INSERT into predictable and
error-free operations.
When INSERT IGNORE is used, if any of the inserted rows would violate
a PRIMARY KEY or UNIQUE constraint, that row is not inserted. The rest
of the rows are then inserted only if there is no other violation such
as long strings or type mismatch, otherwise the appropriate error is
returned.
When REPLACE or ON DUPLICATE KEY UPDATE is used, the rows that need
replacing or updating are updated with the given values. This works
exactly like an UPDATE statement for those rows. Referential
constraints and other integrity checks are enforced and update
triggers are activated. The row count returned is simply the total
number of rows inserted and updated.
However when I try
REPLACE INTO my_table (my_id, my_int) VALUES (1, 2);
I get
unexpected token: REPLACE required: INSERT
Why is that?
I suggest that you need to enable MySQL compatibility mode in order to get MySQL-specific commands like REPLACE to work. From Chapter 7 of the HSQL documentation:
In MySQL syntax compatibility mode, HyperSQL supports INSERT IGNORE, REPLACE and ON DUPLICATE KEY UPDATE variations of the INSERT statement.
The key point here is that it MySQL syntax compatibility mode needs to be turned on. Following the link to Chapter 12 which you posted in your question we find:
Use SET DATABASE SQL SYNTAX MYS TRUE or the equivalent URL property sql.syntax_mys=true to enable support for AUTO_INCREMENT and TEXT data types and several other types. These type definitions are translated into HyperSQL equivalents.
So the documentation is giving us two ways to enable MySQL compatibility mode. One we can execute directly from the HSQL console:
SET DATABASE SQL SYNTAX MYS TRUE
The other, probably the one to use for development purposes, is to add the following to the connection string:
sql.syntax_mys=true
Once you have MySQL compatibility mode enabled, REPLACE should work without error.

Is merge statement available in MySQL

I have to use INSERT and UPDATE in single query. For that SQL having MERGE statement.
Is MERGE statement supported in MySQL. If supported, please provide sample.
MERGE is not supported by MySQL, However, there is other possible way of doing the same:
INSERT...ON DUPLICATE KEY UPDATE
If you specify the ON DUPLICATE KEY UPDATE option in the INSERT
statement and the new row causes a duplicate value in the UNIQUE or
PRIMARY KEY index, MySQL performs an update to the old row based on
the new values.

INSERT...ON DUPLICATE KEY UPDATE in mysql workbench

Original Question
MySQL workbench allows one to define "inserts": rows to be inserted into the database on creation. It does this by adding lines such as
START TRANSACTION;
USE `someDB`;
INSERT INTO `someDB`.`countries` (`name`) VALUES ('South Africa');
COMMIT;
However, if the database, table and entry exists, this throws an error. Creation of tables does not, as workbench uses CREATE IF NOT EXISTS for those. Is there a way to get workbench to insert using INSERT...ON DUPLICATE KEY UPDATE?
Half Solution
Running the script with the force argument:
mysql user=xx password=xx --force < script.sql
Ignores such errors, and is thus a solution in my particular case. However, the actual question of modifying the type of INSERTS still stands (for interest)
See here

SQL standard UPSERT call

I'm looking for a standard SQL "UPSERT" statement. A one call for insert and update if exists.
I'm looking for a working, efficient and cross platform call.
I've seen MERGE, UPSERT, REPLACE, INSERT .. ON DUPLICATE UPDATE but no statement meets the needs.
BTW I use MYSQL and HSQLDB for unitests. I understand that HSQLDB is limited and may not cover what I need, but I couldn't find a standard way even without it.
A statement that only MYSQL and HSQLDB will also be enough for now.
I've been looking around for a while and couldn't get an answer.
My table:
CREATE TABLE MY_TABLE (
MY_KEY varchar(50) NOT NULL ,
MY_VALUE varchar(50) DEFAULT NULL,
TIME_STAMP bigint NOT NULL,
PRIMARY KEY (MY_KEY)
);
Any idea?
The only solution that is supported by both MySQL and HSQLDB is to query the rows you intend to replace, and conditionally either INSERT or UPDATE. This means you have to write more application code to compensate for the differences between RDBMS implementations.
START TRANSACTION.
SELECT ... FOR UPDATE.
If the SELECT finds rows, then UPDATE.
Else, INSERT.
COMMIT.
MySQL doesn't support the ANSI SQL MERGE statement. It supports REPLACE and INSERT...ON DUPLICATE KEY UPDATE. See my answer to "INSERT IGNORE" vs "INSERT ... ON DUPLICATE KEY UPDATE" for more on that.
Re comments: Yes, another approach is to just try the INSERT and see if it succeeds. Otherwise, do an UPDATE. If you attempt the INSERT and it hits a duplicate key, it'll generate an error, which turns into an exception in some client interfaces. The disadvantage of doing this in MySQL is that it generates a new auto-increment ID even if the INSERT fails. So you end up with gaps. I know gaps in auto-increment sequence are not ordinarily something to worry about, but I helped a customer last year who had gaps of 1000-1500 in between successful inserts because of this effect, and the result was that they exhausted the range of an INT in their primary key.
As #baraky says, one could instead attempt the UPDATE first, and if that affects zero rows, then do the INSERT instead. My comment on this strategy is that UPDATEing zero rows is not an exception -- you'll have to check for "number of rows affected" after the UPDATE to know whether it "succeeded" or not.
But querying the number of rows affected returns you to the original problem: you have to use different queries in MySQL versus HSQLDB.
HSQLDB:
CALL DIAGNOSTICS(ROW_COUNT);
MySQL:
SELECT ROW_COUNT();
The syntax for doing an upsert in a single command varies by RDBMS.
MySQLINSERT…ON DUPLICATE KEY UPDATE
HSQLDBMERGE
PostgresINSERT…ON CONFLICT…
See Wikipedia for more.
If you want a cross platform solution, then you'll need to use multiple commands. First check for the existing row, then conditionally insert or update as appropriate.

Is inserting a new database entry faster than checking if the entry exists first?

I was once told that it is faster to just run an insert and let the insert fail than to check if a database entry exists and then inserting if it is missing.
I was also told that that most databases are heavily optimized for reading reading rather than writing, so wouldn't a quick check be faster than a slow insert?
Is this a question of the expected number of collisions? (IE it's faster to insert only if there is a low chance of the entry already existing.) Does it depend on the database type I am running? And for that matter, is it bad practice to have a method that is going to be constantly adding insert errors to my error log?
Thanks.
If the insert is going to fail because of an index violation, it will be at most marginally slower than a check that the record exists. (Both require checking whether the index contains the value.) If the insert is going to succeed, then issuing two queries is significantly slower than issuing one.
You can use INSERT IGNORE so that if the key already exist, the insert command would just be ignored, else the new row will be inserted. This way you need to issue a single query, which checks the duplicate values as well inserts new values too.
still Be careful with INSERT IGNORE as it turns EVERY error into a warning. Read this post for insert ignore
On duplicate key ignore?
I think INSERT IGNORE INTO .... can be used here, either it will insert or ignore it.
If you use the IGNORE keyword, errors that occur while executing the INSERT statement are treated as warnings instead. For example, without IGNORE, a row that duplicates an existing UNIQUE index or PRIMARY KEY value in the table causes a duplicate-key error and the statement is aborted. With IGNORE, the row still is not inserted, but no error is issued.
If you want to delete the old value and insert a new value you can use REPLACE You can use REPLACE instead of INSERT to overwrite old rows.
REPLACE works exactly like INSERT, except that if an old row in the table has the same value as a new row for a PRIMARY KEY or a UNIQUE index, the old row is deleted before the new row is inserted.
Else use the INSERT IGNORE as it will either inserts or ignores.
a row that duplicates an existing UNIQUE index or PRIMARY KEY value in the table causes a duplicate-key error and the statement is aborted. With IGNORE, the row still is not inserted, but no error is issued.
If your intension is to Insert if its a new record OR Update the record if it already exists then how about doing an UPSERT?
Check out - http://vadivel.blogspot.com/2011/09/upsert-insert-and-update-in-sql-server.html
Instead of checking whether the record exists or not we can try to Update it directly. If there is no matching record then ##RowCount would be 0. Based on that we can Insert it as a new record. [In SQL Server 2008 you can use MERGE concept for this]
EDIT: Please note, I know this works for MS SQL Server and I don't know about MySQL or ORACLE