Модераторы: LSD, AntonSaburov

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Проблема с вызовом внутренней процедуры в MySQL 
:(
    Опции темы
Sun
Дата 2.10.2006, 21:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Account removed
***


Профиль
Группа: Экс. модератор
Сообщений: 1611
Регистрация: 14.8.2002

Репутация: 2
Всего: 48



boroda, привел текст процедуры в котором был следующий вызов
Код

insert into testTable(name, idCoutry) values(aName, country);

В приведенной мной цитате вы советуете заменить вызовы хранимых процедур прямым вызовом SQL запроса. Следуя из контекста темы, запрос должен вызываться из Java кода, то есть извне. Далее следует фраза "Мало того, что это будет переносимое решение, но оно так же обычно оказывается лучше по производительности" из чего я делаю логический вывод что вы считаете хранимые процедуры менее эффективными чем вызовы внешних SQL запросов.

Дальше по моей просьбе вы приводите доказательство своей мысли. Я ставлю его под сомнение, так как мне непонятно почему "Заменяя подзапросы вызовами хранимых процедур вы лишите оптимизатор возможности выбора стратегии.". Дальше мы приходим к выводу что вызов SQL запроса происходит практически одинаково как из хранимой процедуры, так и из внешнего приложения. На основе этого я делаю утверждение, что вы признали, что внешние SQL запросы не могут быть лучшими по производительности, чем хранимые процедуры  smile 


--------------------
Account removed
PM MAIL   Вверх
3x3
Дата 2.10.2006, 22:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 261
Регистрация: 17.9.2006

Репутация: 5
Всего: 8



Цитата(Sun @ 2.10.2006,  21:33)
boroda, привел текст процедуры в котором был следующий вызов
Код

insert into testTable(name, idCoutry) values(aName, country);

В приведенной мной цитате вы советуете заменить вызовы хранимых процедур прямым вызовом SQL запроса. 

Да, естественно. У процедур, конечно, есть своя роль в жини, но она не заключается в подмене частей обычных SQL-выражений. 


Цитата
Следуя из контекста темы, запрос должен вызываться из Java кода, то есть извне. 


Это может вытечь разве из соображений переносимости, вряд ли из контекста.

Цитата
Далее следует фраза "Мало того, что это будет переносимое решение, но оно так же обычно оказывается лучше по производительности" из чего я делаю логический вывод что вы считаете хранимые процедуры менее эффективными чем вызовы внешних SQL запросов.
Дальше по моей просьбе вы приводите доказательство своей мысли. Я ставлю его под сомнение, так как мне непонятно почему "Заменяя подзапросы вызовами хранимых процедур вы лишите оптимизатор возможности выбора стратегии.". Дальше мы приходим к выводу что вызов SQL запроса происходит практически одинаково как из хранимой процедуры, так и из внешнего приложения. На основе этого я делаю утверждение, что вы признали, что внешние SQL запросы не могут быть лучшими по производительности, чем хранимые процедуры  smile


Я начинаю вас понимать smile 
Вы немножко забываете о том, что кроме отработки вызова, серверу ещё нужно сделать некоторую полезную работу.
Я не думаю, что вы мне приведёте пример базы данных, в которой времени на отработку собственно вызов тратится больше, чем на работу - такая система просто не смогла бы работать постоянно с полной нагрузкой, периодически уходя в отказ в обслуживании при переполнении очереди вызовов, да в случаях когда время обращения к серверу критично - используются более другие решения: прямая загрузка данных в базу в обход ограничений целостности, кеширование огромных объемов данных на сервере приложений и т.п. 

PS
Кстати, с точки зрения сопровождения БД 24х7 будет любопытно взглянуть как вы в работающей базе будете при необходимости апдейтить две функции, из которых одна вызывает другую - ну мало ли что там поменялось ;)


PPS
А у вас аватар не просто так выбран? По SunOS можно обратиться с вопросиком?
 




Это сообщение отредактировал(а) 3x3 - 2.10.2006, 22:40


--------------------
Зачем платить больше,
когда можно заплатить дважды?
PM   Вверх
3x3
  Дата 3.10.2006, 03:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 261
Регистрация: 17.9.2006

Репутация: 5
Всего: 8



Вобщем, проверил на практике. Начерно, конечно, но меня устраивает smile 
А кому не нравятся мои результаты - могут опровергать своими, код приведен. 

База:
Код

