我想找到我的数据库中包含列名 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)
不,不是在一个声明中。
要获取包含名为 的列的所有表的名称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_schema。performance_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,EXECUTE和DEALLOCATE 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)
希望这能更详细地解释我给出的示例。
| 归档时间: |
|
| 查看次数: |
9748 次 |
| 最近记录: |