ฉันจะวนรอบทุกแถวของตารางได้อย่างไร (MySQL)


90

ฉันมีตาราง A และมีรหัสคีย์หลักหนึ่งรหัส

ตอนนี้ฉันต้องการผ่านทุกแถวใน A

ฉันพบบางอย่างเช่น 'สำหรับแต่ละระเบียนใน A' แต่ดูเหมือนว่าคุณจะทำใน MySQL ไม่ได้

สิ่งนี้มีไว้สำหรับแต่ละแถวที่ฉันต้องการใช้ฟิลด์และแปลงมันแทรกลงในตารางอื่นจากนั้นอัปเดตฟิลด์บางส่วนของแถว ฉันสามารถใส่ส่วนที่เลือกและส่วนแทรกลงในคำสั่งเดียวได้ แต่ฉันไม่รู้ว่าจะรับการอัปเดตได้อย่างไร เลยอยากวน. และสำหรับทางปฏิบัติฉันไม่ต้องการใช้อย่างอื่นนอกจาก MySQL

แก้ไข

ฉันขอขอบคุณตัวอย่าง

และวิธีการแก้ปัญหาที่ไม่จำเป็นต้องมีขั้นตอน

แก้ไข 2

ลองนึกถึงสถานการณ์นี้:

ตาราง A และ B แต่ละรายการมี ID ฟิลด์และ VAL

ตอนนี้เป็นรหัสหลอกสำหรับสิ่งที่ฉันต้องการทำ:

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

โดยทั่วไปจะคัดลอกเนื้อหาของ A ลงใน B โดยใช้การวนซ้ำ

(นี่เป็นเพียงตัวอย่างง่ายๆแน่นอนว่าคุณจะไม่ใช้ลูปสำหรับสิ่งนี้)}


สวัสดี Rafeel สำหรับฉันแล้วมันให้ข้อผิดพลาดเป็น: My SQL> สำหรับ (แต่ละแถวใน wp_mobiune_ecommune_user เป็น x) {แทรกใน wp_mobiune_ecommune_user (gender_new) ค่า (x [gender])}; RROR 1064 (42000): คุณมีข้อผิดพลาดในไวยากรณ์ SQL ของคุณ ตรวจสอบคู่มือที่ตรงกับเวอร์ชันเซิร์ฟเวอร์ MySQL ของคุณสำหรับไวยากรณ์ที่ถูกต้องที่จะใช้ใกล้ 'สำหรับ (แต่ละแถวใน wp_mobiune_ecommune_user เป็น x) {ใส่ใน wp_mobiune_ecommune' ที่บรรทัด 1
dinesh kandpal

คำตอบ:


137

เนื่องจากข้อเสนอแนะของลูปแสดงถึงการร้องขอโซลูชันประเภทโพรซีเดอร์ ที่นี่เป็นของฉัน

แบบสอบถามใด ๆ ที่ทำงานกับระเบียนเดียวที่นำมาจากตารางสามารถถูกรวมไว้ในขั้นตอนเพื่อให้รันผ่านแต่ละแถวของตารางได้ดังนี้:

DROP PROCEDURE IF EXISTS ROWPERROW;
DELIMITER ;;

นี่คือขั้นตอนตามตัวอย่างของคุณ (table_A และ table_B ใช้เพื่อความชัดเจน)

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;
;;

จากนั้นอย่าลืมรีเซ็ตตัวคั่น

DELIMITER ;

และรันโพรซีเดอร์ใหม่

CALL ROWPERROW();

คุณสามารถทำอะไรก็ได้ตามต้องการในบรรทัด "INSERT INTO" ซึ่งฉันคัดลอกมาจากคำขอตัวอย่างของคุณ

โปรดสังเกตอย่างระมัดระวังว่าบรรทัด "INSERT INTO" ที่ใช้ในที่นี้จะสะท้อนบรรทัดในคำถาม ตามความคิดเห็นของคำตอบนี้คุณต้องตรวจสอบให้แน่ใจว่าแบบสอบถามของคุณถูกต้องตามหลักไวยากรณ์สำหรับเวอร์ชันของ SQL ที่คุณใช้อยู่

ในกรณีง่ายๆที่ช่อง ID ของคุณเพิ่มขึ้นและเริ่มต้นที่ 1 บรรทัดในตัวอย่างอาจกลายเป็น:

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

แทนที่บรรทัด "SELECT COUNT" ด้วย

SET n=10;

จะช่วยให้คุณทดสอบแบบสอบถามของคุณใน 10 ระเบียนแรกใน table_A เท่านั้น

สิ่งสุดท้าย. กระบวนการนี้ยังง่ายมากในการซ้อนกันในตารางต่างๆและเป็นวิธีเดียวที่ฉันสามารถดำเนินการตามขั้นตอนบนตารางเดียวซึ่งแทรกระเบียนจำนวนต่างๆลงในตารางใหม่จากแต่ละแถวของตารางหลักแบบไดนามิก

หากคุณต้องการให้ทำงานได้เร็วขึ้นอย่าลืมลองตั้งค่าตามหากไม่เป็นเช่นนั้นก็ใช้ได้ คุณยังสามารถเขียนข้างต้นในรูปแบบเคอร์เซอร์ได้ แต่อาจไม่ช่วยเพิ่มประสิทธิภาพ เช่น:

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;
;;

