Динамический оператор UPDATE для обновления значений в столбцах, возвращаемых предыдущим SELECT

По сути, я хочу сделать следующее:

  • найти все таблицы и их столбцы, которые соответствуют конкретному запросу,
  • обновить значения в этих столбцах.

Так скажи у меня что то типа

SELECT COLUMN_NAME, TABLE_NAME, TABLE_SCHEMA
  FROM INFORMATION_SCHEMA.COLUMNS
  WHERE
    (
      TABLE_SCHEMA = 'PUBLIC'
    ) AND (
      COLUMN_NAME LIKE '%SOMETHING%'
      OR COLUMN_NAME LIKE '%SOMETHINGELSE%'
    ) AND (
      DATA_TYPE = 'BIGINT' OR
      DATA_TYPE = 'TINYINT' OR
      DATA_TYPE = 'SMALLINT' OR
      DATA_TYPE = 'INTEGER'
    )

Или для Oracle что-то вроде:

SELECT COLUMN_NAME, TABLE_NAME
  FROM USER_TAB_COLS
  WHERE
    (
      COLUMN_NAME LIKE '%SOMETHING%'
      OR COLUMN_NAME LIKE '%SOMETHINGELSE%'
    ) AND
    DATA_TYPE IN ('NUMBER')

Я хочу тогда сделать UPDATE на всех результирующих столбцах, аналогичных:

UPDATE _RESULTING_COLUMN_NAMES_HERE_THEORETICALLY_
  SET
    _SINGLE_COLUMN_NAME_ = _SOME_NEW_VALUE_
  WHERE _SINGLE_COLUMN_NAME_ = _SOME_OLD_VALUE_;

Ну, очевидно, что это не работает или даже не существует, но я надеюсь, что вы понимаете, чего я хочу достичь.

Я мог видеть способ, где вы генерируете UPDATE заявление для каждой таблицы соответствия из SELECT набор результатов, но я не вижу, как этого добиться.

Чтобы сделать вещи более увлекательными, мне нужно сделать это для списка преобразований old_value в new_value.

Любые идеи приветствуются.

Я пытаюсь сделать эту работу на HSQLDB и Oracle как свои 2 требования, но поддержка дополнительных платформ была бы довольно хорошим бонусом.

2 ответа

Решение

Каждый раз, когда вы думаете, что вам нужно использовать динамический SQL, вы должны остановиться, сделать шаг назад и посмотреть, есть ли другой способ сделать это, или вам действительно нужно делать то, что вы делаете.

Я бы, наверное, серьезно усомнился в вашем базовом "требовании":

  • обновление всех столбцов для всех таблиц, соответствующих некоторой строке, и типа integer (или их разновидности).

Что-то все еще пахнет "забавно"... Я бы ОЧЕНЬ осторожно относился к тому, что вы делаете - убедитесь, что вы знаете, какими будут результаты, тестовый тестовый тест... и снова ТЕСТ... на где-нибудь устройстве DEV...

Тем не менее, всякий раз, когда мне нужно прибегнуть к динамическому SQL, я обнаружил, что самый простой способ - начать с "шаблона":

Итак, в вашем случае последнее обновление, которое вы хотите запустить, выглядит так:

  UPDATE _RESULTING_COLUMN_NAMES_HERE_THEORETICALLY_
    SET
      _SINGLE_COLUMN_NAME_ = _SOME_NEW_VALUE_
    WHERE _SINGLE_COLUMN_NAME_ = _SOME_OLD_VALUE_;

Хорошо, я бы сейчас переписал это как строку и начал бы запрос, используя предложение WITH:

  WITH w_template AS ( select 
        rtrim(q'[ UPDATE _RESULTING_COLUMN_NAMES_HERE_THEORETICALLY_    ]')||CHR(10)||
        rtrim(q'[   SET                                                 ]')||CHR(10)||
        rtrim(q'[     _SINGLE_COLUMN_NAME_ = _SOME_NEW_VALUE_           ]')||CHR(10)||
        rtrim(q'[   WHERE _SINGLE_COLUMN_NAME_ = _SOME_OLD_VALUE_;      ]')
           template from dual
        )

Обратите внимание, что я ничего не изменил в вашем запросе (пока). Все, что я сделал, это завернул немного "q'[" а также "]'" вокруг этого... rtrim, CHR(10) и положить его в WITH пункт.

1) q'[ some string ]' альтернативный способ сделать строку. Преимущество в том, что внутри строки можно заключать одинарные кавычки без каких-либо проблем: q'[ some 'string' ]' работает просто отлично... печатает " some 'string' "

2) RTRIM - Я оставил пробелы в конце строки как косметические, чтобы нам было легче читать. Однако, из-за ограничений длины строк, эти пробелы могут увеличивать эту строку очень быстро, очень быстро с большим запросом. Так RTRIM это привычка, в которую я попал. Оставьте косметические места, но скажите Oracle, чтобы они не использовались;) Они только для нас.

