如何 select 所有具有列名的表并更新该列

How to select all tables with column name and update that column

我想在我的数据库中找到所有包含列名 Foo 的表,并将其值更新为 0,我在想这样的事情,但我不知道如何在该代码上放置更新,我计划在 MySQL 数据库中的事件上有这个声明,我正在使用 WAMP,这个想法基本上是每天有一个事件 运行 将我所有的 'Foo' 列设置为 0无需我手动操作

SELECT TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name LIKE 'Foo'

不,不是在一个单一的声明中。

获取包含名为 Foo:

的列的所有 table 的名称
SELECT table_schema, table_name
  FROM information_schema.columns 
  WHERE column_name = 'Foo'

然后,每个 table 都需要一个 UPDATE 语句。 (可以在单个语句中更新多个 table,但这需要一个(不必要的)交叉连接。)最好分别执行每个 table。

您可以使用动态 SQL 在 MySQL 存储程序(例如 PROCEDURE)

中执行更新语句
  DECLARE sql VARCHAR(2000);
  SET sql = 'UPDATE db.tbl SET Foo = 0';
  PREPARE stmt FROM sql;
  EXECUTE stmt;
  DEALLOCATE stmt;

如果从 information_schema.tables 为 select 声明游标,则可以使用游标循环为每个 table_name [=216] 处理动态 UPDATE 语句=]ed.

  DECLARE done TINYINT(1) DEFAULT FALSE;
  DECLARE sql  VARCHAR(2000);

  DECLARE csr FOR
  SELECT CONCAT('UPDATE `',c.table_schema,'`.`',c.table_name,'` SET `Foo` = 0') AS sql
    FROM information_schema.columns c
   WHERE c.column_name = 'Foo'
     AND c.table_schema NOT IN ('mysql','information_schema','performance_schema');
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

  OPEN csr;
  do_foo: LOOP
     FETCH csr INTO sql;
     IF done THEN
        LEAVE do_foo;
     END IF;
     PREPARE stmt FROM sql;
     EXECUTE stmt;
     DEALLOCATE PREPARE stmt;
  END LOOP do_foo;
  CLOSE csr;

(这只是示例的粗略轮廓,未经语法检查或测试。)

跟进

关于上面答案中可能被掩盖的一些想法的一些简短说明。

要获取包含列 Foo 的 table 的名称,我们可以 运行 来自 information_schema.columns table 的查询。 (这是 MySQL information_schema 数据库中提供的 table 之一。)

因为我们可能在多个数据库中都有table,所以table_name不足以识别一个table;我们需要知道 table 在哪个数据库中。与其在 运行 和 UPDATE 之前使用“use db”语句,不如直接引用 [=235] =] UPDATE db.mytable SET Foo....

我们可以使用 information_schema.columns 的查询继续并将我们需要为 UPDATE 语句创建的部分串在一起(连接),并让 SELECT return我们需要 运行 更新列 Foo 的实际语句,基本上是这样的:

UPDATE `mydatabase`.`mytable` SET `Foo` = 0 

但是我们想用 table_schematable_name 的值代替 mydatabasemytable。如果我们 运行 这个 SELECT

SELECT 'UPDATE `mydatabase`.`mytable` SET `Foo` = 0' AS sql

return 为我们提供了单行,包含单列(该列恰好被命名为 sql,但该列的名称对我们来说并不重要)。该列的值将只是一个字符串。但是我们得到的字符串恰好是(我们希望)一个 SQL 语句,我们可以 运行.

如果我们将该字符串分解成多个部分,并使用 CONCAT 为我们将它们重新组合在一起,我们会得到同样的结果,例如

SELECT CONCAT('UPDATE `','mydatabase','`.`','mytable','` SET `Foo` = 0') AS sql

我们可以使用该查询作为我们想要 运行 反对 information_schema.columns 的语句的模型。我们将用 information_schema.columns table 中为我们提供数据库和 table_name.

的列的引用替换 'mydatabase''mytable'
SELECT CONCAT('UPDATE `',c.table_schema,'`.`',c.table_name,'` SET `Foo` = 0') AS sql
  FROM information_schema.columns 
 WHERE c.column_name = 'Foo'

有些数据库我们确实不想更新...mysqlinformation_schemaperformance_schema。我们要么需要将包含我们要更新

的 table 的数据库列入白名单
  AND c.table_schema IN ('mydatabase','anotherdatabase')

-- 我们需要将绝对不想更新的数据库列入黑名单

  AND c.table_schema NOT IN ('mysql','information_schema','performance_schema')

我们可以 运行 该查询(如果我们希望行 return 以特定顺序编辑,我们可以添加一个 ORDER BY),我们得到的是包含语句的列表我们想要运行。如果我们将该组字符串保存为纯文本文件(不包括 header 行和额外的格式),在每行末尾添加一个分号,我们就会有一个可以从 mysql> 命令行客户端。

(如果以上任何内容令人困惑,请告诉我。)


接下来的部分有点复杂。其余部分涉及将 SELECT 的输出保存为纯文本文件并从 mysql 命令行客户端执行语句的替代方法。

MySQL 提供了一个 facility/feature 允许我们在 any 字符串作为 SQL 语句执行=199=] 存储程序(例如,存储过程。我们将要使用的功能称为 动态 SQL.

要使用 动态 SQL,我们使用语句 PREPAREEXECUTEDEALLOCATE PREPARE。 (解除分配不是绝对必要的,如果我们不使用它,MySQL 会为我们清理,但我认为无论如何这样做都是很好的做法。)

同样,动态 SQL 在 MySQL 存储程序的上下文中可用。为此,我们需要一个包含我们要执行的 SQL 语句的字符串。作为一个简单的例子,假设我们有这个:

DECLARE str VARCHAR(2000);
SET str = 'UPDATE mytable SET mycol = 0 WHERE mycol < 0';

获取str的内容作为SQL语句求值执行,基本大纲是:

PREPARE stmt FROM str;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

下一个复杂的部分是将它与我​​们 运行 获取我们想要作为 SQL 语句执行的字符串值的查询放在一起。为此,我们组合了一个游标循环。基本大纲是采用我们的 SELECT 语句:

SELECT bah FROM humbug

并将其转换为游标定义:

DECLARE mycursor FOR SELECT bah FROM humbug ;

我们想要的是执行它并循环遍历它 return 的行。要执行语句并准备结果集,我们 "open" 游标

OPEN mycursor; 

完成后,我们将发布 "close",以释放结果集,因此MySQL 服务器知道我们不再需要它,可以清理并释放分配给它的资源。

CLOSE mycursor;

但是,在我们关闭游标之前,我们想要 "loop" 遍历结果集,获取每一行,并对行进行一些操作。我们用来从结果集中获取下一行到过程变量中的语句是:

FETCH mycursor INTO some_variable;

在我们可以将行提取到变量中之前,我们需要定义变量,例如

DECLARE some_variable VARCHAR(2000); 

由于我们的游标(SELECT 语句)return只有一列,所以我们只需要一个变量。如果我们有更多列,则每列都需要一个变量。

最终,我们将从结果集中获取最后一行。当我们尝试获取下一个时,MySQL 将抛出一个错误。

其他编程语言会让我们只做一个 while 循环,然后让我们获取行并在处理完所有行后退出循环。 MySQL 更神秘。做一个循环:

mylabel: LOOP
  -- do something
END LOOP mylabel;

这本身就是一个非常好的无限循环,因为该循环没有 "exit"。幸运的是,MySQL 为我们提供了 LEAVE 语句作为退出循环的方式。我们通常不想在第一次进入循环时就退出循环,所以我们通常会使用一些条件测试来确定我们是否完成并应该退出循环,或者我们没有完成并应该绕过再次循环。

 mylabel: LOOP
     -- do something useful
     IF some_condition THEN 
         LEAVE mylabel;
     END IF;
 END LOOP mylabel;

在我们的例子中,我们想要遍历结果集中的所有行,所以我们将在循环中放置一个 FETCH a 第一个语句(我们想要做的有用的事情).

为了在 MySQL 尝试获取结果集中的最后一行时抛出的错误与我们必须确定是否应该离开的条件测试之间建立联系...

MySQL 为我们提供了一种在抛出错误时定义 CONTINUE HANDLER(我们希望执行的某些语句)的方法...

 DECLARE CONTINUE HANDLER FOR NOT FOUND 

我们要执行的操作是将变量设置为 TRUE。

 SET done = TRUE;

在我们可以运行 SET之前,我们需要定义变量:

 DECLARE done TINYINT(1) DEFAULT FALSE;

有了这个,我们可以改变我们的循环来测试 done 变量是否设置为 TRUE,作为退出条件,所以我们的循环看起来像这样:

 mylabel: LOOP
     FETCH mycursor INTO some_variable;
     IF done THEN 
         LEAVE mylabel;
     END IF;
     -- do something with the row
 END LOOP mylabel;

"do something with the row" 是我们想要获取 some_variable 的内容并用它做一些有用的事情的地方。我们的游标 return 给我们一个字符串,我们希望将其作为 SQL 语句执行。 MySQL 为我们提供了 动态 SQL 功能,我们可以使用它来做到这一点。

注意:MySQL 对过程中语句的顺序有规定。例如 DECLARE 语句必须放在开头。而且我认为 CONTINUE HANDLER 必须是最后声明的东西。


再次:光标动态SQL功能可用在 MySQL 存储程序的上下文中,例如存储过程。我上面举的例子只是一个程序的body的例子。

要将其创建为存储过程,需要将其合并为如下内容的一部分:

DELIMITER $$

DROP PROCEDURE IF EXISTS myproc $$

CREATE PROCEDURE myproc 
NOT DETERMINISTIC
MODIFIES SQL DATA
BEGIN

   -- procedure body goes here

END$$

DELIMITER ;

希望这能更详细地解释我给出的示例。

这应该获取数据库中的所有 table 并在每个 table 后附加更新列 foo 语句复制和 运行 它,复制输出和 运行作为 sql

select concat('update ',table_name,' set foo=0;') from information_schema.tables where table_schema = 'Your database name here' and table_type = 'base table';