CREATE TABLE `xcountry` (`c_id` int(11) NOT NULL,`c_name` varchar(200) NOT NULL,PRIMARY KEY  (`c_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `xtest` (`t_name` varchar(200) NOT NULL,`c_id` int(11) default NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE FUNCTION `xget_country_id`(aName varchar(200)) RETURNS int(11)                                  
  READS SQL DATA DETERMINISTIC                               
BEGIN
  DECLARE aRes Integer;
  SELECT c_id INTO aRes FROM xcountry where c_name=aName LIMIT 1;  
  RETURN aRes;
END

CREATE PROCEDURE `xinsert_test`(IN val varchar(200), IN country varchar(200))
BEGIN
  declare cid integer default xget_country_id(country);
  insert into xtest values(val,cid);                                                                                      
END


CREATE PROCEDURE `xinsert_test2`(IN val varchar(200), IN country varchar(200))
BEGIN
  insert into xtest select val, c_id from xcountry where c_name=country;
END


Вызывающий код:
Код


        String[] queries= {
                "insert into xtest select ?, c_id from xcountry where c_name=?" 
                ,"insert into xtest values(?, xget_country_id(?))" 
                ,"call xinsert_test(?,?)"
                ,"call xinsert_test2(?,?)"
                };
        String[] countries= {
                "Russia", 
                "Switzerland"};
        
        int NumPasses = 4;
        int NumRows=100000;
        
        
        System.out.printf("Test started for %d rows per pass\n",NumRows);
        
        try {
          Class.forName("com.mysql.jdbc.Driver").newInstance();
          Connection conn = DriverManager.getConnection(ConnectString);
          conn.setAutoCommit(false);
          for(int pass=0; pass<NumPasses; pass++){
              for(int i=0; i<queries.length; i++){
                  String s=queries[i];
                  long passStarted = System.currentTimeMillis();

                  PreparedStatement stmt = conn.prepareStatement(s);
                  for(int rowN=0; rowN<NumRows; rowN++){
                    stmt.setString(1, String.valueOf(pass)+":"+String.valueOf(i)+":"+String.valueOf(rowN));
                    stmt.setString(2, countries[rowN&1]);
                    stmt.execute();
                    //stmt.executeQuery();
                  }
                  if(!conn.getAutoCommit()) conn.commit(); 
                  long passFinished = System.currentTimeMillis();
                  System.out.printf("  Pass %d query %d passed for %d ms\n",pass,i,(passFinished-passStarted));
              }
          }
        } catch (SQLException ex) {
            // handle any errors
            System.out.println("SQLException: " + ex.getMessage());
            System.out.println("SQLState: " + ex.getSQLState());
            System.out.println("VendorError: " + ex.getErrorCode());
        } catch (Exception ex) {
          // handle any errors
          System.out.println("Exception: " + ex.getMessage());
        }        
        System.out.printf("Test finished\n");



Как видим, четыре разных запроса по-разному вставляют строку:
0: insert into xtest select ?, c_id from xcountry where c_name=? 
  - простой запрос, который я рекомендовал
1: insert into xtest values(?, xget_country_id(?))
  - запрос извлекающий ИД страны вызовом функции
2: call xinsert_test(?,?)
  - процедура, идущая по пути автора треда и вызывающая функцию для получения ИДа
3: call xinsert_test2(?,?)
  - Такой же запрос, как и в случае 0, только зачем-то упакованный в процедуру

Результат теста:

При включенном автокоммите:

Test started for 1000 rows per pass
  Pass 0 query 0 passed for 26438 ms
  Pass 0 query 1 passed for 29344 ms
  Pass 0 query 2 passed for 30265 ms
  Pass 0 query 3 passed for 28360 ms
  Pass 1 query 0 passed for 27109 ms
  Pass 1 query 1 passed for 30047 ms
  Pass 1 query 2 passed for 30172 ms
  Pass 1 query 3 passed for 28015 ms
  Pass 2 query 0 passed for 28875 ms
  Pass 2 query 1 passed for 30125 ms
  Pass 2 query 2 passed for 29797 ms
  Pass 2 query 3 passed for 27172 ms
  Pass 3 query 0 passed for 30344 ms
  Pass 3 query 1 passed for 30141 ms
  Pass 3 query 2 passed for 29218 ms
  Pass 3 query 3 passed for 27860 ms
Test finished


Выключаю автокоммит

Test started for 1000 rows per pass
  Pass 0 query 0 passed for 281 ms
  Pass 0 query 1 passed for 187 ms
  Pass 0 query 2 passed for 188 ms
  Pass 0 query 3 passed for 172 ms
  Pass 1 query 0 passed for 140 ms
  Pass 1 query 1 passed for 172 ms
  Pass 1 query 2 passed for 188 ms
  Pass 1 query 3 passed for 156 ms
  Pass 2 query 0 passed for 156 ms
  Pass 2 query 1 passed for 157 ms
  Pass 2 query 2 passed for 203 ms
  Pass 2 query 3 passed for 156 ms
  Pass 3 query 0 passed for 156 ms
  Pass 3 query 1 passed for 406 ms
  Pass 3 query 2 passed for 235 ms
  Pass 3 query 3 passed for 156 ms
Test finished

Быстровато, увеличил количество инсертов

Test started for 100000 rows per pass
  Pass 0 query 0 passed for 12046 ms
  Pass 0 query 1 passed for 12563 ms
  Pass 0 query 2 passed for 13812 ms
  Pass 0 query 3 passed for 12703 ms
  Pass 1 query 0 passed for 11953 ms
  Pass 1 query 1 passed for 12907 ms
  Pass 1 query 2 passed for 16171 ms
  Pass 1 query 3 passed for 12891 ms
  Pass 2 query 0 passed for 12125 ms
  Pass 2 query 1 passed for 12891 ms
  Pass 2 query 2 passed for 16093 ms
  Pass 2 query 3 passed for 13047 ms
  Pass 3 query 0 passed for 11657 ms
  Pass 3 query 1 passed for 13203 ms
  Pass 3 query 2 passed for 15968 ms
  Pass 3 query 3 passed for 13032 ms
Test finished

Добавил DETERMINISTIC в определение функции

Test started for 100000 rows per pass
  Pass 0 query 0 passed for 11875 ms
  Pass 0 query 1 passed for 12594 ms
  Pass 0 query 2 passed for 13812 ms
  Pass 0 query 3 passed for 12922 ms
  Pass 1 query 0 passed for 11766 ms
  Pass 1 query 1 passed for 12906 ms
  Pass 1 query 2 passed for 14406 ms
  Pass 1 query 3 passed for 12781 ms
  Pass 2 query 0 passed for 11828 ms
  Pass 2 query 1 passed for 13032 ms
  Pass 2 query 2 passed for 16297 ms
  Pass 2 query 3 passed for 12421 ms
  Pass 3 query 0 passed for 12141 ms
  Pass 3 query 1 passed for 13156 ms
  Pass 3 query 2 passed for 14266 ms
  Pass 3 query 3 passed for 12844 ms
Test finished


ЧТД

PS

На всякий случай, статус базы:


SSL:                    Not in use
Using delimiter:        ;
Server version:         5.0.22-community-max-nt
Protocol version:       10
Connection:             localhost via TCP/IP
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8
TCP port:               3306
Uptime:                 23 hours 36 min 42 sec


Это сообщение отредактировал(а) 3x3 - 3.10.2006, 03:34


--------------------
Зачем платить больше,
когда можно заплатить дважды?
PM   Вверх
Sun
Дата 3.10.2006, 09:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Account removed
***


Профиль
Группа: Экс. модератор
Сообщений: 1611
Регистрация: 14.8.2002

Репутация: 2
Всего: 48



Цитата(3x3 @  2.10.2006,  19:38 Найти цитируемый пост)
А у вас аватар не просто так выбран? По SunOS можно обратиться с вопросиком?

Можно, правда я с ней не работал  smile  Работал только с FreeBSD и Linux. Аватар выбран не случайно - он соотвествует моему нику в этом форуме.

Что касается приведенного кода - я наконец-то понял к чему вы клоните. То-есть вы ничего не имеете против самих хранимых процедур, вы просто против их вызова в теле запроса и говорите что использовать вложенные запросы гораздо эффективнее. С этим спорить не буду, так как не проводил бэнчмарков по сабжу. Единственно добавлю что вложенных запросов также стоит избегать, а по возможности использовать JOIN.
Код

SELECT person.id, person.name FROM person
INNER JOIN country ON country.id=person.country_id
INNER JOIN continent ON country.continent_id=continent.id AND continent.name='Australia'
WHERE person.sex='M' ;



--------------------
Account removed
PM MAIL   Вверх
3x3
Дата 3.10.2006, 10:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 261
Регистрация: 17.9.2006

Репутация: 5
Всего: 8



Цитата(Sun @ 3.10.2006,  09:39)
Цитата(3x3 @  2.10.2006,  19:38 Найти цитируемый пост)
А у вас аватар не просто так выбран? По SunOS можно обратиться с вопросиком?

Можно, правда я с ней не работал  smile  Работал только с FreeBSD и Linux. Аватар выбран не случайно - он соотвествует моему нику в этом форуме.


А, ну ладно тогда, я в соответствующем разделе спросил..

Цитата

Что касается приведенного кода - я наконец-то понял к чему вы клоните. То-есть вы ничего не имеете против самих хранимых процедур, вы просто против их вызова в теле запроса и говорите что использовать вложенные запросы гораздо эффективнее. 


Поздравляю. Утро вечера мудренее.

Цитата

С этим спорить не буду, так как не проводил бэнчмарков по сабжу. Единственно добавлю что вложенных запросов также стоит избегать, а по возможности использовать JOIN.
Код

SELECT person.id, person.name FROM person
INNER JOIN country ON country.id=person.country_id
INNER JOIN continent ON country.continent_id=continent.id AND continent.name='Australia'
WHERE person.sex='M' ;



Не люблю этот синтаксис, и не знаю что он такого даёт в повседневном использовании. Мне больше нравится когда таблицы в запросе рядом через запятую перечислены smile 



--------------------
Зачем платить больше,
когда можно заплатить дважды?
PM   Вверх
Sun
Дата 3.10.2006, 11:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Account removed
***


Профиль
Группа: Экс. модератор
Сообщений: 1611
Регистрация: 14.8.2002

Репутация: 2
Всего: 48



Цитата(3x3 @  3.10.2006,  07:46 Найти цитируемый пост)
Не люблю этот синтаксис, и не знаю что он такого даёт в повседневном использовании. Мне больше нравится когда таблицы в запросе рядом через запятую перечислены smile 

Такой запрос отрабатывает более быстро чем если вы укажите через запятую или как вложенный запрос.


--------------------
Account removed
PM MAIL   Вверх
boroda
Дата 3.10.2006, 11:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 29
Регистрация: 2.12.2005
Где: Киев

Репутация: 1
Всего: 1



Спасибо всем. Но в данном случае я не могу отказаться от процедур. Опять вопрос у меня был не в том что лучше, а что хуже. Вопрос был почему так работает а этак не работает. Но всеравно спасибо, было действительно интересно
PM MAIL   Вверх
3x3
Дата 3.10.2006, 11:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 261
Регистрация: 17.9.2006

Репутация: 5
Всего: 8



Цитата(Sun @ 3.10.2006,  11:04)
Цитата(3x3 @  3.10.2006,  07:46 Найти цитируемый пост)
Не люблю этот синтаксис, и не знаю что он такого даёт в повседневном использовании. Мне больше нравится когда таблицы в запросе рядом через запятую перечислены smile 

Такой запрос отрабатывает более быстро чем если вы укажите через запятую или как вложенный запрос.

Вот только вопрос - почему? 
Я вижу только одну потенциальную причину - это то, что в запросе вы явно задаёте оптимизатору по какому набору данных осуществлять внешний цикл, а по каким - вложенные.  Т.е. в общем случае выигрыш должен быть на RDBMS, с оптимизатором, ничего не знающим про свои данные и, соответственно, неспособным полноценно оптимизировать план запроса?

Добавлено @ 12:04 
Цитата(boroda @ 3.10.2006,  11:53)
Спасибо всем. Но в данном случае я не могу отказаться от процедур. Опять вопрос у меня был не в том что лучше, а что хуже. Вопрос был почему так работает а этак не работает.

Работающие процедуры приведены в коде бенчмарка..
Я бы на вашем месте их бенчмарком у себя и тестировал. Там, возможно, и у вас не null возвращается, а исключение. Там какие-то грабли с разграничением полномочий мне повстречались.




Цитата

Но всеравно спасибо, было действительно интересно

Ну теперь мне уже и самому интересно стало


--------------------
Зачем платить больше,
когда можно заплатить дважды?
PM   Вверх
Sun
Дата 3.10.2006, 13:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Account removed
***


Профиль
Группа: Экс. модератор
Сообщений: 1611
Регистрация: 14.8.2002

Репутация: 2
Всего: 48



Цитата(3x3 @  3.10.2006,  08:54 Найти цитируемый пост)
Я вижу только одну потенциальную причину - это то, что в запросе вы явно задаёте оптимизатору по какому набору данных осуществлять внешний цикл, а по каким - вложенные.  Т.е. в общем случае выигрыш должен быть на RDBMS, с оптимизатором, ничего не знающим про свои данные и, соответственно, неспособным полноценно оптимизировать план запроса?

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


--------------------
Account removed
PM MAIL   Вверх
Страницы: (3) Все 1 2 [3] 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java EE (J2EE) и Spring | Следующая тема »


 




[ Время генерации скрипта: 0.0622 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.