Тарифы Услуги Сим-карты

Postgresql вернуть курсор из выходного параметра. Иллюстрированный самоучитель по PostgreSQL. Что такое курсор в SQL

P rimary Key (Первичный ключ) является полем в таблице, которое однозначно идентифицирует каждую строку/запись в таблице базы данных. Первичные ключи должны содержать уникальные значения. Первичный ключ столбец не может иметь значения .

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

Если таблица имеет первичный ключ, определенный на любом поле (ях), то вы не можете иметь две записи, имеющие одинаковое значение этого поля (ей).

Примечание – Вы могли бы использовать эти понятия при создании таблиц базы данных.

Создание первичного ключа

Вот синтаксис для определения атрибута ID в качестве первичного ключа в таблице Customers.

CREATE TABLE CUSTOMERS(ID INT NOT NULL, NAME VARCHAR (20) NOT NULL, AGE INT NOT NULL, ADDRESS CHAR (25) , SALARY DECIMAL (18, 2), PRIMARY KEY (ID));

Для того, чтобы создать ограничение первичного ключа на столбце «ID», когда таблица CUSTOMERS уже существует, используйте следующий синтаксис SQL:

ALTER TABLE CUSTOMERS ADD PRIMARY KEY (ID);

Примечание

Если вы используете оператор ALTER TABLE, чтобы добавить первичный ключ, столбец первичного ключа (ей) должен был уже объявлен как не содержащий NULL значения (если таблица была создана первым).

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

CREATE TABLE CUSTOMERS(ID INT NOT NULL, NAME VARCHAR (20) NOT NULL, AGE INT NOT NULL, ADDRESS CHAR (25) , SALARY DECIMAL (18, 2), PRIMARY KEY (ID, NAME));

Чтобы создать ограничение первичного ключа на колонки «ID» и «NAME», когда таблица CUSTOMERS уже существует, используйте следующий синтаксис SQL.

ALTER TABLE CUSTOMERS ADD CONSTRAINT PK_CUSTID PRIMARY KEY (ID, NAME);

Удаление первичного ключа

Вы можете очистить ограничения первичного ключа из таблицы с помощью синтаксиса, приведенного ниже.

ALTER TABLE CUSTOMERS DROP PRIMARY KEY;

PRIMARY KEY — первичный ключ, ограничение, позволяющее однозначно идентифицировать каждую запись в таблице SQL .

PRIMARY KEY Oracle
Первичный Ключ (PRIMARY KEY ) может ограничивать таблицы или их столбцы. Это ограничение работает так же как и ограничение UNIQUE. Но следует учитывать различие между первичными ключами и уникальностью столбцов в способе их использования с внешними ключами. Первичные ключи не могут позволять значений NULL. Это означает что, подобно полям в ограничении UNIQUE, любое поле, используемое в ограничении PRIMARY KEY , должно уже быть обьявлено NOT NULL.

PRIMARY KEY Oracle . Пример №1.
Пример создания таблицы SQL с ограничением PRIMARY KEY :

Student
(Kod_stud integer NOT NULL PRIMARY KEY ,
Fam char(30) NOT NULL UNIQUE,
Adres char(50),
Ball decimal);

Лучше всего помещать ограничение PRIMARY KEY в поле (или в поля), которое будет образовывать уникальный идентификатор строки, и сохранить ограничение UNIQUE для полей которые должны быть уникальными логически (такие как номера телефона или поле sname), а не для идентификации строк. Ограничение PRIMARY KEY может также быть применено для многочисленных полей, составляющих уникальную комбинацию значений:

PRIMARY KEY Oracle . Пример №2.

CREATE TABLE Student
(Fam char (30) NOT NULL,
Im char (30) NOT NULL
Adres char (50),
PRIMARY KEY (Fam, Im));

PRIMARY KEY MySQL

PRIMARY KEY SQL / MySQL . Пример №3.

CREATE TABLE Persons (
P_Id int NOT NULL,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255),
PRIMARY KEY (P_Id));

