Как я могу просмотреть все строки таблицы? (MySQL)

89

У меня есть таблица 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 с помощью цикла.

(это просто упрощенный пример, конечно, вы бы не использовали для этого цикл.)}

Рафаэль
источник
Привет, Рафил, для меня это ошибка выдачи: Мой SQL> для (каждая строка в wp_mobiune_ecommune_user как x) {вставить в значения wp_mobiune_ecommune_user (пол_новый) (x [пол])}; RROR 1064 (42000): у вас есть ошибка в синтаксисе SQL; проверьте руководство, которое соответствует вашей версии сервера MySQL, чтобы найти правильный синтаксис для использования рядом с 'for (каждая строка в wp_mobiune_ecommune_user как x) {вставить в wp_mobiune_ecommune' в строке 1
dinesh kandpal

Ответы:

135

Поскольку предложение цикла подразумевает запрос решения типа процедуры. Вот мой.

Любой запрос, который работает с любой отдельной записью, взятой из таблицы, можно обернуть в процедуру, чтобы он проходил через каждую строку таблицы следующим образом:

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, которую вы используете.

В простом случае, когда поле вашего идентификатора увеличивается и начинается с 1, строка в примере может выглядеть так:

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

Замена строки «ВЫБРАТЬ СЧЕТЧИК» на

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

Не забудьте объявить переменные, которые вы будете использовать, того же типа, что и переменные из запрашиваемых таблиц.

Я советую использовать запросы на основе наборов, когда это возможно, и использовать только простые циклы или курсоры, если это необходимо.

Мистер пурпурный
источник
2
INSERT INTO table_B (ID, VAL) VALUES (ID, VAL) FROM table_A LIMIT i, 1; дает синтаксическую ошибку.
Джонатан
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) FROM table_A WHERE ID = i;
Ambar
Это займет время жизни, поэтому попробуйте увеличить размер пакета до 1000, изменив предел и приращение: LIMIT i,1000;и set i = i + 1000;
Алан Дип
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 для примера. хотя я сомневаюсь, что это применимо в моем случае, потому что обновление впоследствии связано со вставкой и особенно с тем, какие выбранные строки вызывают конкретную вставку. Вот почему я предложил цикл, чтобы я мог выполнять выбор / вставку / обновление для каждой строки индивидуально.
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 Можете ли вы помочь мне в моем вопросе
Нурав
4

КУРСОРЫ являются здесь вариантом, но обычно не одобряются, так как они часто не наилучшим образом используют механизм запросов. Подумайте о том, чтобы изучить «Запросы на основе SET», чтобы увидеть, сможете ли вы достичь того, чего хотите, без использования КУРСОРА.

Рон Уэстон
источник
Я не могу найти много информации о заданных запросах. но я предполагаю, что вы имеете в виду выбор в виде утверждения. проблема в том, что мне также нужно выполнить обновление.
Raffael
0

Пример мистера 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
-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
источник