H2 MERGE INTO ...使用多个值,如果存在则不要更新

时间:2018-04-21 14:04:12

标签: sql spring-boot h2

我目前正在为spring-boot应用程序编写 data.sql SQL脚本。我希望它填写各种表的默认值(例如,在初始启动时)。

我已在 application.properties 中设置spring.jpa.hibernate.ddl-auto=update以保留数据库的内容,但每次启动服务时都会执行data.sql。

现在我正在寻找一种插入行的方法,如果它们不存在,如果它们确实存在则不更新

我正在使用 H2Dialect (以及H2数据库)。

最初我想使用某种“IF NOT EXISTS”语句和SELECT COUNT(*) FROM table,但看起来H2并不直接支持这种说法。

所以下一个最好的事情要做的就是使用MERGE INTO,但这似乎没有按预期工作,我不明白为什么会这样。

这就是我的脚本的样子,但遗憾的是它没有按预期工作:

MERGE INTO Languages AS T USING (SELECT * FROM Languages) AS S ON (T.ID = S.ID)
    WHEN NOT MATCHED THEN
        INSERT (LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE) VALUES
        ('en', 'English', 'USA', TRUE, TRUE),
        ('de', 'German', 'Deutschland', FALSE, FALSE);

我想添加这些值如果它们不存在(这可能更容易)或者如果语言表中没有值(其中是我更喜欢的。)

这是我启动spring-boot应用程序时收到的(缩短的)异常:

