我想找到我的数据库中包含列名Foo的所有表,并将其值更新为0,我正在考虑这样的事情,但我不知道如何在该代码上放置UPDATE,我计划在mysql数据库中的事件上有这个声明,我使用WAMP,这个想法基本上是每天运行一个事件,它将我所有的'Foo'列设置为0,而不必手动操作
SELECT TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name LIKE 'Foo'
答案 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_schema
和table_name
代替mydatabase
和mytable
替换值。如果我们运行这个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'
我们肯定会有一些数据库不要更新... 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语句执行(例如,存储过程。我们的功能&#39 ;重新使用称为动态SQL 。
要使用动态SQL ,我们使用语句PREPARE
,EXECUTE
和DEALLOCATE 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';