2

I want to find all the tables in my db that contain the column name Foo, and update its value to 0, I was thinking something like this, but I don't know how to place the UPDATE on that code, I plan on having this statement on the Events inside the MySQL database, I'm using WAMP, the idea is basically having an event run daily which sets all my 'Foo' Columns to 0 without me having to do it manually

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

2 回答 2

5

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

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

SELECT table_schema, table_name
  FROM information_schema.columns 
  WHERE column_name = 'Foo'

然后,您需要为每个表创建一个 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;

如果为从 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;

(这只是一个示例的粗略轮廓,没有检查或测试语法。)

跟进

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

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

因为我们可能在多个数据库中都有表,table_name 不足以识别一个表;我们需要知道该表在哪个数据库中。我们可以use db在运行之前使用“”语句,而不是UPDATE直接引用该表UPDATE db.mytable SET Foo...

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

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

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

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

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

如果我们把那根绳子分成几块,我们会得到同样的结果,并使用 CONCAT 为我们将它们重新串在一起,例如

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

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

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

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

  AND c.table_schema IN ('mydatabase','anotherdatabase')

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

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

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

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


下一部分稍微复杂一些。其余部分处理将 SELECT 的输出保存为纯文本文件的替代方法,并mysql从命令行客户端执行语句。

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

要使用动态 SQL,我们使用语句PREPARE和。(deallocate 并不是绝对必要的,如果我们不使用它,MySQL 会为我们清理,但我认为无论如何这样做是个好习惯。)EXECUTEDEALLOCATE PREPARE

同样,动态 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 ;

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

OPEN mycursor; 

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

CLOSE mycursor;

但是,在我们关闭游标之前,我们想“循环”遍历结果集,获取每一行,并对行做一些事情。我们用来从结果集中获取下一行到过程变量的语句是:

FETCH mycursor INTO some_variable;

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

DECLARE some_variable VARCHAR(2000); 

由于我们的游标(SELECT 语句)只返回一列,我们只需要一个变量。如果我们有更多列,我们需要为每一列设置一个变量。

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

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

mylabel: LOOP
  -- do something
END LOOP mylabel;

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

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

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

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

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

 DECLARE CONTINUE HANDLER FOR NOT FOUND 

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

 SET done = TRUE;

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

 DECLARE done TINYINT(1) DEFAULT FALSE;

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

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

“对行做一些事情”是我们想要获取内容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 ;

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

于 2015-05-31T05:26:14.110 回答
0

这应该获取数据库中的所有表,并在每个表中附加更新列 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';

于 2021-04-10T22:49:54.750 回答