I need to convert a MySQL INSERT IGNORE INTO ... statement to a changeSet in liquibase and I prefer not to insert the statement directly in the changeSet.
Since I cannot find a direct translation, I'm wondering how to write a changeSet to insert a row only if the primary key part doesn't already exists in another row of the same table?
Liquibase doesn't have direct support for INSERT IGNORE support. If you are not wanting to use the <sql> tag, your only option is to use something like
<changeSet>
<insert ...></insert>
<modifySql>
<replace replace="INSERT" with="INSERT IGNORE"/>
</modifySql>
</changeSet>
With a liquibase 3.2 and oracle12
<changeSet>
<insert ...></insert>
<modifySql>
<replace replace="INSERT" with="insert /*+ ignore_row_on_dupkey_index(my_table, my_table_pk) */"/>
</modifySql>
</changeSet>
remarks :
Not use "replace value" but "replace replace"
It works too with the changeset loadData
Related
I'm using MySQL database. I'm giving call to an stored procedure for 3 times to insert 3 records. following is the example for the same.
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.10.xsd
http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-3.10.xsd">
<changeSet id="Demo" author="rambo" runOnChange="true">
<sql splitStatements="true">
CALL insert_animal(NULL, 'Cat', 'Cat', NULL, 0, '', '');
CALL insert_animal(NULL, 'Mouse', 'Mouse', NULL, 0, '', '');
CALL insert_animal(NULL, 'Dog', 'Dog', NULL, 0, '', '');
</sql>
<rollback/>
</changeSet>
</databaseChangeLog>
Now when I update this above script for addition of 4th animal lets say, then I get error and basically it says that record exist with the name Cat and script execution is halted.
What I want to do is, I want to continue the execution even if the SP call to insert any animal fails because its duplicate.
I'm new to liquibase and seeking for some advise on this.
You trying to use liquibase wrong way. The changeset aimed be written only once and never changed after you apply it to database. If you need to add something new to existing changeset - just create new one. No exception from this rule, especially if you already commit the changes.
Actually, there is only one exception - if you test the changeset on your own database and it is not satisfy the requirements. In this case you should remove corresponding row from databasechangelog table, manually return database schema to previous state, update the changeset and apply it to database again.
In addition, you should NEVER use liquibase to add new data to tables. Liquibase is aimed to change schema state, not database data. If you break this rule, you will get a lot of problems all the way, because the data is a subject for frequently changes. Use plain sql for data migration after you apply liquibase changes.
I am trying out Liquibase in view of trying to automate Database Deployments and get some structure around Database changes.
Here is my Changeset which i wanted to evaluate for auto rollbacks. Now, i am trying to introduce an error in the foreign key constraint and was expecting that the rollback statements run after the error and it deletes the column from the customer table as well.
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd">
<preConditions>
<dbms type="mysql" />
</preConditions>
<changeSet id="002" author="Hrishi" failOnError="true"
runInTransaction="true">
<preConditions onError="HALT" onFail="HALT"
onFailMessage="The table gender doesnt exist. Precondition not met.">
<not>
<columnExists tableName="customer" columnName="gender_id" />
</not>
<and>
<not>
<foreignKeyConstraintExists foreignKeyName="fk_gender_customer"/>
</not>
</and>
</preConditions>
<addColumn tableName="customer" schemaName="sakila">
<column name="gender_id" type="smallint(1)" />
</addColumn>
<addForeignKeyConstraint constraintName="fk_gender_customer"
referencedTableName="gender" baseColumnNames="gender_id"
baseTableName="customer" referencedColumnNames="invoking_an_error" />
<rollback>
<dropColumn tableName="customer" columnName="gender_id" />
<dropForeignKeyConstraint baseTableName="customer"
constraintName="fk_gender_customer" />
</rollback>
</changeSet>
What really though does happen is that there is no entry in the DATABASECHANGELOG table. Which i expected, but then the column in the customer table still exists.
I did look at the help on the addColumn url on liquibase documentation and MySQL is supported.
http://www.liquibase.org/documentation/changes/add_column.html
Is my understanding correct that it should autorollback all the statements in the changeset?
EDIT : I think the problem in this specific case is that the ALTER table is a DDL statement. From the documentation of MySQL cannot-roll-back.
I'm not sure of the command you issued, but the changes listed in the <rollback> section of the changeset do not happen automatically when an error happens. When an error happens, liquibase tries to roll back the database transaction, but if some things have already been committed, you can get into an inconsistent state. The <rollback> tag is there so that you can manually roll back changes by using the liquibase rollback command.
I need to insert new record on this users table with Liquibase. It works well on PostgreSQL, but when it comes to MySQL the SQL output has quotes for the boolean field (u_administrator). Like this:
INSERT INTO users (u_name, ..., u_administrator) VALUES ('Administrator',..., '1')
<changeSet author="jmartins" id="AdminInsertion">
<insert tableName="users" dbms="postgresql">
<column name="u_name" value="Administrator"/>
...
<column name="u_administrator" value="true"/>
</insert>
<insert tableName="users" dbms="mysql">
<column name="u_name" value="Administrator"/>
...
<column name="u_administrator" value="1"/>
</insert>
</changeSet>
So I can I force liquibase to output "u_administrator" without quotes so it can insert correctly on MySQL. Do I need to go for a < sql> custom command?
Thanks anyway.
Use valueBoolean instead of value to tell liquibase it needs to use the database-specific value for a boolean value.
I have a liquibase xml script. When I run it on Postgres I don't face any problem but when I run it for MYSQL it gives error when the structure is of the following type:-
<insert tableName="user_table">
<column name="id" valueComputed="(select max(id)+1 from user_table)"/>
<column name="name" value="someName"/>
</insert>
When the above script is executed for MYSQL it gives error:-
You can't specify target table 'user_table' for update in FROM clause.
I found a solution to this by using alias like this :-
<insert tableName="user_table">
<column name="id" valueComputed="(select max(id)+1 from (Select * from user_table) t)" />
<column name="name" value="someName"/>
</insert>
But there are thousands of entries like this. Is there any generic way of doing it so that I don't have to change the script at so many places. Thanks.
The easiest approach would be to just update the XML, either with an simple XML parser program or even a regexp search and replace in your text editor.
Alternately, you can override the standard liquibase logic to look for that particular valueComputed pattern and replace it. There are a couple points you could make the change at:
Override the liquibase.parser.core.xml.XMLCHangeLogSAXParser class, probably the parseToNode() method to search through the generated ParsedNode for valueComputed nodes
Override the liquibase.change.core.InsertDataChange class generateStatements() method or addColumn() method to replace valueComputed fields.
See http://liquibase.org/extensions for more info on writing extensions.
I have an 11MB dump containing UPDATE statements, formatted like this:
UPDATE `table` SET `id` = 1,`field`='etc' WHERE `table`.`id` = 1;
However, I need to insert these values into a new database, using statements like this:
INSERT INTO `table` (`id`, `field`) VALUES ('etc', 1);
Has anyone written a tool or service to convert UPDATE statements to INSERT statements? (At first glance, it seems to be just beyond the reach of regex)
Lazy approach. If you have the ids, insert no matter what for those id's and after that run the updates.
Nothing is beyond the reach of a regex. Foreach line in the dump, use the following :
Pattern : ^.*`id` = (\d+).*`field`='(.*)'[^']*WHERE.*$
Replacement : INSERT INTO `table` (`id`, `field`) VALUES ('$2', $1);
This simple regex search and replace in notepad++ will convert any update into an insert:
Search for: UPDATE (.*) SET (.*) WHERE .*$
Replace with: INSERT INTO \1 SET \2;
This assumes that what followed the "where" was originally a unique key. It changes it so that key will auto-increment when you insert.