Oracle SQL Merge не будет выполняться по оператору

У меня есть таблица с четырьмя столбцами, вот как это выглядит. Я бы назвал это T_BPR_KPI_MONTHLY_VALUES

 KPI_NAME_SHORT_S | MONTH_N | YEAR_N | VALUE_N
-----------------------------------------------
 MY_KPI_1         |       1 |   2015 |   99.87
 MY_KPI_2         |       1 |   2015 |   97.62
 ...              |       1 |   2015 |     ...
 MY_KPI_1         |       2 |   2015 |     ...
 ...              |     ... |   2015 |     ...

Каждый kpi представляет собой измерение, и каждый из них имеет ежедневные значения, которые сохраняются в другой таблице с именем T_BPR_KPI_DY. Моя цель — рассчитать и сохранить ежемесячные значения каждого KPI.

Возможно, что в определенный день дневные значения для некоторых kpi все еще отсутствуют, и для точного расчета месячных значений я должен иметь возможность заменить существующие значения в базе данных, а также вставить новые записи для будущих месяцев и лет.

Я думал, что операция слияния oracle sql будет хорошим выбором для этой задачи. Идея состоит в том, чтобы проверить, существует ли уже запись, и если да, то обновить ее значение, а если нет, то вставить новую.

Вот так выглядит запрос

MERGE INTO T_BPR_KPI_MONTHLY_VALUE A
USING( SELECT 'MY_KPI_1' AS KPI_NAME_SHORT_S, 1 AS MONTH_N, 2014 AS YEAR_N FROM DUAL ) B 
ON ( A.KPI_NAME_SHORT_S = B.KPI_NAME_SHORT_S ) 
WHEN MATCHED THEN 
UPDATE SET VALUE_N = ( select AVG(MY_KPI_1) from T_BPR_KPI_DY where DAY_D between '01.01.2014' AND '31.01.2014') 
WHEN NOT MATCHED THEN 
INSERT (KPI_NAME_SHORT_S, MONTH_N, YEAR_N, VALUE_N) VALUES ('MY_KPI_1', 1, 2014, ( select AVG(MY_KPI_1) from T_BPR_KPI_DY where DAY_D between '01.01.2014' AND '31.01.2014') )

Я подумал, что вычисление средних месячных значений на лету — неплохая идея, поэтому, как вы можете видеть, у меня есть еще один запрос на выборку, который вычисляет только среднее месячное значение для определенного KPI. Я не уверен, что это лучшее практическое решение, но оно отлично работает, когда я выполняю этот запрос в инструменте разработчика oracle sql. однако, когда я пытаюсь выполнить его из приложения, он не работает.

Так выглядит метод

public static void storeValuesToDb(ArrayList<String> kpiNames) throws SQLException {

    Connection conn = getOracleJDBCConnection_DASH();

    int currentYear = cal.get(Calendar.YEAR);
    int startYear = cal.get(Calendar.YEAR) - 1;
    for (String kpiName : kpiNames) {
        for (int i = startYear; i <= currentYear; i++) {
            for (int j = 0; j < 12; j++) {

                try {
                    String myMergeSQL = ""
                            + "MERGE INTO T_BPR_KPI_MONTHLY_VALUE A "
                            + "USING( SELECT '" + kpiName + "' AS KPI_NAME_SHORT_S, " + (j + 1) + " AS MONTH_N, " + i + " AS YEAR_N FROM DUAL ) B ON ( A.KPI_NAME_SHORT_S = B.KPI_NAME_SHORT_S ) "
                            + "WHEN MATCHED THEN "
                            + "UPDATE SET VALUE_N = ( select AVG(" + kpiName + ") from T_BPR_KPI_DY where DAY_D between '" + getFirstDateOfMonth(j, i) + "' AND '" + getLastDateOfMonth(j, i) + "') "
                            + "WHEN NOT MATCHED THEN "
                            + "INSERT (KPI_NAME_SHORT_S, MONTH_N, YEAR_N, VALUE_N) VALUES ('" + kpiName + "', " + (j + 1) + ", " + i + ", ( select AVG(" + kpiName + ") from T_BPR_KPI_DY where DAY_D between '" + getFirstDateOfMonth(j, i) + "' AND '" + getLastDateOfMonth(j, i) + "') )";

                    System.out.println(myMergeSQL);

                    Statement stmt_dash = conn.createStatement();
                    stmt_dash.executeUpdate(myMergeSQL);
                    conn.commit();
                    stmt_dash.close();
                } catch (SQLException ex) {
                    conn.close();
                }
            }
        }
    }

    conn.close();
}

