Компилятор, совместимый с языками семейства 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 |