PRIMARY KEY SQL / MySQL . Пример №4.

CREATE TABLE `ad_packages` (
`id` int(111) NOT NULL auto_increment,
`title` varchar(132) NOT NULL default »,
`price` float NOT NULL default ‘0’,
`type` varchar(22) NOT NULL default »,
`c_type` enum(‘cash’,’points’,’rur’) NOT NULL default ‘cash’,
PRIMARY KEY (`id`)
);

PRIMARY KEY SQL / MySQL . Пример №5.

CREATE TABLE `gamestat` (
`id` int(11) NOT NULL auto_increment,
`game` varchar(10) NOT NULL default ‘tuz’,
`stavok` int(11) NOT NULL default ‘0’,
`usd` float NOT NULL default ‘0’,
`rur` float NOT NULL default ‘0’,
`point` float NOT NULL default ‘0’,
`bank_usd` decimal(12,2) NOT NULL default ‘0.00’,
`bank_rur` decimal(12,2) NOT NULL default ‘0.00’,
`bank_point` decimal(12,2) NOT NULL default ‘0.00’,
PRIMARY KEY (`id`)
);

Вместо того чтобы сразу выполнять весь запрос, есть возможность настроить курсор, инкапсулирующий запрос, и затем получать результат запроса по нескольку строк за раз. Одна из причин так делать заключается в том, чтобы избежать переполнения памяти, когда результат содержит большое количество строк. (Пользователям PL/pgSQL не нужно об этом беспокоиться, так как циклы FOR автоматически используют курсоры, чтобы избежать проблем с памятью.) Более интересным вариантом использования является возврат из функции ссылки на курсор, что позволяет вызывающему получать строки запроса. Это эффективный способ получать большие наборы строк из функций.

Доступ к курсорам в PL/pgSQL осуществляется через курсорные переменные, которые всегда имеют специальный тип данных refcursor . Один из способов создать курсорную переменную, просто объявить её как переменную типа refcursor . Другой способ заключается в использовании синтаксиса объявления курсора, который в общем виде выглядит так:

имя [ [ NO ] SCROLL ] CURSOR [ ( аргументы ) ] FOR запрос ;

(Для совместимости с Oracle, FOR можно заменять на IS .) С указанием SCROLL курсор можно будет прокручивать назад. При NO SCROLL прокрутка назад не разрешается. Если ничего не указано, то возможность прокрутки назад зависит от запроса. Если указаны аргументы , то они должны представлять собой пары имя тип_данных , разделённые через запятую. Эти пары определяют имена, которые будут заменены значениями параметров в данном запросе. Фактические значения для замены этих имён появятся позже, при открытии курсора.

DECLARE curs1 refcursor; curs2 CURSOR FOR SELECT * FROM tenk1; curs3 CURSOR (key integer) FOR SELECT * FROM tenk1 WHERE unique1 = key;

Все три переменные имеют тип данных refcursor . Первая может быть использована с любым запросом, вторая связана (bound) с полностью сформированным запросом, а последняя связана с параметризованным запросом. (key будет заменён целочисленным значением параметра при открытии курсора.) Про переменную curs1 говорят, что она является несвязанной (unbound), так как к ней не привязан никакой запрос.

Прежде чем получать строки из курсора, его нужно открыть. (Это эквивалентно действию SQL-команды DECLARE CURSOR .) В PL/pgSQL есть три формы оператора OPEN , две из которых используются для несвязанных курсорных переменных, а третья для связанных.

40.7.2.1. OPEN FOR запрос

OPEN [[NO ] SCROLL ] FOR запрос ;

Курсорная переменная открывается и получает конкретный запрос для выполнения. Курсор не может уже быть открытым, а курсорная переменная обязана быть несвязанной (то есть просто переменной типа refcursor). Запрос должен быть командой SELECT или любой другой, которая возвращает строки (к примеру EXPLAIN). Запрос обрабатывается так же, как и другие команды SQL в PL/pgSQL : имена переменных PL/pgSQL заменяются на значения, план запроса кешируется для повторного использования. Подстановка значений переменных PL/pgSQL проводится при открытии курсора командой OPEN , последующие изменения значений переменных не влияют на работу курсора. SCROLL и NO SCROLL имеют тот же смысл, что и для связанного курсора.

OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;

40.7.2.2. OPEN FOR EXECUTE

OPEN не [[NO ] SCROLL ] FOR EXECUTE строка_запроса [USING выражение [, ... ] ];

Переменная курсора открывается и получает конкретный запрос для выполнения. Курсор не может быть уже открыт и он должен быть объявлен как несвязанная переменная курсора (то есть, как просто переменная refcursor). Запрос задаётся строковым выражением, так же, как в команде EXECUTE . Как обычно, это даёт возможность гибко менять план запроса от раза к разу (см. Подраздел 40.10.2). Это также означает, что замена переменных происходит не в самой строке команды. Как и с EXECUTE , значения параметров вставляются в динамическую команду, используя format() и USING . Параметры SCROLL и NO SCROLL здесь действуют так же, как и со связанным курсором.

OPEN curs1 FOR EXECUTE format("SELECT * FROM %I WHERE col1 = $1",tabname) USING keyvalue;

В этом примере в текст запроса вставляется имя таблицы с применением format() . Значение, сравниваемое с col1 , вставляется посредством параметра USING , так что заключать его в апострофы не нужно.

OPEN связанная_переменная_курсора [([ имя_аргумента := ] значение_аргумента [, ... ]) ];

Эта форма OPEN используется для открытия курсорной переменной, которая была связана с запросом при объявлении. Курсор не может уже быть открытым. Список фактических значений аргументов должен присутствовать только в том случае, если курсор объявлялся с параметрами. Эти значения будут подставлены в запрос.

План запроса для связанного курсора всегда считается кешируемым. В этом случае нет эквивалента EXECUTE . Обратите внимание, что SCROLL и NO SCROLL не могут быть указаны в этой форме OPEN , возможность прокрутки назад была определена при объявлении курсора.

При передаче значений аргументов можно использовать позиционную или именную нотацию. В позиционной нотации все аргументы указываются по порядку. В именной нотации имя каждого аргумента отделяется от выражения аргумента с помощью:= . Это подобно вызову функций, описанному в Разделе 4.3 . Также разрешается смешивать позиционную и именную нотации.

Примеры (здесь используются ранее объявленные курсоры):

OPEN curs2; OPEN curs3(42); OPEN curs3(key:= 42);

Так как для связанного курсора выполняется подстановка значений переменных, то, на самом деле, существует два способа передать значения в курсор. Либо использовать явные аргументы в OPEN , либо неявно, ссылаясь на переменные PL/pgSQL в запросе. В связанном курсоре можно ссылаться только на те переменные, которые были объявлены до самого курсора. В любом случае значение переменной для подстановки в запрос будет определяться на момент выполнения OPEN . Вот ещё один способ получить тот же результат с curs3 , как в примере выше:

DECLARE key integer; curs4 CURSOR FOR SELECT * FROM tenk1 WHERE unique1 = key; BEGIN key:= 42; OPEN curs4;

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

Работать с курсором необязательно в той же функции, где он был открыт. Из функции можно вернуть значение с типом refcursor , что позволит вызывающему продолжить работу с курсором. (Внутри refcursor представляет собой обычное строковое имя так называемого портала, содержащего активный запрос курсора. Это имя можно передавать, присваивать другим переменным с типом refcursor и так далее, при этом портал не нарушается.)

Все порталы неявно закрываются в конце транзакции, поэтому значение refcursor можно использовать для ссылки на открытый курсор только до конца транзакции.

40.7.3.1. FETCH

FETCH [ направление { FROM | IN } ] курсор INTO цель ;