В терминале выводится только первое слияние sql. Он не завершает операцию и не генерирует исключение. Он каким-то образом блокируется, и в БД тоже ничего не происходит. Возможно, мой запрос на слияние неверен или невозможно выполнить такую ​​​​операцию с объектом оператора. Если кто-то может увидеть, в каких случаях эта проблема, пожалуйста, помогите.

Спасибо заранее


person amsalk    schedule 18.06.2015    source источник
comment
ваш улов (SQLException ex) просто закрывает соединение. Распечатайте сообщение об исключении на консоли, оно будет содержать ошибку.   -  person LonWolf    schedule 18.06.2015
comment
Привет, спасибо за повтор. Я пробовал, как вы предложили, с ex.printStackTrace(), а также с System.err.println(ex.getMessage()), но ничего не произошло. Я также удалил conn.close() из блока catch, но это не помогло. однако, когда я выполняю простую вставку sql с этим кодом, он работает нормально. Это тебе что-то говорит?   -  person amsalk    schedule 18.06.2015
comment
Как долго выполняется select AVG(MY_KPI_1) from T_BPR_KPI_DY where DAY_D between '01.01.2014' AND '31.01.2014'?   -  person Bob Jarvis - Reinstate Monica    schedule 18.06.2015


Ответы (1)


Я бы начал с переформулировки вашего запроса на слияние и решил некоторые проблемы:

  1. USING часть MERGE на самом деле означает ваш «источник необработанных данных». Вы используете выбор из двойного с жестко запрограммированными значениями. Здесь следует выбрать все KPI, а также рассчитать Среднее значение по KPI. Составьте свой запрос, который выбирает все KPI с их соответствующими значениями VALUE_N, и используйте его в части USING.
  2. при совпадении UPDATE SET использует значения из «источника необработанных данных», который является псевдонимом B в вашем коде, а не вычисляет внутри предложения UPDATE.
  3. если не совпадают, тогда ВСТАВЬТЕ ЗНАЧЕНИЯ - снова используйте значения из «источника необработанных данных», который является псевдонимом B в вашем коде, не пытайтесь вычислить VALUE_N внутри вставки - ну, по крайней мере, не таким образом, я думаю, что это ваши запросы Главная проблема.
  4. MERGE INTO xxx A с помощью () B вы дали 2 псевдонима своим таблицам, но по строке внутри WHEN MATCHED or NOT вы не используете псевдоним. Это может вызвать проблемы, если A и B имеют столбцы с одинаковыми именами.

Пример того, как я использую слияние в производстве: слияние с пунктом назначения, используя выбор из таблицы Source (внутри выбора из источника вы также можете добавить другие вычисления, очевидно, в вашем случае среднее значение)

T_REPORT_DAILY_SNAPSHOT_2G должен быть в вашем коде, чтобы выбрать имя KPI, значение и среднее значение или все, что вам нужно для INSERT и UPDATE.

MERGE INTO T_CELLS_2G dest 
 USING (SELECT DISTINCT *
        FROM T_REPORT_DAILY_SNAPSHOT_2G) src
 ON (dest.lac = src.lac and dest.cell_id = src.cell_id)
 WHEN MATCHED THEN
 UPDATE SET 
      dest.cell_name = src.cell_name, 
      dest.loc_code = src.loc_code,
      dest.site_code = src.site_code,
      dest.rac = src.rac
WHEN NOT MATCHED THEN
     INSERT (dest.cell_name, 
      dest.loc_code,
      dest.site_code,
      dest.lac,
      dest.cell_id,
      dest.rac) 
     VALUES (src.cell_name, 
      src.loc_code,
      src.site_code,
      src.lac,
      src.cell_id,
      src.rac);

Надеюсь, это поможет в некотором роде.

person LonWolf    schedule 18.06.2015
comment
Большой! Спасибо, это кажется хорошим решением, но, поскольку я никогда раньше не использовал слияние, я все еще не понимаю его на 100%. Специально на шаге 1. мне просто нужно использовать такой запрос, например. SELECT * FROM T_BPR_KPI_MONTHLY_VALUES потому что он уже содержит VALUE_N для каждого ключевого показателя эффективности за каждый месяц в 2014 и 2015 годах, или мне нужно вычислить для него новое значение? - person amsalk; 18.06.2015
comment
он может быть уже рассчитан в представлении/таблице (более быстрое время выполнения, если оно уже находится в 1 таблице) или вычислено на лету в подзапросе в операторе using(). Удачи!:) - person LonWolf; 19.06.2015