Làm cách nào tôi có thể lặp qua tất cả các hàng của bảng? (MySQL)


89

Tôi có một bảng A và có một ID khóa chính.

Bây giờ tôi muốn đi qua tất cả các hàng trong A.

Tôi đã tìm thấy một cái gì đó như 'cho mỗi bản ghi trong A', nhưng đây có vẻ không phải là cách bạn thực hiện trong MySQL.

Điều là đối với mỗi hàng tôi muốn lấy một trường và biến đổi nó, chèn nó vào một bảng khác và sau đó cập nhật một số trường của hàng. Tôi có thể đặt phần chọn và phần chèn vào một câu lệnh, nhưng tôi cũng không biết làm cách nào để tải bản cập nhật vào đó. Vì vậy, tôi muốn lặp lại. Và để thực hành, tôi không muốn sử dụng bất kỳ thứ gì khác ngoài MySQL.

biên tập

Tôi sẽ đánh giá cao một ví dụ.

Và một giải pháp không cần phải đưa vào thủ tục.

chỉnh sửa 2

Được rồi, hãy nghĩ về kịch bản này:

Bảng A và B, mỗi bảng có các trường ID và VAL.

Bây giờ đây là mã giả cho những gì tôi muốn làm:

for(each row in A as rowA)
{
  insert into B(ID, VAL) values(rowA[ID], rowA[VAL]);
}

về cơ bản sao chép nội dung của A vào B bằng cách sử dụng một vòng lặp.

(đây chỉ là một ví dụ đơn giản, tất nhiên bạn sẽ không sử dụng vòng lặp cho việc này.)}


Xin chào Rafeel, tôi gặp lỗi là: My SQL> for (mỗi hàng trong wp_mobiune_ecomne_user là x) {insert into wp_mobiune_ecomne_user (architecture_new) giá trị (x [giới tính])}; RROR 1064 (42000): Bạn có lỗi trong cú pháp SQL của mình; kiểm tra hướng dẫn mà tương ứng với phiên bản máy chủ MySQL của bạn cho đúng cú pháp để sử dụng gần 'for (mỗi hàng trong wp_mobiune_ecommune_user như x) {chèn vào wp_mobiune_ecommune' tại dòng 1
Dinesh kandpal

Câu trả lời:


135

Vì gợi ý của một vòng lặp ngụ ý yêu cầu giải pháp kiểu thủ tục. Đây là của tôi.

Bất kỳ truy vấn nào hoạt động trên bất kỳ bản ghi nào được lấy từ một bảng đều có thể được bao bọc trong một thủ tục để làm cho nó chạy qua từng hàng của bảng như sau:

DROP PROCEDURE IF EXISTS ROWPERROW;
DELIMITER ;;

Sau đó, đây là quy trình theo ví dụ của bạn (table_A và table_B được sử dụng để rõ ràng)

CREATE PROCEDURE ROWPERROW()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
SELECT COUNT(*) FROM table_A INTO n;
SET i=0;
WHILE i<n DO 
  INSERT INTO table_B(ID, VAL) SELECT (ID, VAL) FROM table_A LIMIT i,1;
  SET i = i + 1;
END WHILE;
End;
;;

Sau đó, đừng quên đặt lại dấu phân cách

DELIMITER ;

Và chạy thủ tục mới

CALL ROWPERROW();

Bạn có thể làm bất cứ điều gì bạn thích tại dòng "CHÈN VÀO" mà tôi chỉ cần sao chép từ yêu cầu mẫu của bạn.

Lưu ý CẨN THẬN rằng dòng "CHÈN VÀO" được sử dụng ở đây phản ánh dòng trong câu hỏi. Theo các nhận xét cho câu trả lời này, bạn cần đảm bảo rằng truy vấn của bạn đúng về mặt cú pháp cho phiên bản SQL nào bạn đang chạy.

Trong trường hợp đơn giản khi trường ID của bạn được tăng dần và bắt đầu từ 1 dòng trong ví dụ có thể trở thành:

INSERT INTO table_B(ID, VAL) VALUES(ID, VAL) FROM table_A WHERE ID=i;

Thay thế dòng "SELECT COUNT" bằng

SET n=10;

Sẽ cho phép bạn kiểm tra truy vấn của mình trên 10 bản ghi đầu tiên chỉ trong table_A.

Một điều cuối cùng. Quá trình này cũng rất dễ dàng để lồng trên các bảng khác nhau và là cách duy nhất tôi có thể thực hiện một quy trình trên một bảng để chèn động các số lượng bản ghi khác nhau vào một bảng mới từ mỗi hàng của bảng mẹ.

Nếu bạn cần nó chạy nhanh hơn thì hãy cố gắng đặt nó dựa trên, nếu không thì điều này cũng ổn. Bạn cũng có thể viết lại những điều trên ở dạng con trỏ nhưng nó có thể không cải thiện hiệu suất. ví dụ:

DROP PROCEDURE IF EXISTS cursor_ROWPERROW;
DELIMITER ;;

CREATE PROCEDURE cursor_ROWPERROW()
BEGIN
  DECLARE cursor_ID INT;
  DECLARE cursor_VAL VARCHAR;
  DECLARE done INT DEFAULT FALSE;
  DECLARE cursor_i CURSOR FOR SELECT ID,VAL FROM table_A;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
  OPEN cursor_i;
  read_loop: LOOP
    FETCH cursor_i INTO cursor_ID, cursor_VAL;
    IF done THEN
      LEAVE read_loop;
    END IF;
    INSERT INTO table_B(ID, VAL) VALUES(cursor_ID, cursor_VAL);
  END LOOP;
  CLOSE cursor_i;