FETCH извлекает следующую строку из курсора в цель . В качестве цели может быть строковая переменная, переменная типа record , или разделённый запятыми список простых переменных, как и в SELECT INTO . Если следующей строки нет, цели присваивается NULL. Как и в SELECT INTO , проверить, была ли получена запись, можно при помощи специальной переменной FOUND .

Здесь направление может быть любым допустимым в SQL-команде FETCH вариантом, кроме тех, что извлекают более одной строки. А именно: NEXT , PRIOR , FIRST , LAST , ABSOLUTE число , RELATIVE число , FORWARD или BACKWARD . Без указания направления подразумевается вариант NEXT . Везде, где используется число , оно может определяться любым целочисленным выражением (в отличие от SQL-команды FETCH , допускающей только целочисленные константы). Значения направления , которые требуют перемещения назад, приведут к ошибке, если курсор не был объявлен или открыт с указанием SCROLL .

курсор это переменная с типом refcursor , которая ссылается на открытый портал курсора.

FETCH curs1 INTO rowvar; FETCH curs2 INTO foo, bar, baz; FETCH LAST FROM curs3 INTO x, y; FETCH RELATIVE -2 FROM curs4 INTO x;

40.7.3.2. MOVE

MOVE [ направление { FROM | IN } ] курсор ;

MOVE перемещает курсор без извлечения данных. MOVE работает точно так же как и FETCH , но при этом только перемещает курсор и не извлекает строку, к которой переместился. Как и в SELECT INTO , проверить успешность перемещения можно с помощью специальной переменной FOUND .

MOVE curs1; MOVE LAST FROM curs3; MOVE RELATIVE -2 FROM curs4; MOVE FORWARD 2 FROM curs4;

UPDATE таблица SET ... WHERE CURRENT OF курсор ; DELETE FROM таблица WHERE CURRENT OF курсор ;

Когда курсор позиционирован на строку таблицы, эту строку можно изменить или удалить при помощи курсора. Есть ограничения на то, каким может быть запрос курсора (в частности, не должно быть группировок), и крайне желательно использовать указание FOR UPDATE . За дополнительными сведениями обратитесь к странице справки DECLARE .

UPDATE foo SET dataval = myval WHERE CURRENT OF curs1;

Курсоры можно возвращать из функции на PL/pgSQL . Это полезно, когда нужно вернуть множество строк и столбцов, особенно если выборки очень большие. Для этого, в функции открывается курсор и его имя возвращается вызывающему (или просто открывается курсор, используя указанное имя портала, каким-либо образом известное вызывающему). Вызывающий затем может извлекать строки из курсора. Курсор может быть закрыт вызывающим или он будет автоматически закрыт при завершении транзакции.

Имя портала, используемое для курсора, может быть указано разработчиком или будет генерироваться автоматически. Чтобы указать имя портала, нужно просто присвоить строку в переменную refcursor перед его открытием. Значение строки переменной refcursor будет использоваться командой OPEN как имя портала. Однако, если переменная refcursor имеет значение NULL, OPEN автоматически генерирует имя, которое не конфликтует с любым существующим порталом и присваивает его переменной refcursor .

Примечание

Связанная курсорная переменная инициализируется в строковое значение, представляющее собой имя самой переменной. Таким образом, имя портала совпадает с именем курсорной переменной, кроме случаев, когда разработчик переопределил имя, присвоив новое значение перед открытием курсора. Несвязанная курсорная переменная инициализируется в NULL и получит автоматически сгенерированное уникальное имя, если не будет переопределена.

Следующий пример показывает один из способов передачи имени курсора вызывающему:

CREATE TABLE test (col text); INSERT INTO test VALUES ("123"); CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS " BEGIN OPEN $1 FOR SELECT col FROM test; RETURN $1; END; " LANGUAGE plpgsql; BEGIN; SELECT reffunc("funccursor"); FETCH ALL IN funccursor; COMMIT;

В следующем примере используется автоматическая генерация имени курсора:

