SQLinfo.ru - Все о MySQL

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 30.04.2008 09:50:39

Aco
Участник
Откуда: СПб
Зарегистрирован: 30.04.2008
Сообщений: 10

Не корректно работает процедура. В чем ошибка?

Доброго времени суток!
Есть иерархическая таблица, `parent_id` - `item_id` родителя:

Код:

CREATE TABLE `tree` (                                                                                     
          `item_id` int(11) unsigned NOT NULL auto_increment,                                                     
          `parent_id` int(11) default NULL,                                                                       
          `descr` varchar(64) default NULL,                                                                       
          `some_data` tinytext,                                                                                   
          PRIMARY KEY  (`item_id`)                                                                                
) ENGINE=MyISAM

Заполненая следующим образом

Код:

INSERT INTO `tree` (`item_id`,`parent_id`,`descr`,`some_data`) VALUES
(1,'0','root','The start'),
(2,'1','level 1','2'),
(3,'1','level 1','3'),
(4,'3','level 2','4'),
(5,'4','level 3','5'),
(6,'2','level 2','6'),
(7,'3','level 2','7'),
(8,'2','level 2','8'),
(9,'8','level 3','9'),
(10,'1','level 1','10'),
(11,'10','level 2','11'),
(12,'6','level 3','12'),
(13,'11','level 3','13'),
(14,'4','level 3','14'),
(15,'7','level 3','15'),
(16,'12','appendix','The end');

Есть следующая процедура. В неё передаётся item_id любого элемента и она выбирает клиенту все элементы от этого item_id до root.

Код:

DELIMITER $$
DROP PROCEDURE IF EXISTS `fromItemToRoot` $$
CREATE PROCEDURE `fromItemToRoot` (IN _item_id INT)
BEGIN
WHILE _item_id > 0 DO
  SELECT * FROM `tree` WHERE `item_id` = _item_id;
  SELECT parent_id INTO _item_id FROM `tree` WHERE `item_id` = _item_id;
  #SELECT _item_id;
END WHILE;
END $$
DELIMITER ;

Проблема в том что, передавая, например, _item_id = 16, WHILE делает только одну итерацию, хотя по окончанию первой итерации _item_id = 12. Почему так? Как заставить его делать все итерации?

Отредактированно Aco (30.04.2008 10:20:27)

Неактивен

 

#2 30.04.2008 10:49:18

Aco
Участник
Откуда: СПб
Зарегистрирован: 30.04.2008
Сообщений: 10

Re: Не корректно работает процедура. В чем ошибка?

А вообще возможно такие выборки составлять?

Неактивен

 

#3 30.04.2008 13:08:01

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Не корректно работает процедура. В чем ошибка?

Спасибо за подробное описание. Я скопировал Ваш код в консоль mysql и вызвал процедуру, она сделала 5 итераций как положено. Какая у Вас версия MySQL?

Код:

mysql> CALL fromItemToRoot(16);
+---------+-----------+----------+-----------+
| item_id | parent_id | descr    | some_data |
+---------+-----------+----------+-----------+
|      16 |        12 | appendix | The end   | 
+---------+-----------+----------+-----------+
1 row in set (0.00 sec)

+---------+-----------+---------+-----------+
| item_id | parent_id | descr   | some_data |
+---------+-----------+---------+-----------+
|      12 |         6 | level 3 | 12        | 
+---------+-----------+---------+-----------+
1 row in set (0.00 sec)

+---------+-----------+---------+-----------+
| item_id | parent_id | descr   | some_data |
+---------+-----------+---------+-----------+
|       6 |         2 | level 2 | 6         | 
+---------+-----------+---------+-----------+
1 row in set (0.00 sec)

+---------+-----------+---------+-----------+
| item_id | parent_id | descr   | some_data |
+---------+-----------+---------+-----------+
|       2 |         1 | level 1 | 2         | 
+---------+-----------+---------+-----------+
1 row in set (0.00 sec)

+---------+-----------+-------+-----------+
| item_id | parent_id | descr | some_data |
+---------+-----------+-------+-----------+
|       1 |         0 | root  | The start | 
+---------+-----------+-------+-----------+
1 row in set (0.01 sec)

Query OK, 0 rows affected (0.01 sec)

mysql> \s
--------------
mysql  Ver 14.12 Distrib 5.0.22, for redhat-linux-gnu (i686) using readline 5.0

Connection id:          2
Current database:       test
Current user:           root@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server version:         5.0.22-log
Protocol version:       10
Connection:             Localhost via UNIX socket
Server characterset:    cp1251
Db     characterset:    latin1
Client characterset:    cp1251
Conn.  characterset:    cp1251
UNIX socket:            /var/lib/mysql/mysql.sock
Uptime:                 46 min 9 sec

Threads: 1  Questions: 108  Slow queries: 0  Opens: 66  Flush tables: 1  Open tables: 64  Queries per second avg: 0.039
--------------

Неактивен

 

#4 30.04.2008 14:23:44

Aco
Участник
Откуда: СПб
Зарегистрирован: 30.04.2008
Сообщений: 10

Re: Не корректно работает процедура. В чем ошибка?

MySQL версии 5.0.43. Хмм.. в консоле именно так всё и обстоит именно так.
Но дело в том, что я использую GUI оболочку MySQLyog. Проблема, видимо, с её стороны. Она может обработать зараз только один SELECT, в то время как процедура за раз вызывает несколько "разных" SELECT'ов. Интересно, а как будет реагировать PHP? (сейчас нет возможности проверить)

Отредактированно Aco (30.04.2008 14:24:04)

Неактивен

 

#5 30.04.2008 14:42:20

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Не корректно работает процедура. В чем ошибка?

