Bir tablonun tüm satırlarında nasıl döngü yapabilirim? (MySQL)


90

Bir A tablosuna sahibim ve bir birincil anahtar kimliği var.

Şimdi A'daki tüm satırlardan geçmek istiyorum.

'A'daki her kayıt için' gibi bir şey buldum, ancak bu MySQL'de yaptığınız gibi görünmüyor.

Mesele, her satır için bir alan alıp dönüştürmek, başka bir tabloya eklemek ve ardından satırın bazı alanlarını güncellemek istiyorum. Select kısmını ve eki tek bir ifadeye koyabilirim, ancak güncellemeyi oraya nasıl getireceğimi de bilmiyorum. Bu yüzden döngü yapmak istiyorum. Ve pratik için MySQL'den başka bir şey kullanmak istemiyorum.

Düzenle

Bir örnek isterim.

Ve bir prosedüre sokulması gerekmeyen bir çözüm.

düzenleme 2

tamam şu senaryoyu düşün:

Tablo A ve B, her biri ID ve VAL alanlarına sahiptir.

Şimdi bu, yapmak istediğim şeyin sözde kodudur:

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

temelde A'nın içeriğini bir döngü kullanarak B'ye kopyalamak.

(bu sadece basitleştirilmiş bir örnektir, elbette bunun için bir döngü kullanmazsınız.)}