CREATE FUNCTION reffunc2() RETURNS refcursor AS " DECLARE ref refcursor; BEGIN OPEN ref FOR SELECT col FROM test; RETURN ref; END; " LANGUAGE plpgsql; -- для использования курсоров, необходимо начать транзакцию BEGIN; SELECT reffunc2(); reffunc2 -------------------- (1 row) FETCH ALL IN ""; COMMIT;

В следующем примере показан один из способов вернуть несколько курсоров из одной функции:

CREATE FUNCTION myfunc(refcursor, refcursor) RETURNS SETOF refcursor AS $$ BEGIN OPEN $1 FOR SELECT * FROM table_1; RETURN NEXT $1; OPEN $2 FOR SELECT * FROM table_2; RETURN NEXT $2; END; $$ LANGUAGE plpgsql; -- для использования курсоров необходимо начать транзакцию BEGIN; SELECT * FROM myfunc("a", "b"); FETCH ALL FROM a; FETCH ALL FROM b; COMMIT;

Один из вариантов цикла FOR позволяет перебирать строки, возвращённые курсором. Вот его синтаксис:

[ << метка >> ] FOR переменная-запись IN связанная_переменная_курсора [ ([ имя_аргумента := ] значение_аргумента [, ... ]) ] LOOP операторы END LOOP [ метка ];

Курсорная переменная должна быть связана с запросом при объявлении. Курсор не может быть открытым. Команда FOR автоматически открывает курсор и автоматически закрывает при завершении цикла. Список фактических значений аргументов должен присутствовать только в том случае, если курсор объявлялся с параметрами. Эти значения будут подставлены в запрос, как и при выполнении OPEN (см. Подраздел 40.7.2.3).

Данная переменная-запись автоматически определяется как переменная типа record и существует только внутри цикла (другие объявленные переменные с таким именем игнорируется в цикле). Каждая возвращаемая курсором строка последовательно присваивается этой переменной и выполняется тело цикла.

И снова SQL! А если быть точнее PL/pgSQL, сегодня поговорим именно об этом расширение языка SQL, а конкретней о том, как использовать курсор при написании функции в СУБД PostgreSQL . И о том, для чего вообще нужны курсоры, и когда их лучше использовать.

Надеюсь, Вы не забыли все те примеры и уроки, которые мы рассматривали ранее, так как для прочтения этой статьи необходимы минимальные знания SQL, для того чтобы Вы вспомнили, вот эти материалы: Как написать функцию на PL/pgSQL , Написание табличной функции на PL/pgSQL — функция, которая возвращает таблицу в последней, кстати, уже затрагивалась тема курсоров, но не подробно, поэтому сегодня мы поговорим о курсорах уже подробней.

Что такое курсор в SQL?

Курсор в SQL – это временная выборка записей в процессе выполнения функции, над которой могут выполняться необходимые Вам действия, данная выборка является указателем на область памяти.

Курсоры могут быть очень полезны, например, если Вам в функции необходимо выполнять определенные действия с каким то набором строк, при этом до начала выполнения функции Вы даже не знаете, сколько строк будет при обработке той или иной записи. Если проще курсор — это просто запрос, который запускается в процессе выполнения функции.

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

О том, что курсоры могут быть полезны, мы поговорили, но когда их лучше использовать? А использовать их лучше всего только тогда, когда у Вас нет другого выхода! Потому что курсор является очень ресурсоемким решением. В принципе если Вы будете выполнять операции над небольшим количеством записей, то это приемлемо, а если необходимо обработать большой объем данных, то Вы можете очень долго ждать, пока будет выполняться Ваша функция, а как Вы знаете быстрота в нашем деле чуть ли не главный фактор.

Пример использования курсора в функции на PL/pgSQL

О теории мы поговорили пора переходить к практике, и для начала общий синтаксис курсора в функции.

CREATE OR REPLACE FUNCTION название функции(типы переменных) RETURNS тип возвращаемого значения AS $BODY$ DECLARE объявление переменных объявление курсора BEGIN открытие курсора перебор данных и операции над ними закрытие курсора RETURN возвращение значения; END; $BODY$ LANGUAGE "plpgsql" VOLATILE

