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

时间:2015-05-31 04:48:07

标签: mysql mysqli mysql-event

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

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

2 个答案:

答案 0 :(得分:1)

不,不是一个声明。

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

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

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

您可以使用动态SQL在MySQL存储程序中执行UPDATE语句(例如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处理动态UPDATE语句。

  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的表的名称,我们可以从information_schema.columns表运行查询。 (这是MySQL information_schema数据库中提供的表之一。)

因为我们可能在多个数据库中有表,所以table_name不足以标识表;我们需要知道表格所在的数据库。而不是使用&#34; use db&#34;在我们运行UPDATE之前的声明,我们只需引用表UPDATE db.mytable SET Foo...

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

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

但我们希望用table_schematable_name代替mydatabasemytable替换值。如果我们运行这个SELECT

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

这会返回一行,包含一个列(该列恰好名为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'

我们肯定会有一些数据库要更新... mysqlinformation_schemaperformance_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语句执行(例如,存储过程。我们的功能&#39 ;重新使用称为动态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 ;

我们想要的是执行它并遍历它返回的行。为了执行语句并准备结果集,我们打开&#34;光标

OPEN mycursor; 

当我们完成它之后,我们会发布一个&#34;关闭&#34;,以发布结果集,因此MySQL服务器知道我们不再需要它了,并且可以清理,并释放分配给它的资源。

CLOSE mycursor;

但是,在我们关闭光标之前,我们想要&#34;循环&#34;通过结果集,获取每一行,并对该行执行某些操作。我们用于将结果集中的下一行转换为过程变量的语句是:

FETCH mycursor INTO some_variable;

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

DECLARE some_variable VARCHAR(2000); 

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

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

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

mylabel: LOOP
  -- do something
END LOOP mylabel;

这本身就形成了一个非常精细的无限循环,因为该循环没有&#34;退出&#34;。幸运的是,MySQL为我们提供了LEAVE语句作为退出循环的方法。我们通常不会在第一次输入时退出循环,因此我们通常会使用一些条件测试来确定我们是否已完成,并应退出循环,或者我们&# 39;没有完成,应该再循环一遍。

 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;

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

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

&#34;对行做什么&#34;是我们想要获取some_variable的内容并使用它做一些有用的事情。我们的游标返回一个我们想要作为SQL语句执行的字符串。 MySQL为我们提供了我们可以用来实现的动态SQL 功能。

注意:MySQL有关于过程中语句顺序的规则。例如,DECLARE语句必须在开头。我认为CONTINUE HANDLER必须是最后宣布的东西。

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

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

DELIMITER $$

DROP PROCEDURE IF EXISTS myproc $$

CREATE PROCEDURE myproc 
NOT DETERMINISTIC
MODIFIES SQL DATA
BEGIN

   -- procedure body goes here

END$$

DELIMITER ;

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

答案 1 :(得分: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';