อย่าลืมประกาศตัวแปรที่คุณจะใช้เป็นประเภทเดียวกับตัวแปรจากตารางที่สอบถาม

คำแนะนำของฉันคือให้ใช้แบบสอบถาม setbased เมื่อคุณทำได้และใช้เฉพาะลูปหรือเคอร์เซอร์ธรรมดาเท่านั้นหากคุณต้องการ


2
INSERT INTO table_B (ID, VAL) VALUES (ID, VAL) จาก table_A LIMIT i, 1; ทำให้เกิดข้อผิดพลาดทางไวยากรณ์
Jonathan

1
ดูเหมือนจะหายไป 'DECLARE done INT DEFAULT FALSE;' หลังจากการประกาศ cursor_i
ErikL

1
คุณสมบัติ done ถูกตั้งค่าเป็น true ที่ใดฉันควรวางไว้หรือเป็นคำสงวนที่เคอร์เซอร์ mysql ใช้โดยอัตโนมัติ
IgniteCoders

1
การเรียก INSERT INTO แสดงข้อผิดพลาดทางไวยากรณ์ ณ SQL 5.5 การเรียกที่ถูกต้องคือ INSERT INTO table_B (ID, VAL) SELECT (ID, VAL) จาก table_A WHERE ID = i;
Ambar

การดำเนินการนี้จะใช้เวลาตลอดชีวิตดังนั้นพยายามเพิ่มขนาดแบทช์เป็น 1,000 โดยเปลี่ยนขีด จำกัด และการเพิ่ม: LIMIT i,1000;และset i = i + 1000;
Alan Deep

16

คุณควรใช้ชุดโซลูชันที่เกี่ยวข้องกับสองแบบสอบถาม (การแทรกพื้นฐาน):

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

UPDATE TableA SET column1 = column2 * column3

และสำหรับการเปลี่ยนแปลงของคุณ:

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

UPDATE TableA SET column1 = column2 * column3

ตอนนี้ถ้าการแปลงของคุณซับซ้อนกว่านั้นและเกี่ยวข้องกับหลายตารางให้โพสต์คำถามอื่นพร้อมรายละเอียด


+1 สำหรับตัวอย่าง แม้ว่าฉันจะสงสัยว่าสิ่งนี้ใช้ได้กับแหล่งที่มา b / c ของฉัน แต่การอัปเดตหลังจากนั้นเกี่ยวข้องกับการแทรกและโดยเฉพาะอย่างยิ่งแถวที่เลือกทำให้เกิดการแทรกเฉพาะ นี่คือเหตุผลที่ฉันแนะนำลูปดังนั้นฉันสามารถเลือก / แทรก / อัปเดตสำหรับแต่ละแถวทีละแถวได้
Raffael

2
@ Raffael1984: แก้ไขคำถามของคุณเพื่อเพิ่มเงื่อนไขสำหรับ "แถวเฉพาะที่ทำให้เกิดการแทรกเฉพาะ" และเราสามารถช่วยได้ คุณไม่ต้องการไปเส้นทางเคอร์เซอร์ / ลูป - มันไม่มีประสิทธิภาพอย่างยิ่ง
Raj More

โอ้ดี ... คุณรู้ว่าฉันมีความสุขมากกว่าที่จะใช้วิธีการสืบค้นตามชุด! แต่ประสิทธิภาพไม่ใช่แรงจูงใจที่นี่เนื่องจากคำถามของฉันเป็นที่สนใจในเชิงวิชาการมากกว่า โต๊ะเล็กพอที่จะทำด้วยมือได้ ฉันอยากจะขอบคุณเวอร์ชันลูป ฉันอาจโพสต์ปัญหานั้นอีกครั้งเพื่อให้รายละเอียดเพิ่มเติมเพื่อให้ใครก็ได้ช่วยฉันในการกำหนดรูปแบบตามชุด
Raffael

เห็นด้วยกับ @RajMore เคอร์เซอร์ / ลูปไม่มีประสิทธิภาพด้านล่างนี้คือ 2 ลิงก์เกี่ยวกับประสิทธิภาพของเคอร์เซอร์สำหรับการอ้างอิง: 1. rpbouman.blogspot.tw/2006/09/refactoring-mysql-cursors.html 2. stackoverflow.com/questions/11549567/ …
Browny Lin

@RajMore คุณช่วยฉันในคำถาม
Nurav

4

CURSORS เป็นตัวเลือกที่นี่ แต่โดยทั่วไปมักจะขมวดคิ้วเนื่องจากมักไม่ได้ใช้ประโยชน์สูงสุดจากเครื่องมือสืบค้นข้อมูล ลองตรวจสอบ 'การสืบค้นตาม SET' เพื่อดูว่าคุณสามารถบรรลุสิ่งที่ต้องการทำได้โดยไม่ต้องใช้ CURSOR หรือไม่


ฉันไม่พบข้อมูลมากเกี่ยวกับการสืบค้นตามชุด แต่ฉันเดาว่าคุณหมายถึงเลือกเป็นประเภทของคำสั่ง ปัญหาคือฉันต้องดำเนินการอัปเดตด้วย
Raffael

0

ตัวอย่างของ Mr Purple ที่ฉันใช้ในทริกเกอร์ mysql เช่นนั้น

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
คุณช่วยเพิ่มข้อมูลวิธีแก้ปัญหาของคุณได้ไหม
TheTanic

-26
    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
    }   
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.