Теперь давайте приведем рабочий пример использования курсора, т.е. напишем функцию, в которой мы будем использовать курсор.

Примечание! Данный пример не из жизни, он смоделирован мной, поэтому у Вас такой ситуации может и не возникнуть.

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

Пример таблицы с несколькими записями:

id_per id_user rashod summa pr
1 1 100 100 0
1 2 100 110 0
2 1 100 90 0
2 2 100 90 0
3 1 110 100 0
3 2 100 100 10
  • id_per – период по которому идет отчет;
  • id_user – идентификатор сотрудника;
  • rashod – сумма расходов за этот период;
  • summa – сумма, которая выделялась на расходы;
  • pr – возможная премия, на погашение задолженности в прошлом периоде.

Стоит следующая задача, нам необходимо определить тот период у сотрудников, в котором они расходовали средств больше, чем им выдали, и потом не возместили. При условии, что в следующем месяце им могут возместить этот расход (колонка pr ), а могут и не возместить.

Т.е. в нашем примере у сотрудника с id_user = 1, этот период будет с id_per = 2, а у сотрудника с id_user = 2, этот период будет с id_per = 3. Другими словами, во втором периоде они оба перерасходовали выданные им средства, но сотруднику с id_user = 2 в следующем месяце их возместили, а с id_user = 1 нет, поэтому первый период возникновения перерасхода (причем не погашенного ) у сотрудника с id_user = 1 будет именно период с id_per = 2.

В общем как то так:). Но для нас главное научиться использовать курсор в функциях, и наша функция будет выглядеть вот так.

Схема в базе PostgreSQL называется test и таблица тоже называется test, а функцию я назвал test.my_fun(numeric) . Numeric – это как Вы помните тип входящего параметра.

CREATE OR REPLACE FUNCTION test.my_fun(numeric) RETURNS numeric AS $BODY$ DECLARE _id_user ALIAS FOR $1; --объявляем курсор crs_my CURSOR FOR select id_per, rashod, summa from test.test where id_user = _id_user order by id_per; --объявляем нужные нам переменные _id_per numeric; _rashod numeric; _summa numeric; _pr numeric; _var numeric; _rezult numeric; BEGIN _pr:=0; OPEN crs_my;--открываем курсор LOOP --начинаем цикл по курсору --извлекаем данные из строки и записываем их в переменные FETCH crs_my INTO _id_per, _rashod, _summa; --если такого периода и не возникнет, то мы выходим IF NOT FOUND THEN EXIT;END IF; --ищем сумму возмещения, если она была select into _pr pr from test.test where id_user=_id_user and id_per = _id_per+1; _var = _rashod - _summa; if _var > 0 then _var = _var - _pr; End if; _rezult=_id_per; --если _var даже после возмещения больше нуля, то выходим и возвращаем период EXIT when _var > 0; END LOOP;--заканчиваем цикл по курсору CLOSE crs_my; --закрываем курсор RETURN _rezult;--возвращаем результат END; $BODY$ LANGUAGE "plpgsql" VOLATILE

В функции я все прокомментировал, надеюсь понятно. Главное здесь это наш курсор crs_my, затем мы просто открываем его, извлекаем данные с помощью FETCH по каждой строке наших данных и делаем это с помощью цикла LOOP . Есть один нюанс, когда будете писать запрос для курсора, в Ваших функциях, то учитывайте сортировку, потому что цикл работает с первой строки записи, и если необходимо изменить порядок извлечения данных, то делайте это в запросе, который используется в курсоре.

Использовать функцию можно вот так:

SELECT test.my_fun(1)

Результат, как Вы помните, будет 2.

Если хотите запустить по всем записям, то используйте вот такой запрос:

SELECT test.my_fun(id_user) FROM test.test

Вот такой простой пример! Надеюсь, курсоры Вам как-то помогут в ваших функциях, но снова напомню, злоупотреблять курсорами не следует, а использовать их нужно только по необходимости. Удачи!