Không, không phải trong một tuyên bố nào.
Để lấy tên của tất cả các bảng có chứa cột có tên Foo
:
SELECT table_schema, table_name
FROM information_schema.columns
WHERE column_name = 'Foo'
Sau đó, bạn cần một câu lệnh CẬP NHẬT cho mỗi bảng. (Có thể cập nhật nhiều bảng trong một câu lệnh duy nhất, nhưng đó sẽ cần phải là một phép nối chéo (không cần thiết).) Tốt hơn nên thực hiện từng bảng riêng biệt.
Bạn có thể sử dụng SQL động để thực thi các câu lệnh UPDATE trong chương trình được lưu trữ MySQL (ví dụ:PROCEDURE)
DECLARE sql VARCHAR(2000);
SET sql = 'UPDATE db.tbl SET Foo = 0';
PREPARE stmt FROM sql;
EXECUTE stmt;
DEALLOCATE stmt;
Nếu bạn khai báo một con trỏ cho select từ information_schema.tables, bạn có thể sử dụng vòng lặp con trỏ để xử lý UPDATE
động câu lệnh cho mỗi tên_bảng được trả về.
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;
(Đây chỉ là một phác thảo sơ bộ của một ví dụ, không được kiểm tra hoặc thử nghiệm cú pháp.)
THEO DÕI
Một số ghi chú ngắn gọn về một số ý tưởng có thể đã được lược bỏ trong câu trả lời ở trên.
Để lấy tên của các bảng chứa cột Foo
, chúng tôi có thể chạy một truy vấn từ information_schema.columns
bàn. (Đó là một trong những bảng được cung cấp trong MySQL information_schema
cơ sở dữ liệu.)
Bởi vì chúng ta có thể có các bảng trong nhiều cơ sở dữ liệu, tên_bảng không đủ để xác định một bảng; chúng ta cần biết cơ sở dữ liệu của bảng đó là gì. Thay vì sử dụng " use db
" "trước khi chúng tôi chạy một câu lệnh UPDATE
, chúng ta chỉ có thể tham chiếu bảng UPDATE db.mytable SET Foo ...
.
Chúng tôi có thể sử dụng truy vấn information_schema.columns
của mình để tiếp tục và xâu chuỗi lại với nhau (nối) các phần chúng ta cần tạo cho một câu lệnh UPDATE và có câu lệnh SELECT trả về các câu lệnh thực mà chúng tôi cần chạy để cập nhật cột Foo
, về cơ bản thế này:
UPDATE `mydatabase`.`mytable` SET `Foo` = 0
Nhưng chúng tôi muốn thay thế các giá trị từ table_schema
và table_name
thay cho mydatabase
và mytable
. Nếu chúng tôi chạy SELECT này
SELECT 'UPDATE `mydatabase`.`mytable` SET `Foo` = 0' AS sql
Điều đó trả về cho chúng ta một hàng duy nhất, chứa một cột duy nhất (cột này có tên là sql
, nhưng tên của cột không quan trọng đối với chúng tôi). Giá trị của cột sẽ chỉ là một chuỗi. Nhưng chuỗi mà chúng tôi nhận lại tình cờ là (chúng tôi hy vọng) là một câu lệnh SQL mà chúng tôi có thể chạy.
Chúng tôi sẽ nhận được điều tương tự nếu chúng tôi bẻ chuỗi đó thành nhiều mảnh và sử dụng CONCAT để xâu chuỗi chúng lại với nhau cho chúng tôi, ví dụ:
SELECT CONCAT('UPDATE `','mydatabase','`.`','mytable','` SET `Foo` = 0') AS sql
Chúng tôi có thể sử dụng truy vấn đó làm mô hình cho câu lệnh mà chúng tôi muốn chạy với information_schema.columns
. Chúng tôi sẽ thay thế 'mydatabase'
và 'mytable'
với các tham chiếu đến các cột từ information_schema.columns
bảng cung cấp cho chúng tôi cơ sở dữ liệu và tên_bảng.
SELECT CONCAT('UPDATE `',c.table_schema,'`.`',c.table_name,'` SET `Foo` = 0') AS sql
FROM information_schema.columns
WHERE c.column_name = 'Foo'
Có một số cơ sở dữ liệu mà chúng tôi chắc chắn không muốn cập nhật ... mysql
, information_schema
, performance_schema
. Chúng tôi cần đưa vào danh sách trắng các cơ sở dữ liệu có chứa bảng mà chúng tôi muốn cập nhật
AND c.table_schema IN ('mydatabase','anotherdatabase')
- hoặc - chúng tôi cần đưa vào danh sách đen những cơ sở dữ liệu mà chúng tôi chắc chắn không muốn cập nhật
AND c.table_schema NOT IN ('mysql','information_schema','performance_schema')
Chúng tôi có thể chạy truy vấn đó (chúng tôi có thể thêm ORDER BY
nếu chúng ta muốn các hàng được trả về theo một thứ tự cụ thể) và những gì chúng ta nhận lại là danh sách chứa các câu lệnh mà chúng ta muốn chạy. Nếu chúng tôi lưu tập hợp chuỗi đó dưới dạng tệp văn bản thuần túy (không bao gồm hàng tiêu đề và định dạng bổ sung), thêm dấu chấm phẩy ở cuối mỗi dòng, chúng tôi sẽ có một tệp mà chúng tôi có thể thực thi từ mã mysql> > máy khách dòng lệnh.
(Nếu bất kỳ điều nào ở trên gây nhầm lẫn, hãy cho tôi biết.)
Phần tiếp theo phức tạp hơn một chút. Phần còn lại của phần này đề cập đến một giải pháp thay thế để lưu đầu ra từ SELECT dưới dạng tệp văn bản thuần túy và thực thi các câu lệnh từ mysql
máy khách dòng lệnh.
MySQL cung cấp một cơ sở / tính năng cho phép chúng tôi thực thi về cơ bản bất kỳ chuỗi dưới dạng câu lệnh SQL, trong ngữ cảnh của chương trình được lưu trữ MySQL (ví dụ:một thủ tục được lưu trữ. Tính năng chúng tôi sẽ sử dụng được gọi là dynamic SQL .
Để sử dụng SQL động , chúng tôi sử dụng các câu lệnh PREPARE
, THỰC HIỆN
và DEALLOCATE PREPARE
. (Thỏa thuận phân bổ không thực sự cần thiết, MySQL sẽ dọn dẹp cho chúng tôi nếu chúng tôi không sử dụng nó, nhưng tôi nghĩ dù sao cũng nên làm điều đó.)
Một lần nữa, SQL động có sẵn CHỈ trong ngữ cảnh của một chương trình được lưu trữ MySQL. Để làm điều này, chúng ta cần có một chuỗi chứa câu lệnh SQL mà chúng ta muốn thực thi. Ví dụ đơn giản, giả sử chúng ta có cái này:
DECLARE str VARCHAR(2000);
SET str = 'UPDATE mytable SET mycol = 0 WHERE mycol < 0';
Để lấy nội dung của str
được đánh giá và thực thi như một câu lệnh SQL, phác thảo cơ bản là:
PREPARE stmt FROM str;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Phần phức tạp tiếp theo là đặt điều đó cùng với truy vấn mà chúng tôi đang chạy để nhận giá trị chuỗi mà chúng tôi muốn thực thi dưới dạng câu lệnh SQL. Để làm điều đó, chúng tôi đặt một vòng lặp con trỏ lại với nhau. Đề cương cơ bản cho điều đó là sử dụng câu lệnh SELECT của chúng tôi:
SELECT bah FROM humbug
Và biến nó thành một định nghĩa con trỏ:
DECLARE mycursor FOR SELECT bah FROM humbug ;
Những gì chúng ta muốn là thực thi điều đó và lặp qua các hàng mà nó trả về. Để thực hiện câu lệnh và chuẩn bị tập kết quả, chúng tôi "mở" con trỏ
OPEN mycursor;
Khi chúng tôi hoàn thành nó, chúng tôi sẽ đưa ra một "đóng", để phát hành tập kết quả, vì vậy máy chủ MySQL biết chúng tôi không cần nó nữa và có thể dọn dẹp và giải phóng tài nguyên được phân bổ cho tập hợp đó.
CLOSE mycursor;
Tuy nhiên, trước khi đóng con trỏ, chúng ta muốn "lặp" qua tập kết quả, tìm nạp từng hàng và làm điều gì đó với hàng đó. Câu lệnh mà chúng tôi sử dụng để chuyển hàng tiếp theo từ tập kết quả thành một biến thủ tục là:
FETCH mycursor INTO some_variable;
Trước khi có thể tìm nạp các hàng vào các biến, chúng ta cần xác định các biến, ví dụ:
DECLARE some_variable VARCHAR(2000);
Vì con trỏ của chúng tôi (câu lệnh SELECT) chỉ trả về một cột duy nhất, chúng tôi chỉ cần một biến. Nếu chúng tôi có nhiều cột hơn, chúng tôi sẽ cần một biến cho mỗi cột.
Cuối cùng, chúng tôi đã tìm nạp hàng cuối cùng từ tập kết quả. Khi chúng tôi cố gắng tìm nạp cái tiếp theo, MySQL sẽ gặp lỗi.
Các ngôn ngữ lập trình khác sẽ cho phép chúng tôi thực hiện while
vòng lặp và hãy để chúng tôi tìm nạp các hàng và thoát khỏi vòng lặp khi chúng tôi đã xử lý tất cả. MySQL phức tạp hơn. Để thực hiện một vòng lặp:
mylabel: LOOP
-- do something
END LOOP mylabel;
Điều đó tự nó tạo nên một vòng lặp vô hạn rất tốt, bởi vì vòng lặp đó không có "lối ra". May mắn thay, MySQL cung cấp cho chúng ta LEAVE
câu lệnh như một cách để thoát khỏi một vòng lặp. Chúng tôi thường không muốn thoát khỏi vòng lặp ngay lần đầu tiên chúng tôi nhập nó, vì vậy thường có một số kiểm tra có điều kiện mà chúng tôi sử dụng để xác định xem chúng tôi đã hoàn thành chưa và nên thoát khỏi vòng lặp hay chúng tôi chưa hoàn thành và nên đi vòng lại vòng lặp lại.
mylabel: LOOP
-- do something useful
IF some_condition THEN
LEAVE mylabel;
END IF;
END LOOP mylabel;
Trong trường hợp của chúng tôi, chúng tôi muốn lặp lại tất cả các hàng trong tập kết quả, vì vậy chúng tôi sẽ đặt một FETCH
câu lệnh đầu tiên bên trong vòng lặp (điều hữu ích mà chúng tôi muốn làm).
Để có được mối liên hệ giữa lỗi mà MySQL ném ra khi chúng tôi cố gắng tìm nạp qua hàng cuối cùng trong tập kết quả và kiểm tra có điều kiện, chúng tôi phải xác định xem chúng tôi có nên rời khỏi ...
MySQL cung cấp một cách để chúng tôi xác định một TIẾP TỤC XỬ LÝ
(một số câu lệnh chúng tôi muốn thực hiện) khi lỗi được ném ra ...
DECLARE CONTINUE HANDLER FOR NOT FOUND
Hành động chúng tôi muốn thực hiện là đặt một biến thành TRUE.
SET done = TRUE;
Trước khi có thể chạy SET, chúng ta cần xác định biến:
DECLARE done TINYINT(1) DEFAULT FALSE;
Với điều đó, chúng tôi có thể thay đổi LOOP của mình để kiểm tra xem đã xong
biến được đặt thành TRUE, làm điều kiện thoát, vì vậy vòng lặp của chúng ta trông giống như sau:
mylabel: LOOP
FETCH mycursor INTO some_variable;
IF done THEN
LEAVE mylabel;
END IF;
-- do something with the row
END LOOP mylabel;
"Làm điều gì đó với hàng" là nơi chúng tôi muốn lấy nội dung của some_variable
và làm điều gì đó hữu ích với nó. Con trỏ của chúng tôi đang trả về cho chúng tôi một chuỗi mà chúng tôi muốn thực thi dưới dạng một câu lệnh SQL. Và MySQL cung cấp cho chúng ta SQL động tính năng mà chúng tôi có thể sử dụng để làm điều đó.
LƯU Ý:MySQL có các quy tắc về thứ tự của các câu lệnh trong thủ tục. Ví dụ: DECLARE
tuyên bố phải xuất hiện ở đầu. Và tôi nghĩ BỘ XỬ LÝ TIẾP TỤC phải là thứ cuối cùng được khai báo.
Một lần nữa: Con trỏ và SQL động các tính năng có sẵn CHỈ trong ngữ cảnh của một chương trình được lưu trữ MySQL, chẳng hạn như một thủ tục được lưu trữ. Ví dụ tôi đưa ra ở trên chỉ là ví dụ của body của một thủ tục.
Để tạo điều này dưới dạng một thủ tục được lưu trữ, nó sẽ cần được kết hợp như một phần của một cái gì đó như sau:
DELIMITER $$
DROP PROCEDURE IF EXISTS myproc $$
CREATE PROCEDURE myproc
NOT DETERMINISTIC
MODIFIES SQL DATA
BEGIN
-- procedure body goes here
END$$
DELIMITER ;
Hy vọng rằng điều đó giải thích ví dụ tôi đã đưa ra một cách chi tiết hơn.