Jak mogę przejrzeć wszystkie wiersze tabeli? (MySQL)

89

Mam tabelę A i jest jeden identyfikator klucza podstawowego.

Teraz chcę przejść przez wszystkie rzędy schematu A.

Znalazłem coś w rodzaju „dla każdego rekordu w A”, ale wydaje się, że nie jest to sposób, w jaki robisz to w MySQL.

Chodzi o każdy wiersz, który chcę wziąć pole i przekształcić je, wstawić do innej tabeli, a następnie zaktualizować niektóre pola wiersza. Mogę umieścić wybraną część i wstawkę w jednym oświadczeniu, ale nie wiem również, jak uzyskać tam aktualizację. Więc chcę zapętlić. I dla praktyki nie chcę używać niczego innego niż MySQL.

edytować

Byłbym wdzięczny za przykład.

I rozwiązanie, którego nie trzeba poddawać zabiegowi.

edytuj 2

dobrze, pomyśl o tym scenariuszu:

Tabela A i B, każda z polami ID i VAL.

Oto pseudokod tego, co chcę zrobić:

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

w zasadzie kopiowanie zawartości A do B za pomocą pętli.

(to jest tylko uproszczony przykład, oczywiście nie użyłbyś do tego pętli).}

Raffael
źródło
Cześć Rafeel dla mnie, podając błąd jako: Mój SQL> dla (każdy wiersz w wp_mobiune_ecommune_user jako x) {wstaw do wp_mobiune_ecommune_user (gender_new) wartości (x [płeć])}; RROR 1064 (42000): Wystąpił błąd w składni SQL; sprawdź podręcznik, który odpowiada Twojej wersji serwera MySQL, aby znaleźć właściwą składnię do użycia w pobliżu 'for (każdy wiersz w wp_mobiune_ecommune_user as x) {wstaw do wp_mobiune_ecommune' w linii 1
dinesh kandpal

Odpowiedzi:

135

Ponieważ sugestia pętli implikuje żądanie rozwiązania typu procedury. Tu jest moje.

Każde zapytanie, które działa na każdym pojedynczym rekordzie pobranym z tabeli, można opakować w procedurę, aby przeszło przez każdy wiersz tabeli w następujący sposób:

DROP PROCEDURE IF EXISTS ROWPERROW;
DELIMITER ;;

Oto procedura jak na twoim przykładzie (tabela_A i tabela_B użyte dla przejrzystości)

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

Następnie nie zapomnij zresetować ogranicznika

DELIMITER ;

I uruchom nową procedurę

CALL ROWPERROW();

Możesz zrobić, co chcesz, w wierszu „INSERT INTO”, który po prostu skopiowałem z Twojego przykładowego żądania.

Zauważ UWAŻNIE, że użyty tutaj wiersz "INSERT INTO" odzwierciedla wiersz w pytaniu. Zgodnie z komentarzami do tej odpowiedzi, musisz upewnić się, że twoje zapytanie jest poprawne składniowo dla każdej używanej wersji SQL.

W prostym przypadku, gdy pole ID jest zwiększane i zaczyna się od 1, wiersz w przykładzie mógłby wyglądać następująco:

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

Zastąpienie wiersza „WYBIERZ LICZNIK” znakiem

SET n=10;

Pozwoli Ci przetestować zapytanie tylko na pierwszych 10 rekordach w tabeli_A.

Ostatnia rzecz. Ten proces jest również bardzo łatwy do zagnieżdżenia w różnych tabelach i był to jedyny sposób, w jaki mogłem przeprowadzić proces na jednej tabeli, który dynamicznie wstawiał różne liczby rekordów do nowej tabeli z każdego wiersza tabeli nadrzędnej.

Jeśli chcesz, aby działał szybciej, spróbuj ustawić go na podstawie zestawu, jeśli nie, to jest w porządku. Możesz również przepisać powyższe w formie kursora, ale może to nie poprawić wydajności. na przykład:

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

Pamiętaj, aby zadeklarować zmienne, których będziesz używać, jako tego samego typu, co zmienne z odpytywanych tabel.

