| Компилятор, совместимый с языками семейства xBase и Clipper | ||
|---|---|---|
| Пред. | Глава 13. Работа с SQL серверами | След. |
Чтобы построить приложение работающее с каким-нибудь SQL сервером, необходимо установить соответствующий пакет clip-<rdbms>. На данный момент доступны пакеты для следующих СУБД:
PostgreSQL by (c) The PostgreSQL Global Development Group http://www.postgresql.org
MySQL http://www.mysql.com
Oracle 8i by (c) Oracle Corporation http://www.oracle.com
ODBC driver manager http://www.microsoft.com
Interbase/Firebird by (c) Borland/Inprise http://www.interbase.com
Прокси сервер DBTCP для использования источников данных ODBC http://www.fastflow.it/dbftp
Установив пакет для выбранного SQL сервера можно собирать приложение примерно так:
bash$ clip -e test.prg -lclip-mysql
Прежде всего приложение должно произвести подключение к серверу. Для этого предназначена функция ConnectNew() ConnectNew() - это конструктор класса TConnect, т.е. он возвращает объект класса TConnect. Этот объект может использоваться для выполнения операторов SQL; для выборки набора записей, возвращаемых запросом SELECT; а также для старта и завершения транзакций. Например:
conn := ConnectNew(...) // obtain a connection
conn:Start() // start a transaction
conn:Command("UPDATE emp SET name='Total' WHERE name='Rust'")
// next time, in pay office i'll say: "My name is Total"
conn:Rollback() // just kidding :) cancel the change![]() | Одновременно может быть произведено несколько несколько подключений. Более того, возможно одновременное подключение к нескольким различным серверам. |
В операторах и запросах SQL могут использоваться параметры. Именам параметров в строке, содержащей оператор или запрос, должен предшествовать символ ':'. Значения параметров передаются в двумерном массиве, одна строка на параметр. Первый столбец каждой строки должен содержать имя параметра, второй - его значение. Например:
conn:Command("UPDATE emp SET fname=:fname,lname=:lname",;
{{"fname","John"},{"lname","Smith"}})Для получения набора записей - результата запроса SELECT используется метод класса TConnect CreateRowset(). Он возвращает объект класса TRowset. Например:
rs := conn:CreateRowset("SELECT * FROM emp WHERE fname=:fname",{{"fname","John"}})
rs:Browse() // simple BROWSE for TRowsetНесколько методов класса TRowset позволяют производить навигацию по наборузаписей: Bof(), Eof(), Skip(), Goto(), GoTop(), GoBottom(), Lastrec(), Recno().
Две функции предназначены для чтения/записи текущей записи набора: Read() и Write(). Read() возвращает объект с такой же структурой, что и структура записи. Например:
rs := conn:CreateRowset("SELECT fname,lname FROM emp")
? rs:Recno(), rs:Read() // 1 {FNAME: John, LNAME: Smith}Метод Write() получает в качестве параметра объект с несколькими атрибутами, и устанавливает значения соответствующих полей. Например:
? rs:Read() // {FNAME: John, LNAME: Smith}
obj := map()
obj:fname := "Robert"
obj:salary := 10000
rs:Write(obj)
? rs:Read() // {FNAME: Robert, LNAME: Smith}Можно добавлять записи к набору ((Append()) и удалять (Delete()). Append() получает параметр - объект. Например:
rs := conn:CreateRowset("SELECT fname,lname FROM emp")
? rs:Lastrec() // 100
obj := map()
obj:fname := "Homer"
obj:lname := "Simpson"
rs:Append(obj)
? rs:Lastrec() // 101
? rs:Read() // {FNAME: Homer, LNAME: Simpson}
rs:Delete()
? rs:Lastrec() // 100![]() | Все изменения набора записей, производимые методами Write(), Append(), Delete() - локальны. Но конструктору TRowset могут быть переданы три дополнительных параметра (<cInsertSQL>, <cDeleteSQL>,<cUpdateSQL>). Если передан, оператор <cInsertSQL> будет неявно выполнен на сервере методом Append(). Точно так же <cDeleteSQL> и <cUpdateSQL> будут выполнены методами Delete() и Write(). В случае Write() и Delete() в наборе должно присутствовать поле с уникальными идентификаторами записей. См. подробности о путях решения этой проблемы драйверами разных СУБД. Например: |
rs := conn:CreateRowset("SELECT rowid,fname,lname FROM emp",,;
"INSERT INTO emp values (:fname,:lname)",;
"DELETE FROM emp WHERE rowid=:rowid",;
"UPDATE emp SET fname=:fname,lname=:lname WHERE rowid=:rowid")В случаях когда количество возвращаемых записей невозможно предугадать будут полезны параметры <bEval> и <nEvery>. Блок кода <bEval> исполняется в процессе выборки через каждые <nEvery> записи. Если он возвращает .F. - процесс прекращается. Таким образом можно сделать индикатор процесса для больших результирующих наборов и прекращать выборку по требованию пользователя. Нижеследующий пример печатает точку через каждые 100 загруженных записей и может быть остановлен нажатием ESC.
rs := conn:CreateRowset("SELECT * FROM hugetable",,,,,,,,,,;
{|| qqout("."), inkey() != K_ESC},100)В подобных же случаях (когда невозможно предугадать размер результирующего набора) было бы полезно иметь возможность не загружать все записи сразу, а только тогда когда они потребуются. Для этого предназначен параметр <lNoFetch>. Если этот параметр .T., CreateRowset() завершается сразу после выполнения оператора SQL сервером, без загрузки записей. Загрузка производится позже, в процессе навигации по набору записей. Но в этом случае невозможно сразу определить количество выбранных записей, до тех пор пока остается хотя бы одна незагруженная запись. Для довыборки незагруженных записей существует функция TRowset:FetchAll(). Для определения количества загруженных на данный момент записей - TRowset:Fetched(). Например:
rs := conn:CreateRowset("SELECT * FROM hugetable",,,,,,,,,.T.)
rs:Gotop()
? rs:Fetched() // 1
? rs:Lastrec() // 0
for i:=1 to 100
rs:Skip()
? rs:Fetched() // 2,3,...,101
next
rs:FetchAll()
? rs:Lastrec() == rs:Fetched() // .T.TRowset поддерживает так называемые "локальные индексы". "Локальный индекс" - это индекс, создаваемый на клиентской стороне и используемый для изменения логического порядка записей в наборе. Это практически то же самое, что и обычные индексы в RDD, но время их жизни ограничено временем жизни набора записей. Т.е. они располгаются в памяти клиентской машины, а не в файлах. Функция TRowset:CreateOrder() создает индекс с заданным именем. Функция TRowset:SetOrder() активирует индекс, после чего он начинает управлять логическим порядком записей. Например:
rs := conn:CreateRowset("SELECT fname,lname FROM emp")
// create an order 'Firstname' on the 'fname' field. Key length is 20 chars.
rs:CreateOrder("Firstname","fname",20)
// create an order 'Lastname' on the 'lname' field. Key length is 20 chars.
rs:CreateOrder("Lastname","lname",20)
// create an order 'Fullname' on the both 'fname' and 'lname' fields.
// Key length is 40 chars.
rs:CreateOrder("Fullname",{|rs| rs:GetValue("fname")+rs:GetValue("lname")},20)
rs:SetOrder("Firstname")
rs:Browse() // show rows sorted by first name
rs:SetOrder("Lastname")
rs:Browse() // show rows sorted by last name
rs:SetOrder("Fullname")
rs:Browse() // show rows sorted by fname and lnameТеперь перейдем к описанию функций и классов предназначенных для работы с SQL серверами.
| Пред. | Начало | След. |
| Работа с SQL серверами | Уровень выше | Описание API SQL |