唯一约束 (indexExists) 的 Liquibase 前提条件不适用于 HSQLDB 数据库?

Posted

技术标签:

【中文标题】唯一约束 (indexExists) 的 Liquibase 前提条件不适用于 HSQLDB 数据库?【英文标题】:Liquibase Preconditions of Unique Constraints (indexExists) Doesn't Work for HSQLDB Databases? 【发布时间】:2016-09-08 08:56:49 【问题描述】:

当我使用 Liquibase 升级现有 HSQLDB 数据库(数据库不包含 2 个 Liquibase 控制表 - databasechangelogdatabasechangeloglock)时脚本(显示如下),报错java.sql.SQLSyntaxErrorException: object name already exists: SOME_TABLE_UK in statement [ALTER TABLE PUBLIC.SOME_TABLE ADD CONSTRAINT SOME_TABLE_UK UNIQUE (COLUMN_3, COLUMN_4)]

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
    <property name="TYPE_AS_ID" value="VARCHAR2(36 BYTE)" dbms="oracle" />
    <property name="TYPE_AS_ID" value="VARCHAR(36)" dbms="postgresql" />
    <property name="TYPE_AS_ID" value="VARCHAR(36)" dbms="hsqldb" />

    <property name="TYPE_AS_NORMAL_TEXT" value="VARCHAR2(255 BYTE)" dbms="oracle" />
    <property name="TYPE_AS_NORMAL_TEXT" value="TEXT" dbms="postgresql" />
    <property name="TYPE_AS_NORMAL_TEXT" value="VARCHAR(255)" dbms="hsqldb" />

    <changeSet author="RayChen" id="1">
        <preConditions onFail="MARK_RAN">
            <not>
                <tableExists tableName="SOME_TABLE" />
            </not>
        </preConditions>
        <createTable tableName="SOME_TABLE">
            <column name="COLUMN_1" type="$TYPE_AS_ID">
                <constraints nullable="false" />
            </column>
            <column name="COLUMN_2" type="$TYPE_AS_ID" />
            <column name="COLUMN_3" type="$TYPE_AS_NORMAL_TEXT" />
            <column name="COLUMN_4" type="$TYPE_AS_NORMAL_TEXT" />
        </createTable>
    </changeSet>

    <changeSet author="RayChen" id="2">
        <preConditions onFail="MARK_RAN">
            <not>
                <tableExists tableName="SOME_OTHER_TABLE" />
            </not>
        </preConditions>
        <createTable tableName="SOME_OTHER_TABLE">
            <column name="COLUMN_1" type="$TYPE_AS_ID">
                <constraints nullable="false" />
            </column>
            <column name="COLUMN_2" type="$TYPE_AS_NORMAL_TEXT" />
        </createTable>
    </changeSet>

    <changeSet author="RayChen" id="3">
        <preConditions onFail="MARK_RAN">
            <not>
                <primaryKeyExists tableName="SOME_TABLE" />
            </not>
        </preConditions>
        <addPrimaryKey constraintName="SOME_TABLE_PK" tableName="SOME_TABLE" columnNames="COLUMN_1" />
    </changeSet>

    <changeSet author="RayChen" id="4">
        <preConditions onFail="MARK_RAN">
            <not>
                <primaryKeyExists tableName="SOME_OTHER_TABLE" />
            </not>
        </preConditions>
        <addPrimaryKey constraintName="SOME_OTHER_TABLE_PK" tableName="SOME_OTHER_TABLE" columnNames="COLUMN_1" />
    </changeSet>

    <changeSet author="RayChen" id="5">
        <preConditions onFail="MARK_RAN">
            <not>
                <foreignKeyConstraintExists foreignKeyName="SOME_TABLE_FK" />
            </not>
        </preConditions>
        <addForeignKeyConstraint constraintName="SOME_TABLE_FK" baseTableName="SOME_TABLE" baseColumnNames="COLUMN_2" referencedTableName="SOME_OTHER_TABLE" referencedColumnNames="COLUMN_1" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" />
    </changeSet>

    <changeSet author="RayChen" id="6">
        <preConditions onFail="MARK_RAN">
            <not>
                <indexExists indexName="SOME_TABLE_UK" />
            </not>
        </preConditions>
        <addUniqueConstraint constraintName="SOME_TABLE_UK" tableName="SOME_TABLE" columnNames="COLUMN_3, COLUMN_4" />
    </changeSet>
