Некоторые решения с применением хранимых процедур |
Автор Administrator | |
20.05.2010 г. | |
С учетом замечаний читателей изменена нотация в задаче 1. Язык SQL поначалу кажется очень неповоротливым. Но по мере его освоения приходит мысль о том, что здесь имеем дело с МНОЖЕСТВОМ записей, отвечающих определенным непротиворечивым условиям. Хранимые процедуры - мост между этим МНОЖЕСТВОМ записей и ОТДЕЛЬНОЙ записью, принадлежащей этому множеству. Вот решения некоторых задач с применением хранимых процедур. Применяемый SQL сервер - народный interbase\firebird.
1. Одновременное отображение физических и юридических лиц, отвечающих дополнительному условию. Иногда бывает необходимо держать данные о физ лицах и юр лицах в разных таблицах.Краткое описание таблиц
Владельцы ценных бумаг считаются просто ЛИЦАМИ, а какое это лицо и его ФИО (в случае физ лица) или НАЗВАНИЕ (в случае юр лица) отобразит хранимая процедура. CREATE TABLE PERSON( PERSON_CODE INTEGER NOT NULL PRIMARY KEY ); CREATE TABLE NATUR( NATUR_CODE INTEGER NOT NULL PRIMARY KEY , PERSON_CODE_E INTEGER NOT NULL , FOREIGN KEY (PERSON_CODE_E) REFERENCES PERSON (PERSON_CODE) ON UPDATE CASCADE ON DELETE CASCADE ); CREATE TABLE JURID( JURID_CODE INTEGER NOT NULL PRIMARY KEY , PERSON_CODE_E INTEGER NOT NULL , FOREIGN KEY (PERSON_CODE_E) REFERENCES PERSON (PERSON_CODE) ON UPDATE CASCADE ON DELETE CASCADE );А вот и текст процедуры. CREATE PROCEDURE SP_ALL_OWNERS ( <font color="#0000ff"> /*входные аргументы*/</font> NAME_FRAG VARCHAR(20), <font color="#0000ff">/*вызывающий обрамляет его в %%*/</font> SECUR_CODE INTEGER, BROKER_CODE INTEGER) RETURNS ( <font color="#0000ff"> /*выходные аргументы*/</font> NAME VARCHAR(45), PERSON_CODE INTEGER, SECUR_CODE_G INTEGER, OWNER_CODE INTEGER) AS begin <font color="#0000ff">/*условия, общие для физ и юр лиц*/</font> for select SECUR_CODE_G, OWNER_CODE, PERSON_CODE_G from OWNER where OWNER.SECUR_CODE_G=:SECUR_CODE and OWNER.BROKER_CODE=:BROKER_CODE into :SECUR_CODE_G, :OWNER_CODE, :PERSON_CODE do begin <font color="#0000ff"> /*условия, частные для физ лиц*/</font> for select FIO from NATUR ,NAT_HIST where NAT_HIST.FIO LIKE :NAME_FRAG and NATUR.PERSON_CODE_E=:PERSON_CODE and <font color="#0000ff">/*лицо*/</font> NATUR.NATUR_CODE=NAT_HIST.NATUR_CODE and NAT_HIST.VALID_NOW=1 into :NAME do suspend; <font color="#0000ff">/*условия, частные для юр лиц*/</font> for select FULL_NAME from JURID ,JUR_HIST where JUR_HIST.FULL_NAME LIKE :NAME_FRAG and JURID.PERSON_CODE_E=:PERSON_CODE_ and<font color="#0000ff"> /*лицо*/</font> JURID.JURID_CODE=JUR_HIST.JURID_CODE JUR_HIST.VALID_NOW=1 into :NAME do suspend; end end^при создании физ и юр лиц :
CREATE PROCEDURE ADD_NATUR_E (name VARCHAR(45)) RETURNS (record_no INTEGER, error_code INTEGER, masterkey INTEGER, current_hist INTEGER) AS BEGIN BEGIN record_no=0; error_code=0; <font color="#0000ff">/*Создание ЛИЦА*/</font> EXECUTE PROCEDURE ADD_PERSON :x RETURNING_VALUES :masterkey, :error_code; IF (error_code=0) THEN BEGIN <font color="#0000ff">/*Создание физ лица*/</font> record_no=gen_id(NATUR_gen, 1); INSERT INTO NATUR (NATUR_CODE, PERSON_CODE_E) VALUES (:record_no,:masterkey); <font color="#0000ff">/*Создание истории физ лица*/</font> EXECUTE PROCEDURE ADD_NAT_HIST :record_no, 1 RETURNING_VALUES :current_hist, :error_code; UPDATE NAT_HIST SET FIO = :name WHERE NAT_HIST_CODE = :current_hist; END END END ^ CREATE PROCEDURE ADD_JURID_E (name VARCHAR(45)) RETURNS (record_no INTEGER, error_code INTEGER, masterkey INTEGER, c urrent_hist INTEGER) AS BEGIN BEGIN record_no=0; error_code=0; <font color="#0000ff">/*Создание ЛИЦА*/</font> EXECUTE PROCEDURE ADD_PERSON :x RETURNING_VALUES :masterkey, :error_code; IF (error_code=0) THEN BEGIN <font color="#0000ff">/*Создание юр лица*/</font> record_no=gen_id(JURID_gen, 1); INSERT INTO JURID (JURID_CODE, PERSON_CODE_E) VALUES (:record_no,:masterkey); <font color="#0000ff"> /*Создание истории юр лица*/</font> EXECUTE PROCEDURE ADD_JUR_HIST :record_no, 1 RETURNING_VALUES :current_hist, :error_code; UPDATE JUR_HIST SET FULL_NAME = :name WHERE JUR_HIST_CODE = :current_hist; END END END ^При удалении физ или юр лиц достаточно удалит ЛИЦО, все остальное будет удалено каскадно. Для отображения в детали (мастер-деталь) результата, возвращаемого хранимой процедурой, в компоненте TIBQuery, как известно можно создать запрос с параметром. select * from SP_ALL_OWNERS('%некто%', :SECUR_CODE) order by NAME;')а назначив свойство qryDetail.DataSource=masterDataSource, можно дать понять IBX-у, что значение параметра :OWNER надо искать в текущей записи указанного мастера. 2. Перестройка баз данных из источника, не поддерживающего автоматической целостности ссылочной системы с проверкой уникальности первичных ключей и целостности внешних ключей. Проверка уникальности первичных ключейОписание таблиц:
CREATE PROCEDURE TEST_UNIQ_SPORG1 ( X INTEGER) AS declare variable iCode integer; begin iCode=0; for select code from SPORG1 into :iCode do begin insert into OK_SPORG1 SELECT * FROM SPORG1 where code=:iCode; delete from SPORG1 where code=:iCode; WHEN SQLCODE -803 DO BEGIN iCode=iCode; end end end ^Проверка целостности внешних ключей Описание таблиц:
CREATE PROCEDURE TEST_INTEG_CLIENT1 ( X INTEGER) AS declare variable iCode integer; begin for select tel from CLIENT_STREET into :iCode do begin insert into OK_CLIENT_STREET SELECT * FROM CLIENT_STREET where tel=:iCode; delete from CLIENT_STREET where tel=:iCode; WHEN SQLCODE -530 DO BEGIN iCode=iCode; end end end ^При ошибке, оказывается, процедура не вылетает с откатом текущей транзакции, а просто возвращает код ошибки и ЦИКЛ ПРОДОЛЖАЕТСЯ. Были рассмотрены и другие решения поставленной задачи, но описанный вариант показал минимальный расход времени. То же самое наблюдалось при закачивании аналогичной таблицы из формата dbf в interbase с применением препроцессора gpre и низкоуровневым доступом к формату dbf (будет описано в следующей статье).3. Выборка пакетами записей с фиксированным числом записей. CREATE PROCEDURE SHOW_PART( SINCE INTEGER, TILL INTEGER) RETURNS ( THE_CODE integer, NAME varchar(10)) AS declare variable i integer; begin i=0; for select THE_CODE, NAME from MY_TABLE where NAME=<font color="#996600">'qq'</font> into :THE_CODE, :NAME do begin i=i+1; if ((SINCE<=i) AND (i<=TILL)) then begin suspend; end if (i>TILL) then begin exit; end end ^при n1>1 приведенное решение немного неоптимально, т.к. серверу приходится перебирать заново все записи, соответствующие поставленному условию. Верхняя допустимая граница TILL, как известно, определяется простым select count()-ом Выборку производить следующим образомselect * from SHOW_PART(1,3); - показать с первой по третью записи, удовлетворяющие заданному в процедуре условию. |
След. » |
---|