Caused by: org.h2.jdbc.JdbcSQLException: Syntax Fehler in SQL Befehl "MERGE INTO LANGUAGES AS[*] T USING (SELECT * FROM LANGUAGES) AS S ON (T.ID = S.ID) WHEN NOT MATCHED THEN INSERT (LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE) VALUES ('en', 'English', 'USA', TRUE, TRUE), ('de', 'German', 'Deutschland', FALSE, FALSE) "; erwartet "., (, KEY, VALUES, (, WITH, SELECT, FROM"
Syntax error in SQL statement "MERGE INTO LANGUAGES AS[*] T USING (SELECT * FROM LANGUAGES) AS S ON (T.ID = S.ID) WHEN NOT MATCHED THEN INSERT (LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE) VALUES ('en', 'English', 'USA', TRUE, TRUE), ('de', 'German', 'Deutschland', FALSE, FALSE) "; expected "., (, KEY, VALUES, (, WITH, SELECT, FROM"; SQL statement:
MERGE INTO Languages AS T USING (SELECT * FROM Languages) AS S ON (T.ID = S.ID) WHEN NOT MATCHED THEN INSERT (LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE) VALUES ('en', 'English', 'USA', TRUE, TRUE), ('de', 'German', 'Deutschland', FALSE, FALSE) [42001-196]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:345) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.message.DbException.getSyntaxError(DbException.java:205) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.command.Parser.getSyntaxError(Parser.java:541) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.command.Parser.parseSelectSimple(Parser.java:2073) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.command.Parser.parseSelectSub(Parser.java:1940) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.command.Parser.parseSelectUnion(Parser.java:1755) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.command.Parser.parseSelect(Parser.java:1743) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.command.Parser.parseMerge(Parser.java:1053) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.command.Parser.parsePrepared(Parser.java:423) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.command.Parser.parse(Parser.java:321) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.command.Parser.parse(Parser.java:297) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.command.Parser.prepareCommand(Parser.java:258) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.engine.Session.prepareLocal(Session.java:578) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.engine.Session.prepareCommand(Session.java:519) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1204) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:176) ~[h2-1.4.196.jar:1.4.196]
    at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:164) ~[h2-1.4.196.jar:1.4.196]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_121]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_121]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
    at org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:114) ~[tomcat-jdbc-8.5.27.jar:na]
    at com.sun.proxy.$Proxy97.execute(Unknown Source) ~[na:na]
    at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:470) ~[spring-jdbc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
    ... 78 common frames omitted

异常堆栈跟踪是巨大的,但是“由”引起的“部分”基本上说,由于最深的异常,我在上面发布的“由...引起的”声明,无法创建bean。如果有人需要更多细节,请告诉我。

编辑:或者是否有初始化数据的最佳做法(例如默认用户或默认用户)?如果执行数据库重置,最好可以重用一个。

编辑(25.04.2018):我认为我的语言类可能出错了,因为用于架构创建的SQL看起来有点奇怪。这是:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.particles.authservice.jwtservice.JSONHelper;
import com.particles.authservice.languageservice.converters.LanguageDeserializer;

import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * This class describes the entity for a language.
 */
@JsonDeserialize(using = LanguageDeserializer.class)
@Data
@NoArgsConstructor
@Entity(name = "Languages")
public class Language {
    private static Language defaultLanguage;

    @Id
    @Column(nullable = false, unique = true)
    private String  languageCode;
    @Column(nullable = false)
    private String  name;
    @Column(nullable = false)
    private String  i18nName;
    @Column(nullable = false)
    private boolean localizeUi = false;
    @Column(nullable = false)
    private boolean canChoose  = false;

    /**
     * This method sets the class attribute {@link Language#defaultLanguage}.
     * 
     * @param defaultLanguage
     *            ({@link Language}) default language
     */
    public static void setDefaultLanguage(final Language defaultLanguage) {
        Language.defaultLanguage = defaultLanguage;
    }

    /**
     * @return ({@link Language}) default language
     */
    public static Language getDefaultLanguage() {
        return Language.defaultLanguage;
    }
}

2 个答案:

答案 0 :(得分:2)

  

现在我正在寻找一种方法来插入行,如果它们不存在而不更新它们,如果它们存在的话。

您可以使用INSERT INTO ... SELECT ...

INSERT INTO Languages(LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE) 
SELECT LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE
FROM (SELECT 'en' AS Language_code, 'English' AS name,
      'USA' AS I18N_NAME, TRUE AS LOCALIZE_UI, TRUE AS CAN_CHOOSE
      UNION ALL
      SELECT 'de', 'German', 'Deutschland', FALSE, FALSE
      ) sub
WHERE NOT EXISTS (SELECT 1
                  FROM Languages l
                  WHERE sub.Language_code = l.Language_code);

假设LANGUAGE_CODE是UNIQUE,否则您需要明确指定ID列。

或者使用MINUS/EXCEPT

INSERT INTO Languages(LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE) 
SELECT LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE
FROM (SELECT 'en' AS Language_code, 'English' AS name,
      'USA' AS I18N_NAME, TRUE AS LOCALIZE_UI, TRUE AS CAN_CHOOSE
      UNION ALL
      SELECT 'de', 'German', 'Deutschland', FALSE, FALSE
      ) sub
EXCEPT
SELECT LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE
FROM Languages;

修改

  

有没有办法避免使用UNION?

我不确定H2是否支持VALUES子句:

SELECT LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE
FROM (SELECT 'en' AS Language_code, 'English' AS name,
      'USA' AS I18N_NAME, TRUE AS LOCALIZE_UI, TRUE AS CAN_CHOOSE
      UNION ALL
      SELECT 'de', 'German', 'Deutschland', FALSE, FALSE
      ) sub

<=>

SELECT *
FROM (VALUES('en', 'English', 'USA', TRUE, TRUE),
             ('de', 'German', 'Deutschland', FALSE, FALSE)
     ) sub(LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE)

<强> SQLFiddle Demo

答案 1 :(得分:0)

根据您的要求,您应该能够在不使用WHEN NOT MATCHED子句的情况下使用MERGE语句。

  

MERGE

     

更新现有行,并插入不存在的行。如果没有钥匙   指定了列,主键列用于查找行。   如果每个新行有多个行受到影响,则抛出异常。

MERGE INTO Languages 
KEY(LANGUAGE_CODE, NAME, I18N_NAME, LOCALIZE_UI, CAN_CHOOSE) VALUES
('en', 'English', 'USA', TRUE, TRUE),
('de', 'German', 'Deutschland', FALSE, FALSE);

如果这仍然不适合你,请告诉我。