不,不是在一个单一的声明中。
要获取所有包含名为列的表的名称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_schema
值table_name
替换mydatabase
和mytable
。如果我们运行这个 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 会为我们清理,但我认为无论如何这样做是个好习惯。)EXECUTE
DEALLOCATE 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 ;
希望这能更详细地解释我给出的示例。