Процедура выполняется на уровне MySQL-сервера. Оболочка передается один запрос CALL имя_процедуры(). MySQL в Вашем случае выдает несколько ответов, видимо оболочка обрабатывает только первый.

Рекомендую модифицировать процедуру. Добавить в начало CREATE TEMPORARY TABLE t LIKE tree;
Далее вместо SELECT, INSERT INTO t SELECT * FROM tree;

В конце SELECT * FROM t; DROP TEMPORARY TABLE t;

Это упрощенная схема, Вам возможно потребуется выводить записи в том же порядке, в котором они выбирались, тогда в цикле следует объявить переменную counter, вставлять ее в таблицу t и в конце сортировать по ней

Неактивен

 

#6 30.04.2008 14:56:15

Aco
Участник
Откуда: СПб
Зарегистрирован: 30.04.2008
Сообщений: 10

Re: Не корректно работает процедура. В чем ошибка?

PHP не может обработать запрос "CALL fromItemToRoot(16)". Возвращает ошибку "PROCEDURE prc.fromItemToRoot can't return a result set in the given context". Переделал процедуру через prepared statements:

Код:

DELIMITER $$
DROP PROCEDURE IF EXISTS `fromItemToRoot` $$
CREATE PROCEDURE `fromItemToRoot` (IN _item_id INT)
BEGIN
SET @items := _item_id;
WHILE _item_id > 0 DO
  SELECT parent_id INTO _item_id FROM `tree` WHERE `item_id` = _item_id;
  SET @items := CONCAT(@items,",",_item_id);
END WHILE;
SET @s := CONCAT("SELECT * FROM `tree` WHERE item_id IN (",@items,") ORDER BY item_id DESC");
PREPARE stp FROM @s;
EXECUTE stp;
END $$
DELIMITER ;

Однако и это не помогло. Ошибка всё та же.
Попробую через временную таблицу, как Вы советуете.
Рас уж такое дело, есть вопрос по временным таблицам: если одновременно два клиента запустят процедуру, они конфликтовать не будут? Или каждый видит только свою временную таблицу?

Отредактированно Aco (30.04.2008 14:57:04)

Неактивен

 

#7 30.04.2008 14:59:53

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Не корректно работает процедура. В чем ошибка?

Видимо PHP не умеет обрабатывать множественные resultset-ы. Временная таблица существует только в рамках подключения, после завершения сессии, временная таблица удаляется автоматически, так что конфликта не будет - каждый пользователь будет видеть свои временные таблицы (в связи с этим есть некоторые особенности репликации)

Неактивен

 

#8 30.04.2008 15:34:19

Aco
Участник
Откуда: СПб
Зарегистрирован: 30.04.2008
Сообщений: 10

Re: Не корректно работает процедура. В чем ошибка?

Переделал на временные таблицы:

Код:

DELIMITER $$
DROP PROCEDURE IF EXISTS `fromItemToRoot` $$
CREATE PROCEDURE `fromItemToRoot` (IN _item_id INT)
BEGIN
DROP TABLE IF EXISTS temp;
CREATE TEMPORARY TABLE temp
(
    `item_id` int(11) unsigned NOT NULL,                                                     
    `parent_id` int(11) default NULL,                                                                       
    `descr` varchar(64) default NULL,                                                                       
    `some_data` tinytext
) CHARSET=cp1251;
WHILE _item_id > 0 DO
  INSERT INTO temp SELECT * FROM `tree` WHERE `item_id` = _item_id;
  SELECT parent_id INTO _item_id FROM `tree` WHERE `item_id` = _item_id;
END WHILE;
SELECT * FROM temp ORDER BY item_id DESC;
END $$
DELIMITER ;

И всё равно ошибка!
MySQL всё равно сообщает, что не может передать данные. Видимо взаимодействие PHP - MySQL через процедуры не такое лёгкое дело Пойду курить мануалы по PHP...

Неактивен

 

#9 30.04.2008 15:55:59

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Не корректно работает процедура. В чем ошибка?

Стандартные php-функции mysql не позволяют процедуре вернуть значения. С процедурами корректо работают функции mysqli (доступные по умолчанию начиная с PHP 5). Вот пример кода, который работает у меня

<?php

$c = mysqli_connect('localhost','user','password','test') or die(mysql_error());
mysqli_select_db($c,'test') or die(mysql_error());
mysqli_query($c,"SET NAMES UTF8") or die(mysql_error());
$result = mysqli_query($c,"call fromItemToRoot(16);") or die(mysql_error());
while($aRow = mysqli_fetch_assoc($result)) {
  print_r($aRow);
}
?>

Неактивен

 

#10 30.04.2008 16:18:07

Aco
Участник
Откуда: СПб
Зарегистрирован: 30.04.2008
Сообщений: 10

Re: Не корректно работает процедура. В чем ошибка?

Спасибо. Через mysqli всё работает. mysqli_connect и mysql_connect конфликтуют между собой, если не указывать коннекты в функциях?

Неактивен

 

#11 30.04.2008 16:27:44

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Не корректно работает процедура. В чем ошибка?

В mysqli нельзя не указать соединение в функциях. mysql_ юзает соединение mysqli. По умолчанию mysql_, кажется, юзает последнее установленное соединение. Если Вы установите только соединение mysqli, то все должно работать правильно.

Неактивен

 

#12 30.04.2008 17:18:00

Aco
Участник
Откуда: СПб
Зарегистрирован: 30.04.2008
Сообщений: 10

Re: Не корректно работает процедура. В чем ошибка?

Кстати, вариант процедуры prepared statements работает быстрее чем через временные таблицы, эдак 2 раза. А помощь большое спасибо!

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson