如何选择具有列名称的所有表并更新该列

non*_*non 2 mysql mysql-event

我想找到我的数据库中包含列名 Foo 的所有表,并将其值更新为 0,我在想这样的事情,但我不知道如何将 UPDATE 放在该代码上,我计划有关于 MySQL 数据库内的事件的这条语句,我使用的是 WAMP,这个想法基本上是每天运行一个事件,将我的所有“Foo”列设置为 0,而无需我手动执行

SELECT TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name LIKE 'Foo'
Run Code Online (Sandbox Code Playgroud)

spe*_*593 5

不,不是在一个声明中。

要获取包含名为 的列的所有表的名称Foo

SELECT table_schema, table_name
  FROM information_schema.columns 
  WHERE column_name = 'Foo'
Run Code Online (Sandbox Code Playgroud)

然后,您需要为每个表添加一个 UPDATE 语句。(可以在单个语句中更新多个表,但这需要是(不必要的)交叉联接。)最好单独执行每个表。

您可以使用动态 SQL 在 MySQL 存储程序(例如 PROCEDURE)中执行 UPDATE 语句

  DECLARE sql VARCHAR(2000);
  SET sql = 'UPDATE db.tbl SET Foo = 0';
  PREPARE stmt FROM sql;
  EXECUTE stmt;
  DEALLOCATE stmt;
Run Code Online (Sandbox Code Playgroud)

如果为从 information_schema.tables 中进行选择声明游标,则可以使用游标循环来处理UPDATE返回的每个 table_name 的动态语句。

  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;
Run Code Online (Sandbox Code Playgroud)

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

跟进

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

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

因为我们可能在多个数据库中有表,所以table_name不足以标识一个表;我们需要知道该表位于哪个数据库中。我们可以只引用该表,而不是use db在运行之前使用“”语句进行处理。UPDATEUPDATE db.mytable SET Foo...

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

UPDATE `mydatabase`.`mytable` SET `Foo` = 0 
Run Code Online (Sandbox Code Playgroud)

但我们想用table_schema和的值来代替 和。如果我们运行这个 SELECTtable_namemydatabasemytable

SELECT 'UPDATE `mydatabase`.`mytable` SET `Foo` = 0' AS sql
Run Code Online (Sandbox Code Playgroud)

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

如果我们将该字符串分成几部分,然后使用 CONCAT 将它们重新串在一起,我们会得到同样的结果,例如

SELECT CONCAT('UPDATE `','mydatabase','`.`','mytable','` SET `Foo` = 0') AS sql
Run Code Online (Sandbox Code Playgroud)

我们可以使用该查询作为我们要运行的语句的模型information_schema.columns。我们将用对表中提供数据库和表名的列的引用来替换'mydatabase'和。'mytable'information_schema.columns

SELECT CONCAT('UPDATE `',c.table_schema,'`.`',c.table_name,'` SET `Foo` = 0') AS sql
  FROM information_schema.columns 
 WHERE c.column_name = 'Foo'
Run Code Online (Sandbox Code Playgroud)

有一些数据库我们绝对不想更新... mysql,,, information_schemaperformance_schema我们需要将包含我们要更新的表的数据库列入白名单

  AND c.table_schema IN ('mydatabase','anotherdatabase')
Run Code Online (Sandbox Code Playgroud)

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

  AND c.table_schema NOT IN ('mysql','information_schema','performance_schema')
Run Code Online (Sandbox Code Playgroud)

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

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


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

MySQL 提供了一种工具/功能,允许我们在 MySQL 存储程序(例如,存储过程)的上下文中执行基本上任何字符串作为 SQL 语句。我们要使用的功能称为动态 SQL

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

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

DECLARE str VARCHAR(2000);
SET str = 'UPDATE mytable SET mycol = 0 WHERE mycol < 0';
Run Code Online (Sandbox Code Playgroud)

要获取计算的内容str并作为 SQL 语句执行,基本轮廓是:

PREPARE stmt FROM str;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Run Code Online (Sandbox Code Playgroud)

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

SELECT bah FROM humbug
Run Code Online (Sandbox Code Playgroud)

并将其转换为游标定义:

DECLARE mycursor FOR SELECT bah FROM humbug ;
Run Code Online (Sandbox Code Playgroud)

我们想要的是执行它并循环它返回的行。为了执行语句并准备结果集,我们“打开”游标

OPEN mycursor; 
Run Code Online (Sandbox Code Playgroud)

当我们完成它时,我们将发出“关闭”,以释放结果集,以便 MySQL 服务器知道我们不再需要它,并且可以清理并释放分配给它的资源。

CLOSE mycursor;
Run Code Online (Sandbox Code Playgroud)

但是,在关闭游标之前,我们希望“循环”结果集,获取每一行,并对该行执行某些操作。我们用来将结果集中的下一行获取到过程变量中的语句是:

FETCH mycursor INTO some_variable;
Run Code Online (Sandbox Code Playgroud)

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

DECLARE some_variable VARCHAR(2000); 
Run Code Online (Sandbox Code Playgroud)

由于我们的游标(SELECT 语句)仅返回一列,因此我们只需要一个变量。如果我们有更多列,则每列都需要一个变量。

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

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

mylabel: LOOP
  -- do something
END LOOP mylabel;
Run Code Online (Sandbox Code Playgroud)

这本身就构成了一个非常精细的无限循环,因为该循环没有“出口”。幸运的是,MySQL 为我们提供了该LEAVE语句作为退出循环的方法。我们通常不想在第一次进入循环时退出循环,因此通常会使用一些条件测试来确定我们是否已完成,并且应该退出循环,或者我们还没有完成,并且应该绕过再次循环。

 mylabel: LOOP
     -- do something useful
     IF some_condition THEN 
         LEAVE mylabel;
     END IF;
 END LOOP mylabel;
Run Code Online (Sandbox Code Playgroud)

在我们的例子中,我们想要循环遍历结果集中的所有行,因此我们将在循环内放置FETCH第一个语句(这是我们想做的有用的事情)。

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

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

 DECLARE CONTINUE HANDLER FOR NOT FOUND 
Run Code Online (Sandbox Code Playgroud)

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

 SET done = TRUE;
Run Code Online (Sandbox Code Playgroud)

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

 DECLARE done TINYINT(1) DEFAULT FALSE;
Run Code Online (Sandbox Code Playgroud)

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

 mylabel: LOOP
     FETCH mycursor INTO some_variable;
     IF done THEN 
         LEAVE mylabel;
     END IF;
     -- do something with the row
 END LOOP mylabel;
Run Code Online (Sandbox Code Playgroud)

“对行做一些事情”是我们想要获取内容some_variable并用它做一些有用的事情的地方。我们的游标返回一个我们想要作为 SQL 语句执行的字符串。MySQL 为我们提供了动态 SQL功能,我们可以使用它来做到这一点。

注意:MySQL 对过程中语句的顺序有规则。例如,该DECLARE声明必须出现在开头。我认为 CONTINUE HANDLER 必须是最后声明的。


再次强调:游标动态 SQL功能在 MySQL 存储程序(例如存储过程)的上下文中可用。我上面给出的例子只是一个过程主体的例子。

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

DELIMITER $$

DROP PROCEDURE IF EXISTS myproc $$

CREATE PROCEDURE myproc 
NOT DETERMINISTIC
MODIFIES SQL DATA
BEGIN

   -- procedure body goes here

END$$

DELIMITER ;
Run Code Online (Sandbox Code Playgroud)

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