动态 UPDATE 语句更新前一个 SELECT 返回的列中的值

Dynamic UPDATE statement to update values in columns returned by a previous 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_;

很明显,这是行不通的,甚至不存在,但我希望你能理解我想要实现的目标。

我可以看到一种方法,您可以从 SELECT 结果集中为每个匹配的 table 生成一个 UPDATE 语句,但我真的不知道如何实现这一点。

为了让事情变得更有趣,我需要对 old_value 到 new_value 转换列表执行此操作。

欢迎提出任何想法。

我正在尝试在 HSQLDB 和 Oracle 上完成这项工作作为我的 2 个要求,但支持其他平台将是一个很好的奖励。

任何时候你认为你需要使用动态SQL,你应该停下来,退后一步,看看是否有其他方法,或者你是否真的需要做你正在做的事情。

我可能会严重质疑你的基础 "requirement":

  • 更新所有 table 匹配某个字符串且类型为整数(或其变体)的所有列。

有些东西仍然有气味 "funny" ...我会非常小心您正在做的事情 - 确保您知道结果是什么,测试测试测试..然后再次测试。 .. 在某处的 DEV 盒上 ...

也就是说,任何时候我需要求助于动态 SQL,我发现最简单的方法是从 "template":

开始

所以在你的情况下,你想要触发的最终更新是你所说的:

  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'[""]'" ... rtrimCHR(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
        )

我所做的只是创建一个易于识别的 "strings",稍后我将使用它来替换值。

注意 如果您的列是字符串,您可能需要在其中加引号:<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 上是否会 运行,但你比我更了解你的查询 ;) 所以我会在这个例子中相信你的查询 "as is" - 只要它挑选出你想要的数据,并包括 table 名称、列名和你想要的 before/after 值(它当前没有)我们没事。

所以我们需要做的就是将这两者结合在一起......我们会这样做:

  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

我离开了???那里有旧/新值,因为你没有指出它们来自哪里?? 但是如果你 运行 那,它 应该 吐出一些更新语句.. ;)

一旦您对这些感到满意table,推动它们立即执行就很容易了。

再次,我建议对这种方法保持谨慎,这对于 1 off 迁移等来说是可以的,但是,不建议日常工作定期 运行ning基础。 ;)

find all tables and their columns that match a specific query, update values in these columns.

使用 HSQLDB,仅 SQL 无法做到这一点。您需要编写一个简短的 Java 程序来列出所需的 table 名称及其列名,然后根据 table 构造一个 UPDATE 语句并执行它。

使用 Oracle,您可以在 PL/SQL 中编写相同的程序。但是 Java 语言解决方案与两个数据库引擎兼容。