END;
;;

Hãy nhớ khai báo các biến bạn sẽ sử dụng cùng kiểu với các biến từ các bảng được truy vấn.

Lời khuyên của tôi là hãy sử dụng các truy vấn setbased khi bạn có thể và chỉ sử dụng các vòng lặp hoặc con trỏ đơn giản nếu bạn phải.


2
CHÈN VÀO bảng_B (ID, VAL) GIÁ TRỊ (ID, VAL) TỪ bảng_A LIMIT i, 1; đưa ra một lỗi cú pháp.
Jonathan

1
Có vẻ như bị thiếu 'DECLARE done INT DEFAULT FALSE;' sau khi khai báo cursor_i
ErikL

1
Trường hợp thuộc tính done được đặt thành true, tôi nên đặt nó hay là một từ dành riêng được sử dụng tự động bởi con trỏ mysql?
IgniteCoders

1
Lệnh gọi INSERT INTO gây ra lỗi cú pháp của SQL 5.5, lệnh gọi đúng là INSERT INTO table_B (ID, VAL) SELECT (ID, VAL) FROM table_A WHERE ID = i;
Ambar,

Điều này sẽ mất cả đời, vì vậy hãy cố gắng tăng kích thước lô lên 1000 bằng cách thay đổi giới hạn và gia số: LIMIT i,1000;set i = i + 1000;
Alan Deep

16

Bạn thực sự nên sử dụng một giải pháp dựa trên tập hợp liên quan đến hai truy vấn (chèn cơ bản):

INSERT INTO TableB (Id2Column, Column33, Column44)
SELECT id, column1, column2 FROM TableA

UPDATE TableA SET column1 = column2 * column3

Và để biến đổi của bạn:

INSERT INTO TableB (Id2Column, Column33, Column44)
SELECT 
    id, 
    column1 * column4 * 100, 
    (column2 / column12) 
FROM TableA

UPDATE TableA SET column1 = column2 * column3

Bây giờ nếu biến đổi của bạn phức tạp hơn thế và liên quan đến nhiều bảng, hãy đăng một câu hỏi khác kèm theo chi tiết.


+1 cho ví dụ. mặc dù tôi nghi ngờ điều này có thể áp dụng trong b / c trang web của tôi, bản cập nhật sau đó có liên quan đến chèn và đặc biệt là những hàng đã chọn gây ra chèn cụ thể nào. Đây là lý do tại sao tôi đề xuất một vòng lặp, vì vậy tôi có thể chọn / chèn / cập nhật hiệu quả cho từng hàng riêng lẻ.
Raffael

2
@ Raffael1984: chỉnh sửa câu hỏi của bạn để thêm các điều kiện cho "các hàng cụ thể gây ra chèn cụ thể" và chúng tôi có thể trợ giúp điều đó. Bạn thực sự không muốn đi theo tuyến con trỏ / vòng lặp - nó cực kỳ kém hiệu quả.
Raj More

ồ ... bạn biết đấy, tôi rất vui khi sử dụng cách truy vấn dựa trên định sẵn! nhưng hiệu quả không phải là động lực ở đây vì câu hỏi của tôi là mối quan tâm học thuật nhiều hơn. cái bàn đủ nhỏ để tôi có thể làm bằng tay. Tôi thực sự đánh giá cao một phiên bản lặp lại. Tôi có thể đăng lại vấn đề đó để cung cấp thêm chi tiết để cho phép ai đó giúp tôi với kiểu dựa trên bộ.
Raffael

đồng ý với @RajMore, con trỏ / vòng lặp không hiệu quả, dưới đây là 2 liên kết về hiệu suất con trỏ để tham khảo: 1. rpbouman.blogspot.tw/2006/09/refactoring-mysql-cursors.html 2. stackoverflow.com/questions/11549567/ …
Browny Lin

@RajMore Bạn có thể giúp tôi trong câu hỏi
Nurav

4

CURSORS là một tùy chọn ở đây, nhưng nhìn chung không thích vì chúng thường không sử dụng tốt nhất công cụ truy vấn. Hãy xem xét điều tra 'Đặt truy vấn dựa trên' để xem liệu bạn có thể đạt được những gì bạn muốn làm mà không cần sử dụng CURSOR hay không.


Tôi không thể tìm thấy nhiều thông tin về các truy vấn dựa trên bộ. nhưng tôi đoán bạn có nghĩa là một lựa chọn vào loại tuyên bố. vấn đề là tôi cũng cần có một bản cập nhật được thực thi.
Raffael

0

Ví dụ của Mr Purple mà tôi đã sử dụng trong trình kích hoạt mysql như vậy,

begin
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
Select COUNT(*) from user where deleted_at is null INTO n;
SET i=0;
WHILE i<n DO 
  INSERT INTO user_notification(notification_id,status,userId)values(new.notification_id,1,(Select userId FROM user LIMIT i,1)) ;
  SET i = i + 1;
END WHILE;
end

2
Bạn có thể vui lòng thêm một số thông tin về cách giải pháp của bạn hoạt động không?
TheTanic

-25
    Use this:

    $stmt = $user->runQuery("SELECT * FROM tbl WHERE ID=:id");
    $stmt->bindparam(":id",$id);
    $stmt->execute();

        $stmt->bindColumn("a_b",$xx);
        $stmt->bindColumn("c_d",$yy);


    while($rows = $stmt->fetch(PDO::FETCH_BOUND))
    {
        //---insert into new tble
    }   
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.