![]() |
Задавайте вопросы, мы ответим
Вы не зашли.
Возникло несколько вопросов по процедурам и функциям в MySQL.
Пока что, только начинаю с ними работать.
1. Правильно создавать функцию или процедуру, если я, в любом случае,
хочу знать результат выполнения (выполнилась корректно или нет/номер ошибки)?
2. Чем отличаются принципиально функции и процедуры?
Например, в руководстве:
"Before MySQL 5.0.10, stored functions created with CREATE FUNCTION must not
contain references to tables, with limited exceptions. They may include some
SET statements that contain table references, for example
SET a:= (SELECT MAX(id) FROM t), and SELECT statements that fetch values
directly into variables, for example SELECT i INTO var1 FROM t."
Почему были такие различия?
3. Как я могу вернуть, выбранную строку, из таблицы (не номер, а именно все
столбцы для определённого ID, не зная полной структуры таблицы)?
4. Чем, в принципе, отличаются USER() и CURRENT_USER() (ну не читал я всё руководство).
Как правильно получить имя и хост пользователя, под которыми он авторизовался?
5. В MySQL существует "объектная модель"?
Вопрос ради праздного любопытства.
К примеру, увидел в руководстве: "MySQL supports spatial extensions to allow
the generation, storage, and analysis of geographic features."
Там почему-то есть описания классов... Дальше не читал. Не до этого.
Но просто интересно: возможно создавать свои классы? o.O
Отредактированно Артём Н. (01.04.2010 14:54:24)
Неактивен
(щас времени нет, поэтому отвечу на часть вопросов; на остальные отвечу чуть позже)
2. Чем отличаются принципиально функции и процедуры?
Процедура может напрямую возвращать результат в виде запроса (т.е. внутри процедуры может быть написано
SELECT * .. и при вызове процедуры Вы будете видеть результат этого SELECT).
Функция - не может (в функции запросы можно лишь сохранять в перменных).
Еще процедура может (при желании её создателя) менять переданные ей параметры. Функция - нет.
3. Как я могу вернуть, выбранную строку, из таблицы (не номер, а именно все
столбцы для определённого ID, не зная полной структуры таблицы)?
Так и пишите: SELECT ID FROM таблица (правда, к процедурам это отношения не имеет )
5. В MySQL существует "объектная модель"?
Нет, классов в MySQL нет (кстати, как и массивов).
Не знаю, где Вы там нашли описание классов :0 (я посмотрел бегло, что-то не нашел). В любом случае, это не про те классы, которые Вы подумали.
Неактивен
1. Не важно, все зависит от того, что Вы пишете внутри (и снаружи, кстати, тоже )
2. Отличаются тем, что это разные сущности. Ответ Lazy во многом покрывает отличия,
я бы сказал, что основное отличие в идеологии: процедура — это «выполни вот это»,
а функция — это «что будет результатом вот этого».
3. SELECT * FROM tablename WHERE id = :id
4.
[aquatica] root (none) > grant usage on *.* to blah@'%'; Query OK, 0 rows affected (0,02 sec) [aquatica] root (none) > ^DBye aquatica:~$ mysql --protocol tcp -ublah Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 199 Server version: 5.1.37-1ubuntu5.1 (Ubuntu) Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. [aquatica] blah (none) > select user(); +----------------+ | user() | +----------------+ | blah@localhost | +----------------+ 1 row in set (0,00 sec) [aquatica] blah (none) > select current_user(); +----------------+ | current_user() | +----------------+ | blah@% | +----------------+ 1 row in set (0,00 sec)
5. Нет, не существует. Впрочем, Вы можете обозвать свой объект классом, хранить
его как BLOB и написать кучу пользовательских функций для работы с этими
объектами. Только зачем?
Неактивен
Разрешите вклиниццо :-[.
Paulus, в некоторых ваших ответах иногда фигурируют запросы вида
SELECT * FROM tablename WHERE id = :id
или
SELECT coeff FROM tablename WHERE period < :period ORDER BY period DESC LIMIT 1;
Скажите пожалуйста - что означает это таинственное двоеточие? Синтаксически-то такой запрос работать отказывается, наверное имеется в виду, что :id нужно на что-то заменить?
Неактивен
Угу, заменить на значение id
Честно говоря, даже не помню, где я ему научился. Кажется, в Delphi (точнее — BDE)
были такие сокращения для параметров. А какой символ тут логичнее писать? $? @?
С @ сработает, но тогда человек может не понять, что тут надо что-то подставить.
С $ — слишком привязано к скриптовым языкам. Просто вопросительный знак — тоже
не очень, т.к. хочется именованный параметр
Неактивен
Логичнее... Да вот как-то не приходит в голову никакого символа, который хорошо на эту замену наталкивал бы... Честно говоря, я сам с BDE дела не имел, и подумал про замену лишь потому что запрос этот синтаксически не сработает. В prepare stmt, например вопросики идут, выглядит вполне хорошо, только тоже не каждый догадается... В-общем, честно говоря, я бы писал
select * from mytable where id=НУЖНОЕ_ЗНАЧЕНИЕ_ID
или
select * from mytable where id=__your_id__
где __your_id__ - нужное значение id.
Но выглядит, конечно совсем не изящно, даже страшновато :-(.
Неактивен
LazY написал:
Процедура может напрямую возвращать результат в виде запроса (т.е. внутри процедуры может быть написано
SELECT * .. и при вызове процедуры Вы будете видеть результат этого SELECT).
Результат, сохранённый в переменной?
Функция - не может (в функции запросы можно лишь сохранять в перменных).
Я не очень понял. Результат запроса?
Так и пишите: SELECT ID FROM таблица (правда, к процедурам это отношения не имеет smile )
Хм... Понятно. Если ничего не выбрано, то вернётся NULL?
В принципе, мне этого достаточно.
Но, ради теоретического интереса: а если необходимо вернуть ещё и код завершения (как я понял, OUT параметров в функциях нет)?
Нет, классов в MySQL нет (кстати, как и массивов).
Не знаю, где Вы там нашли описание классов :0 (я посмотрел бегло, что-то не нашел). В любом случае, это не про те классы, которые Вы подумали.
http://dev.mysql.com/doc/refman/5.0/en/ … archy.html
paulus написал:
1. Не важно, все зависит от того, что Вы пишете внутри (и снаружи, кстати, тоже )
...
Отличаются тем, что это разные сущности. Ответ Lazy во многом покрывает отличия,
я бы сказал, что основное отличие в идеологии: процедура — это «выполни вот это»,
а функция — это «что будет результатом вот этого».
Хм... А если мне нужно выполнить и узнать выполнилось правильно это или нет?
3. SELECT * FROM tablename WHERE id = :id
Хм... Вопрос. Это писать в RETURN функции?
select user();
У меня вернул одно и то же... Т.е., user() возвращает текущий user/host, а current_user() запись из таблицы mysql.user, соответствующую текущему пользователю?
Только зачем?
Незачем. :-) Я спрашивал именно про встроенную "объектную модель".
deadka написал:
Скажите пожалуйста - что означает это таинственное двоеточие? Синтаксически-то такой запрос работать отказывается, наверное имеется в виду, что :id нужно на что-то заменить?
...
paulus написал:
Честно говоря, даже не помню, где я ему научился. Кажется, в Delphi (точнее — BDE)
были такие сокращения для параметров.
Не знаю, как раньше было, но сейчас двоеточие, стоящие перед названием, похоже обрабатывается классами. Т.е., к СУБД никакого отношения не имеет.
Реализовано это в различных компонент для Delphi/Lazarus и прочего Delphi-like.
Следующее за двоеточием имя, является именем параметра.
Применяется примерно так:
Query.ParamByName('param').AsString := 'value';
Query.Open();
Удобно это тем, что не требуется формировать SQL динамически и тем, что в параметрах автоматически экранируются спец. символы.
С @ сработает, но тогда человек может не понять, что тут надо что-то подставить.
С ":" тоже сработает, в своём "контексте". Будет подставлен NULL. :-)
С $ — слишком привязано к скриптовым языкам.
Причём, это ничем не лучше и не хуже двоеточия. Тоже работает только в определённом контексте. :-)
deadka написал:
select * from mytable where id=НУЖНОЕ_ЗНАЧЕНИЕ_ID
Долго уж слишком...
Неактивен
Отвечу на вопросы к себе
• Чтобы узнать, выполнилось ли, нужно посмотреть на код возврата.
• Функции не возвращают строки, они возвращают значения. Писать, стало быть,
в процедуре.
• Да
Неактивен
Чтобы узнать, выполнилось ли, нужно посмотреть на код возврата.
Извиняюсь: а как узнать..?
И вот ещё вопрос. Положим, есть два пользователя, выполняющие процедуру (или разные процедуры) одновременно. Если код возврата, например системная переменная, то, каждому будет выдано правильное значение или как?
Функции не возвращают строки, они возвращают значения.
Хм... Строка разьве не является значением?
Писать, стало быть, в процедуре.
Т.е., просто написать запрос? А если, это сложная процедура, с IF, может циклами, что она вернёт?
Неактивен
А возможно ли получить уникальный идентификатор сессии для данного пользователя?
Где не ищу, натыкаюсь на PHP. :-(
А мне функция или переменная СУБД нужна, для процедуры.
Неактивен
Предлагаю Вам сделать вот таких зверей:
[aurica] root test > delimiter ;; [aurica] root test > create procedure blah() begin select 1; select 2; end;; Query OK, 0 rows affected (0.00 sec) [aurica] root test > create procedure notworking() begin select 1 from zzz; end;; Query OK, 0 rows affected (0.00 sec)
и потренироваться их запускать (с поиском ошибок). Заодно и вопросы отпадут
ненужные:
[aurica] root test > call blah();; +---+ | 1 | +---+ | 1 | +---+ 1 row in set (0.00 sec) +---+ | 2 | +---+ | 2 | +---+ 1 row in set (0.00 sec) Query OK, 0 rows affected (0.00 sec) [aurica] root test > call notworking();; ERROR 1146 (42S02): Table 'test.zzz' doesn't exist
--
Все коды, разумеется, не конфликтуют между потоками. Номер потока Вам
не нужен, т.к. это просто чиселка. Можете считать, что он равен 137. Или 56.
Все равно к данным другого потока Вы обратиться никак не сможете.
Строка базы данных состоит из нескольких колонок, поэтому значением не
является.
Неактивен
Потренировался. Просто немного странная разница между процедурами и функциями. Непривычно.
Все коды, разумеется, не конфликтуют между потоками. Номер потока Вам
не нужен, т.к. это просто чиселка. Можете считать, что он равен 137. Или 56.
Все равно к данным другого потока Вы обратиться никак не сможете.
Мне нужен не ID потока, как таковой. Мне нужен уникальный ID сессии.
Главное, чтобы он был уникальным в пределах текущего сервера.
Простой пример (не то, что мне нужно, но понятнее):
Положим, что данные пользователя, при подключении, заносятся в таблицу. При отключении, удаляются. Типа, "кто on-line"? :-\ (Я не знаю, как обработать отключение, но это просто пример.)
Конечно, возможно использовать дату/время, как ключ, например.
Но, что если два пользователя зайдут одновременно (чисто теоретически такое возможно, например, в какой-нибудь сложной дофигапроцессорной системе)?
Хорошо, возможно добавить хост и логин, в качестве ключей. Неплохо, но, например, что будет, если под тем же логином зайдёт другой пользователь?
Причём, имя хоста у него такое же. o.O
Этот мой бред - это первое. Пусть "практически невозможно", но, тем не менее, вероятно.
Второе - это то, что нужно обязательно хранить: дату, время, user, host.
А если, например, хост не требуется? Или дата?
Мне, к примеру, требуется такая фигня:
1.) Есть таблица, содержащая серии и номера пустых бланков.
2.) Пользователь может добавить договор.
3.) Серию/номер договора он может выбрать из существующих. Вводить самостоятельно запрещается.
Вопрос в том как выбирать?
Я думаю, что алгоритм следующий:
1.) Есть хр. процедура, резервирующая пустой неиспользованный бланк в таблице бланков (bso_journal).
Она устанавливает поле SESSION_ID равным уникальному ID сессии текущего пользователя.
Бланк считается зарезервированным, если SESSION_ID не NULL.
2.) Есть хр. процедура AddContract, добавляющая договор в таблицу dogovor.
Данная процедура возвращает, например - 0, при удачном добавлении или код ошибки, при неудачном.
Параметры процедуры - все данные, требуемые для добавления, кроме серии и номера договора.
Она:
1. Выбирает последний, зарезервированный данным пользователем, договор из таблицы пустых бланков.
SELECT * from bso_journal where SESSION_ID = CURRENT_SESSION_ID()
order by DATE_UPDATE asc limit 1;
Как вариант, процедуре передаются номера бланка и она пытается выбрать зарез. бланк с соответствующими номерами (так даже лучше).
2. Производит проверку ошибок (например соответствия дат).
3. В случае успешной проверки, добавляет договор в таблицу договоров.
Затем, устанавливает флаг USED, для добавленного договора в таблице.
После этого сбрасывает SESSION_ID в NULL.
3.) Есть хр. процедура сбрасывающая последний зарезервированный бланк в свободное состояние. Вызывается, если пользователь, например, кликнул "Отмена".
Тут я вижу следующие минусы:
1.) Если пользователь вдруг отключился, бланк так и останется зарезервированным.
2.) Если, затем, подключится другой пользователь и у него будет тот же SESSION_ID, этот бланк будет ему выдан, вместо требуемого.
3.) Бланк, в таком случае, будет "висеть". Но это, в принципе, решается администратором.
4.) если был перезапущен сервер... То, это совсем не айс. :-|
Вообще, может, кто знает простые способы такое сделать?
Строка базы данных состоит из нескольких колонок, поэтому значением не
является.
Ага, я примерно понял что к чему.
А код возврата возможно передать через OUT переменную?
Неактивен
Ну, генерите какую-нибудь чиселку, и используйте ее в качестве
идентификатора сессии. Вам же просто нужна уникальность между
клиентами.
Неактивен
Ага. Может, есть какая-нибудь встроенная функция для этого?
Конечно, возможно взять, например, время, плюс RAND(), сделать хэш, сохранить в переменную...
Но, может, уже есть стандартная функция/переменная?
Или методы получше этого?
Неактивен
Да и уникальности RAND() + время не гарантирует...
Неактивен
Если есть таблица, куда Вы вставляете строки, то добавьте туда автоинкрементное
поле. Если нету — заведите какую-нибудь небольшую, и можете пользоваться
автоинкрементом как-то так:
LOCK TABLES autogen WRITE;
UPDATE autogen SET id = id + 1;
SELECT @id = id FROM autogen;
UNLOCK TABLES;
Неактивен
Хм... Мне нужен ID, который сохраняется в течение сессии, для пользователя...
Хотя, конечно... Да, это обеспечит уникальность.
Возможно даже сделать таблицу с User, Host и ID.
Спасибо. Идея хорошая.
Но я тут подумал: а стоит ли, вообще, давать двум пользователям с одинаковым логином подключаться? Теоретически, пользователь может запустить два клиента у себя на машине. Но нужно ли это, на практике..?
А возможно ли как-то обрабатывать подключение/отключение пользователя?
Неактивен
Да, возможно, на клиентской стороне
А вообще, конечно, можете прописать init-connect, но это не очень хорошая
штука.
Неактивен
Да, возможно, на клиентской стороне
На стороне сервера.
А вообще, конечно, можете прописать init-connect, но это не очень хорошая
штука.
А что это? И почему не очень хорошо?
Неактивен
Почитал. Понял.
Возможно ли такое это сделать при работающем сервере, не прописывая ничего в конфиге?
И что делать с отключением?
Неактивен
Нет, нельзя. Вы хотите странного. Скажем, Ваши отключательные костылики
никогда не сработают, если, например, сервер остановить. Поэтому нужно
обдумывать технологию так, чтобы не нужно было делать костылики в принципе.
Неактивен
Да... Действительно. :-|
По идее, хорошо делать так, чтобы в случае остановки сервера или "внештатного" отключения пользователя, администратору не пришлось "разрезервировать" зарезервированные записи...
А как сделать это без "костылей"?
Неактивен
Опишите проблему, которую пытаетесь решить?
Неактивен
1.) Возник вопрос не по проблеме. Я обновляю в БД клиента.
Запись в БД производится только через процедуры.
Есть процедура такого типа:
PROCEDURE ClnChg(IN v_surname VARCHAR(255), IN v_name VARCHAR(255),
IN v_middlename VARCHAR(255), IN v_inn VARCHAR(50), ... и т.д.
В ней делается
update CLIENT set
SURNAME= v_surname,
NAME = v_name,
По идее, передаётся тип документа, его серия и номер. Естественно, что документы клиента уникальны. Но, в случае с текущей БД, я в этом не уверен.
Стоит ли передавать ID_CLIENT в процедуру или стоит определять клиента по серии/номеру документа? Ведь, по идее, чем меньше передаётся, тем лучше, во всех отношениях...
2.) Собственно проблема...
В таблицу dogovor добавляется новый договор.
У договора есть серия и номер бланка.
Бланки - документ строгой отчётности. Они либо имеются в наличии, либо нет.
Т.е., серию/номер бланка нельзя ввести вручную. Их возможно только выбрать из имеющихся в наличии.
Имеющиеся в наличии бланки, заносятся привилегированным пользователем.
Обычный пользователь нажимает "Заключить договор". Появляется окно.
В нём есть кнопка "Выбрать бланк". Пользователь заполняет договор.
Нажимает кнопку, выбирает бланк из списка, построенного по таблице.
Затем, продолжает заполнение договора.
Теоретически пользователь может раздумать составлять договор и нажать "Отмена".
При выборе бланка, очевидно, требуется его резервировать в таблице, поскольку теоретически возможен такой вариант, что другой пользователь выберет тот же бланк (по ошибке, например).
При нажатии "Отмена" "разрезервировать" - переводить в разряд свободных..
В этом нехитром действии и есть проблема. Например, если теоретически одновременно подключатся два пользователя под одинаковым логином с одинаковым именем машины... А как это запретить я не знаю.
P.S.:
Реально проблема - это вся эта чёртова программа.
Зачем, например было делать в таблице car идентификатор ID_CAR, когда в той же таблице есть VIN, уникальный, этак, в пределах мира. Конечно, VIN есть не для всех ТС. Но уж точно на все страхуемые ТС имеются уникальные документы... :-\
И в том же духе. Енто "удовольствие" приходится переделывать. А времени делать нормально уже нет. Затянул.
Отредактированно Артём Н. (06.04.2010 11:31:15)
Неактивен
1. Когда что-то делаете, надо руководствоваться здравым смыслом. Написание
хранимого кода — не исключение.
2. Почитайте про транзакции. Резервирование и отмена — это классические
примеры применения транзакций в жизни. Ну и для табличек InnoDB это уже
всё реализовано и хорошо работает (кроме SERIALIZABLE в 5.1, пока багу
не пофиксят).
В данном конкретном случае я бы вообще посадил все транзакции в READ
UNCOMMITTED и тогда Вам вообще ничего не надо делать, всё будет работать
само: есть табличка с доступными номерами бумажек. Когда надо взять
бумажку — Вы тупо стираете строчку с номером из этой таблицы. Если нажали
отмена (или убилось соединение, или рухнул сервер, или взорвалась ядерная
бомба, но сохранилась реплика) — транзакция откатится и номер вернется
в табличку. Если бумажку испортили, напечатав на ней что-то — транзакцию
применили — наступило вселенское счастие.
Неактивен