Як я можу прокрутити всі рядки таблиці? (MySQL)


89

У мене є таблиця 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 за допомогою циклу.

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


Привіт, Рафіл, для мене його помилка видачі: My SQL> for (кожен рядок у wp_mobiune_ecommune_user як x) {вставити у значення wp_mobiune_ecommune_user (gender_new) (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;

Замінивши рядок "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;
;;

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

Моя порада полягає в тому, щоб, коли це можливо, іти із заданими запитами і використовувати лише прості цикли або курсори, якщо це потрібно.


2
ВСТАВИТИ В table_B (ID, VAL) ЦІННОСТІ (ID, VAL) ВІД table_A ОБМЕЖЕННЯ i, 1; дає синтаксичну помилку.
Джонатан

1
Здається, бракує "ЗАЯВИТИ, що зроблено ІНТЕГРАФНО НЕВИЩИМО;" після оголошення 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;
Амбар

Це займе все життя, тому спробуйте збільшити розмір партії до 1000, змінивши обмеження та збільшення: 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 для прикладу. хоча я сумніваюся, що це застосовне у моєму б / к оновленні згодом оновлення пов’язане із вставкою та особливо тим, які вибрані рядки спричиняють яку саме вставку. Ось чому я запропонував цикл, щоб я міг виконувати вибір / вставку / оновлення для кожного рядка окремо.
Раффаель,

2
@ Raffael1984: відредагуйте своє запитання, щоб додати умови для "конкретних рядків, що спричиняють конкретні вставки", і ми можемо з цим допомогти. Ви дійсно не хочете їхати курсором / петлею - це вкрай неефективно.
Радж Море

ну ну ... ти знаєш, я б із задоволенням пішов шляхом встановлених запитів! але ефективність тут не є мотивацією, оскільки моє питання скоріше представляє академічний інтерес. стіл досить маленький, я міг зробити це вручну. Я б дуже вдячний циклічній версії. Я міг би опублікувати цю проблему ще раз, надавши більше деталей, щоб дозволити комусь допомогти мені з набором стилів.
Раффаель,

згоден з @RajMore, курсор / цикл неефективний, нижче 2 посилання про продуктивність курсора для посилань: 1. rpbouman.blogspot.tw/2006/09/refactoring-mysql-cursors.html 2. stackoverflow.com/questions/11549567/ ...
Брауні Лін

@RajMore Чи можете ви допомогти мені у моєму запитанні
Нурав,

4

КУРСОРИ тут є варіантом, але, як правило, з невдоволенням, оскільки вони часто не найкраще використовують механізм запитів. Подумайте про те, щоб дослідити "Запити на основі SET", щоб побачити, чи зможете ви досягти того, що ви хочете зробити, не використовуючи КУРСОР.


Я не можу знайти багато інформації про запити на основі наборів. але, мабуть, ви маєте на увазі обрати своєрідне твердження. проблема в тому, що мені також потрібно виконати оновлення.
Раффаель,

0

Приклад містера Пурпура, який я використовував у 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

-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
    }   
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.