</databaseChangeLog>

但是我已经在变更集中添加了一个 not indexExists 前置条件来创建 SOME_TABLE_UK 唯一键,如下所示,为什么会出现上述错误?

<changeSet author="RayChen" id="6">
    <preConditions onFail="MARK_RAN">
        <not>
            <indexExists indexName="SOME_TABLE_UK" />
        </not>
    </preConditions>
    <addUniqueConstraint constraintName="SOME_TABLE_UK" tableName="SOME_TABLE" columnNames="COLUMN_3, COLUMN_4" />
</changeSet>

让我更困惑的是,当数据库是 Oracle 或 PostgreSQL 时,Liquibase 脚本运行良好且没有错误(由于 not indexExists 前提条件,上述更改集 #6 将被跳过),类似的代码在主键和外键上运行良好,没有错误。

【问题讨论】:

支持唯一约束的索引在 HSQLDB 中没有命名为 SOME_TABLE_UK - 它获得了一个生成的名称。例如SYS_IDX_SOME_TABLE_UK_10120。您需要一个“sql 前提条件”来检查是否定义了唯一约束,而不是索引 @a_horse_with_no_name 感谢您的回复。我也对此表示怀疑,但是当我在HSQLDB数据库管理器中执行SQL语句ALTER TABLE SOME_TABLE DROP CONSTRAINT SOME_TABLE_UK;时,它成功了;当我执行 SQL 语句 ALTER TABLE SOME_TABLE DROP CONSTRAINT SYS_IDX_SOME_TABLE_UK_15356; 时,它失败了(SYS_IDX_SOME_TABLE_UK_15356 是 HSQLDB 数据库管理器中唯一键 SOME_TABLE_UK 的显示名称)。 @a_horse_with_no_name 如果我想使用 SQL 前置条件来检查是否存在唯一键,你能举个例子来说明如何编写它吗?谢谢。 约束确实被命名为SOME_TABLE_UK,但索引不是 @a_horse_with_no_name 你是对的!我将该语句从&lt;indexExists indexName="SOME_TABLE_UK" /&gt; 更改为&lt;indexExists indexName="SYS_IDX_SOME_TABLE_UK_15356" /&gt;,它在HSQLDB 中有效。谢谢!但是,我刚刚测试过,该名称每次由 HSQLDB 生成时都会有所不同(在我清除数据库并重新运行 Liquibase 脚本后,现在是 SYS_IDX_SOME_TABLE_UK_15691),所以我不能依赖于编码。我们能否有另一种方法来编写唯一键存在前提条件? 【参考方案1】:

这个问题还有另一种方法。您可以使用 sql 系统对象来管理自己的 precontions。 (我不确定这是否是正确的 HSQLDB 语法) .

<changeSet author="RayChen" id="6">
        <preConditions onFail="MARK_RAN">
             <sqlCheck expectedResult="0">SELECT COUNT(*) FROM sys.objects WHERE name LIKE 'SOME_TABLE_UK%'</sqlCheck>
        </preConditions>
        <addUniqueConstraint constraintName="SOME_TABLE_UK" tableName="SOME_TABLE" columnNames="COLUMN_3, COLUMN_4" />
<changeSet/>

【讨论】:

以上是关于唯一约束 (indexExists) 的 Liquibase 前提条件不适用于 HSQLDB 数据库?的主要内容,如果未能解决你的问题,请参考以下文章

mysql唯一约束

SQLServer主键和唯一约束的区别

唯一性约束和唯一性索引的区别

SQLServer 唯一键约束和唯一索引有啥区别

oracle 唯一约束 为啥 唯一索引

MySQL添加唯一约束和联合唯一约束