Merhaba Rafeel benim için şu hatayı veriyor: My SQL> for (wp_mobiune_ecommune_user'daki her satır x olarak) {wp_mobiune_ecommune_user (cinsiyet_new) değerlerine (x [cinsiyet])}; RROR 1064 (42000): SQL sözdiziminizde bir hata var; 'for' yakınında (wp_mobiune_ecommune_user'deki her satır x olarak) {wp_mobiune_ecommune'a ekleyin '1. satır
dinesh kandpal

Yanıtlar:


137

Bir döngünün önerisi, bir prosedür tipi çözüm talebini ifade ettiğinden. İşte benim.

Bir tablodan alınan herhangi bir tek kayıt üzerinde çalışan herhangi bir sorgu, bir tablonun her satırında aşağıdaki gibi çalışmasını sağlamak için bir prosedüre sarılabilir:

DROP PROCEDURE IF EXISTS ROWPERROW;
DELIMITER ;;

O zaman işte örneğinize göre prosedür (netlik için kullanılan tablo_A ve tablo_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;
;;

O zaman sınırlayıcıyı sıfırlamayı unutma

DELIMITER ;

Ve yeni prosedürü çalıştırın

CALL ROWPERROW();

Örnek talebinizden kopyaladığım "INSERT INTO" satırında istediğinizi yapabilirsiniz.

DİKKATLİCE, burada kullanılan "INSERT INTO" satırının sorudaki satırı yansıttığına dikkat edin. Bu cevaba verilen yorumlara göre, çalıştırdığınız SQL sürümü için sorgunuzun sözdizimsel olarak doğru olduğundan emin olmanız gerekir.

Kimlik alanınızın artırıldığı ve 1'den başladığı basit durumda, örnekteki satır şöyle olabilir:

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

"SEÇ SAYISI" satırının yerine

SET n=10;

Sorgunuzu yalnızca table_A'daki ilk 10 kayıtta test etmenize izin verir.

Son bir şey. Bu işlemin farklı tablolara yerleştirilmesi de çok kolaydır ve üst tablonun her satırından yeni bir tabloya dinamik olarak farklı sayıda kayıt ekleyen bir tablo üzerinde bir işlem gerçekleştirebilmemin tek yoluydu.

Daha hızlı çalışmasına ihtiyacınız varsa, o zaman bunu temel almaya çalışın, eğer değilse, o zaman sorun değil. Yukarıdakileri imleç biçiminde de yeniden yazabilirsiniz, ancak performansı iyileştirmeyebilir. Örneğin:

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

Kullanacağınız değişkenleri sorgulanan tablolardakilerle aynı tipte tanımlamayı unutmayın.

Benim tavsiyem, yapabildiğiniz zaman set tabanlı sorgularla gitmek ve gerekirse yalnızca basit döngüleri veya imleçleri kullanmaktır.


2
INSERT INTO table_B (ID, VAL) VALUES (ID, VAL) table_A LIMIT i, 1; sözdizimi hatası verir.
Jonathan

1
"BİLDİRİM yapıldı INT VARSAYILAN YANLIŞ;" eksik görünüyor. cursor_i bildiriminden sonra
ErikL

1
Done özelliği nerede true olarak ayarlandı, onu yerleştirmeliyim mi yoksa mysql imleci tarafından otomatik olarak kullanılan ayrılmış bir kelime mi?
IgniteCoders

1
INSERT INTO çağrısı SQL 5.5'ten itibaren bir sözdizimi hatası atıyor, doğru çağrı INSERT INTO table_B (ID, VAL) SELECT (ID, VAL) FROM table_A WHERE ID = i;
Ambar

Bu bir ömür alacaktır, bu nedenle sınırı ve artışı değiştirerek parti boyutunu 1000'e çıkarmaya çalışın: LIMIT i,1000;ve set i = i + 1000;
Alan Deep

16

Gerçekten iki sorgu içeren küme tabanlı bir çözüm kullanmalısınız (temel ekleme):

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

UPDATE TableA SET column1 = column2 * column3

Ve dönüşümünüz için:

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

UPDATE TableA SET column1 = column2 * column3

Şimdi, dönüşümünüz bundan daha karmaşıksa ve birden çok tablo içeriyorsa, ayrıntılarla birlikte başka bir soru gönderin.


Örnek için +1. bunun benim sitemde uygulanabilir olduğundan şüphem olsa da, sonraki güncelleme ek ile ilgilidir ve özellikle seçilen satırların hangi özel eke neden olduğu. Bu yüzden bir döngü önerdim, böylece her satır için ayrı ayrı seçme / ekleme / güncelleme yapabilirim.
Raffael

2
@ Raffael1984: "belirli eklemelere neden olan belirli satırlar" için koşullar eklemek üzere sorunuzu düzenleyin ve biz bu konuda yardımcı olabiliriz. Gerçekten imleç / döngü rotasına gitmek istemezsiniz - bu son derece verimsizdir.
Raj More

oh peki ... set tabanlı sorgu yoluna gitmekten çok mutlu olacağımı biliyorsun! ama benim sorum akademik ilgi daha çok olduğu için burada verimlilik motivasyon değildir. masa el ile yapabileceğim kadar küçük. Döngü versiyonunu gerçekten takdir ediyorum. Birinin bana set tabanlı stilde yardımcı olmasına izin vermek için daha fazla ayrıntı sağlayarak bu sorunu tekrar yayınlayabilirim.
Raffael

@RajMore ile aynı fikirdeyseniz, imleç / döngü verimsizdir, aşağıda referanslar için imleç performansıyla ilgili 2 bağlantı verilmiştir: 1. rpbouman.blogspot.tw/2006/09/refactoring-mysql-cursors.html 2. stackoverflow.com/questions/11549567/ …
Browny Lin

@RajMore benim de bana yardım edebilir sorusuna
Nurav

4

İMLEÇLER burada bir seçenektir, ancak genellikle sorgu motorunu en iyi şekilde kullanmadıkları için hoş karşılanmazlar. Bir CURSOR kullanmadan yapmak istediğiniz şeyi elde edip edemeyeceğinizi görmek için 'SET Tabanlı Sorgular'ı araştırmayı düşünün.


Küme tabanlı sorgular hakkında fazla bilgi bulamıyorum. ama sanırım bir tür ifadeyi seçmek demek istiyorsun. Sorun şu ki, bir güncellemenin yürütülmesine de ihtiyacım var.
Raffael

0

Mr Purple'ın mysql tetikleyicisinde böyle kullandığım örneği,

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
Çözümünüzün nasıl çalıştığı hakkında biraz bilgi ekleyebilir misiniz?
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
    }   
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.