3) CHR(10) - только косметика - вы можете оставить это, если хотите. Мне нравится, что если вы хотите сбросить запрос во время тестирования, вы можете легко прочитать запрос и посмотреть, что он построил.

Далее мы изменим имена ваших динамических значений там, чтобы мы могли легче найти их и заменить их:

  WITH w_template AS ( select 
        rtrim(q'[ UPDATE <table_name>                   ]')||CHR(10)||
        rtrim(q'[   SET                                 ]')||CHR(10)||
        rtrim(q'[     <col_name> = <col_new_val>        ]')||CHR(10)||
        rtrim(q'[   WHERE <col_name> = <col_old_val>;   ]')
           template from dual
        )

все, что я сделал, это создал легко идентифицируемые "строки", которые я буду использовать для подстановки значений позже.

Обратите внимание, что если ваши столбцы были строками, вам могут понадобиться кавычки: <col_name> = '<col_new_val>'но, кажется, вы имеете дело с целочисленными данными.. так что я думаю, что мы в порядке...

Теперь нам нужно получить ваши данные... поэтому мы вернемся к вашему исходному запросу:

        SELECT COLUMN_NAME, TABLE_NAME
          FROM USER_TAB_COLS
          WHERE
            (
              COLUMN_NAME LIKE '%SOMETHING%'
              OR COLUMN_NAME LIKE '%SOMETHINGELSE%'
            ) AND
            DATA_TYPE IN ('NUMBER')

Хм, мне придется доверять вам ваш запрос там, я не уверен, что он будет работать на Oracle, но вы знаете свой запрос лучше, чем я;) Поэтому я буду доверять вашему запросу "как есть" для этого пример - до тех пор, пока он выбирает нужные вам данные и включает в себя имя таблицы, имя столбца и значения до / после, которые вы хотите (чего в данный момент нет), мы в порядке.

Так что все, что нам нужно сделать, это связать эти два вместе... мы сделаем это:

  WITH w_template AS ( select 
        rtrim(q'[ UPDATE <table_name>                   ]')||CHR(10)||
        rtrim(q'[   SET                                 ]')||CHR(10)||
        rtrim(q'[     <col_name> = <col_new_val>        ]')||CHR(10)||
        rtrim(q'[   WHERE <col_name> = <col_old_val>;   ]')
           template from dual
        )
     w_data AS (
        SELECT COLUMN_NAME, TABLE_NAME
          FROM USER_TAB_COLS
          WHERE
            (
              COLUMN_NAME LIKE '%SOMETHING%'
              OR COLUMN_NAME LIKE '%SOMETHINGELSE%'
            ) AND
            DATA_TYPE IN ('NUMBER')
        )

Затем нам просто нужно добавить окончательный запрос, используя REPLACE для подстановки значений.

(примечание: не уверен, откуда вы получаете "some_new_value" и "some_old_value" от???, вам придется присоединить это к вашему запросу..)

  WITH w_template AS ( select 
        rtrim(q'[ UPDATE <table_name>                   ]')||CHR(10)||
        rtrim(q'[   SET                                 ]')||CHR(10)||
        rtrim(q'[     <col_name> = <col_new_val>        ]')||CHR(10)||
        rtrim(q'[   WHERE <col_name> = <col_old_val>;   ]')
           template from dual
        ),
     w_data AS (
        SELECT COLUMN_NAME, TABLE_NAME
          FROM USER_TAB_COLS
          WHERE
            (
              COLUMN_NAME LIKE '%SOMETHING%'
              OR COLUMN_NAME LIKE '%SOMETHINGELSE%'
            ) AND
            DATA_TYPE IN ('NUMBER')
        )
  SELECT REPLACE ( REPLACE ( REPLACE ( REPLACE ( 
              wt.template, '<table_name>', 
                             wd.table_name ),
                 '<col_name>', wd.column_name ),
                 '<col_new_val>', ??? ),
                 '<col_old_val>', ??? )  query
    FROM w_template wt,
         w_data wd

Я ушел??? там для старых / новых значений, так как вы не указали, откуда они пришли?? но если вы запустите это, он должен выплевывать некоторые операторы обновления..;)

Как только вы освоитесь с ними, выполнить их сразу же - легкая работа.

Опять же, я бы посоветовал быть осторожным с этим подходом, это нормально для миграции 1 раз или около того, однако не рекомендуется, чтобы ежедневная работа выполнялась на регулярной основе.;)

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

С HSQLDB это невозможно сделать только с помощью SQL. Вам нужно написать короткую Java-программу для перечисления необходимых имен таблиц и их имен столбцов, затем создать оператор UPDATE для каждой таблицы и выполнить его.

С Oracle вы могли бы написать ту же программу на PL/SQL. Но решение языка Java совместимо с обоими механизмами баз данных.

Другие вопросы по тегам