Radzę korzystać z zapytań opartych na ustawieniach, kiedy tylko możesz, i używać tylko prostych pętli lub kursorów, jeśli musisz.

Panie Purple
źródło
2
INSERT INTO table_B (ID, VAL) VALUES (ID, VAL) FROM table_A LIMIT i, 1; daje błąd składniowy.
Jonathan,
1
Wydaje się, że brakuje „DECLARE done INT DEFAULT FALSE”; po deklaracji kursora_i
ErikL
1
Gdzie atrybut done jest ustawiony na true, powinienem go umieścić, czy jest to słowo zastrzeżone, które jest używane automatycznie przez kursor mysql?
IgniteCoders
1
Wywołanie INSERT INTO zgłasza błąd składniowy od SQL 5.5, poprawne wywołanie to INSERT INTO table_B (ID, VAL) SELECT (ID, VAL) FROM table_A WHERE ID = i;
Ambar
Zajmie to całe życie, więc spróbuj zwiększyć rozmiar partii do 1000, zmieniając limit i przyrost: LIMIT i,1000;i set i = i + 1000;
Alan Deep
16

Naprawdę powinieneś używać rozwiązania opartego na zestawie obejmującym dwa zapytania (podstawowa wstawka):

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

UPDATE TableA SET column1 = column2 * column3

A dla Twojej transformacji:

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

UPDATE TableA SET column1 = column2 * column3

Teraz, jeśli twoja transformacja jest bardziej skomplikowana i obejmuje wiele tabel, opublikuj kolejne pytanie ze szczegółami.

Raj Więcej
źródło
+1 na przykład. chociaż wątpię, że ma to zastosowanie w mojej sytuacji b / c, późniejsza aktualizacja jest związana z wkładką, a zwłaszcza z tym, co wybrane wiersze powoduje, która konkretna wkładka. Dlatego zasugerowałem pętlę, aby móc indywidualnie wybierać / wstawiać / aktualizować dla każdego wiersza.
Raffael
2
@ Raffael1984: edytuj swoje pytanie, dodając warunki dla „określonych wierszy powodujących określone wstawienia” i możemy w tym pomóc. Naprawdę nie chcesz iść po trasie kursora / pętli - jest to wyjątkowo nieefektywne.
Raj More
no cóż ... wiesz, byłbym bardziej niż szczęśliwy, mogąc przejść do zapytania opartego na zestawie! ale skuteczność nie jest tutaj motywacją, ponieważ moje pytanie jest bardziej akademickie. stół jest na tyle mały, że mogłem to zrobić ręcznie. Naprawdę byłbym wdzięczny za wersję loop. Mógłbym ponownie opublikować ten problem, podając więcej szczegółów, aby ktoś mógł mi pomóc w stylu opartym na zestawie.
Raffael
zgadzam się z @RajMore, kursor / pętla jest nieefektywna, poniżej znajdują się 2 linki dotyczące wydajności kursora dla odniesień: 1. rpbouman.blogspot.tw/2006/09/refactoring-mysql-cursors.html 2. stackoverflow.com/questions/11549567/ …
Browny Lin
@RajMore Czy możesz mi pomóc w moim pytaniu
Nurav
4

KURSORY są tutaj opcją, ale generalnie są źle widziane, ponieważ często nie wykorzystują one najlepiej silnika zapytań. Rozważ zbadanie `` Zapytań opartych na SET '', aby zobaczyć, czy możesz osiągnąć to, co chcesz, bez użycia CURSORA.

Ron Weston
źródło
Nie mogę znaleźć wielu informacji na temat zapytań opartych na zestawach. ale myślę, że masz na myśli rodzaj oświadczenia. problem polega na tym, że potrzebuję też wykonać aktualizację.
Raffael
0

Przykład Mr Purple, którego użyłem w wyzwalaczu mysql w ten sposób,

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
Erkan RUA
źródło
2
Czy możesz dodać informacje, jak działa Twoje rozwiązanie?
TheTanic
-24
    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
    }   
utee
źródło