Что такое базы данных


Что такое базы данных.

Итак базы данных существуют следующих видов:

1) Древовидные - простейший пример - Windows Registry, файловая
система FAT и XML - здесь информация хранится в древовидной
структуре и доступ осуществляется через "путь", т.е. указание всех
узлов от корневого до нужного. Например: "c:\My
Docs\MyPictures\Me.jpg". Недостатки этого способа хранения данных
является очень медленный поиск, если не известен путь и очень
плохая устойчивость к повреждениям структуры. Преимущество -
возможность хранить в классифицированном виде очень
разнородную информацию и очень быстрый поиск при знании
ключа. Эти базы данных мы разбирать здесь не будем.

2) Сетевые базы данных - простейший пример - интернет. Т.е.
существуют узлы, обособленные друг от друга, содержащие
определённую информацию. Каждый узел представляет какое-то
количество ссылок на другие узлы, по которым и ведётся поиск.
Недостатки - очень сложный и долгий поиск, возможна неполное
предоставление информации или невозможность найти нужную
информацию. Преимущества - очень легко добавить любую,
разнородную информацию, самая высокая стабильность из всех
систем. Эти базы данных мы разбирать здесь не будем.

3) Объектные базы данных - новое веяние. Их мы разбирать здесь
не будем, но интересующиеся найдут интересной дискуссию о них
в нашем разделе по базам данных.

4) Реляционные базы данных - именно с ними мы и будем
работать. В дальнейшем если говорится "база данных", то
подразумевается "Реляционная база данных". "Реляционный" -
Relation - обозначает взаимосвязанный. С этими связями мы будем
разбираться потом, а пока можно для простоты считать, что
реляционная база данных - это набор двумерных простых таблиц.
Недостатки реляционных баз данных - хранение только
однородной информации, сложности в добавлении новых структур
и взаимоотношений, информация хранящаяся в такой БД должна
быть в нужной степени абстрагированна. Преимущества - прежде
всего очень высокая скорость поиска - по этому параметру у
реляционных баз данных конкурентов нет, высокая стабильность,
обилие софта для их поддержки и разработки, удобность для очень
широкого круга задач.

Реляционные базы данных?

Итак, Реляционная база данных упрощённо является набором
таблиц. Таблица же есть основной строительный кирпичик базы
данных. Расмотрим структуру таблицы. Для начала представте себе
таблицу, например в Word...
Что в ней есть?

Во-первых есть строки и колонки (raw and columns). В базах данных,
в отличие от Word есть строгие ограничения на их содержимое, да
и терминология немного другая:
1) Колонка называется тоже columns, но чаще употребляется
понятие "поле" (field). Колонка всегда имеет имя (по которому ее
можно найти) и обязана хранить данные только одного какого-либо
типа - например целые числа, строки, дату/время и т.п. Создавая
таблицу вы обязаны указать какой тип имеет каждое поле, другими
словами, вы заранее должны определится, какого сорта данные
будут хранится в колонке. Например, вот примерно так может
выглядеть структура таблицы для хранения данных об участнике
этого форума:

·
Ник - строка (25 символов)
·
ФИО - строка (250 символов)
·
Дата регистрации - Дата/время
·
Количество постингов - Целое
·
Показывать email - True/False

Обратите внимание что для строк я указал конкретную длину, а для
остальных ничего не указывал. Зачем? Ответ прост - каждая строка
должна занимать строго одинаковое место (об исключениях потом)
, и это сделано для быстроты поиска. Действительно , если бы
каждая строка имела разную длину, то чтобы найти например 1000
строку, надо было бы перечитать все 999 предыдущих строк, но
если известно, что каждая строка занимает например 1Кб, то чтобы
прочитать 1000 строку достаточно прочитать 1 Кб с 999Кб... Другая
сторона этого - например мне надо сравнить даты в приведенной
выше таблице - сделать это просто - я точно знаю что первую дату
можно прочитать с 276 байта, и так же точно я знаю точные
координаты каждой даты. Именно в этом и лежит одна из сторон
высокой скорости работы баз данных (другие способы ускорения
работы рассмотрим позже).

2) Строка - в базах данных имеет специфическое название - запись
(Record) - к Дельфийскому типу Record этот термин не имеет
никакого отношения. Хотя большинство БД дают возможность
перейти и прочитать например 10 запись, надо с самого начала
попытаться никогда этим не пользоваться. Почему? Да просто
потому что БД практически всегда подразумевают совместный
доступ нескольких пользователей к одним и тем же данным, и если
Вы хотите перейти на 10 запись, а другой пользователь в это время
удалит запись номер 5, то вы перейдёте вовсе не на ту запись что
ожидалось. А как же быть? У вас есть 2 способа - либо вы находите
нужную запись по значению поля - например для нашей таблицы
это будет выглядеть примерно так:

"Найти пользователя [Вася] в первой колонке"

В результате вы получите доступ ко всем полям записи для "Васи".
Либо вы берёте все записи и перебираете их в цикле пока не
найдёте нужный - это гораздо худший способ, так как работает на
2-3 порядка медленнее и имеет другие неприятные последствия (об
этом позже), но он возможен и иногда применяется.

Первая программа с базами данных?

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

Открываем новый проект. Открываем форму. Кладём на форму
компонент TTable (с закладки "Data Access" или "BDE" - у кого какая
версия Дельфей). Оп! Не ожидали - вроде бы и таблица, а компонт
не визуальный! Итак компонент TTable - это пока основной
компонент для нашей базы - всё обращение к таблице идёт только
через него. Теперь давай-те его подсоединим к базе данных.

К Дельфи прилогается учебная база данных, её мы и будем
пользовать. Найдите свойство DatabaseName и из выпадающего
списка выберите "DBDEMOS" - это и есть учебная база данных.
Теперь берём свойство TableName и в выпадающем списке
обнаруживаем список имён всех таблиц в базе данных
"DBDEMOS", выбираем например "biolife.db" - это таблица так
называется (а в данном случае и название файла)

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

Но все визуальные компоненты могут подсоединится к TTable
только через вспомогательный компонент TDataSource -
находящийся на той же закладке. Ставим и его на форму. Находим
свойство DataSet у этого компонента и в выпадающем списке
указываем на Table1. Теперь визуальные компоенты будут "видеть"
инфу в таблице через TDataSource.

Переходим на другую закладку компонентов - "Data Controls" и
ставим компоент TDBGrid. В его свойстве DataSource указываем на
DataSource1. Что видим? Пока ничего! Таблица то не открыта -
кликаем на Table1 и устанавливаем свойство Active в True.
Работает!

Можно программу откомпиллировать и поиграться со своим
первым приложением для баз данных. Неправда ли очень просто!

Визуальные компоненты для DB.

Итак, мы не написав ни строчки кода получили простейшее
приложение, работающее с базой данных. С помощью него мы
можем просматривать и редактировать содержимое таблицы.
Давайте теперь сделаем эту процедуру немного удобнее, на
закладке "Data Control" есть компонент TDBNavigator. Положим его
на форму и в инспекторе объектов поставим его свойство
DataSource указывающим на тот же DataSource1, что и для DBGrid -
собственно, теперь оба визуальных контрола(DBGrid и
DBNavigator) привязаны к одному и тому же DataSource и через него
к одной и той же таблицы. DBNavigator имеет несколько кнопок (вы
можете настроить какие именно вы хотите видеть) дающие лёгкий
контроль над следующими операциями:
(перечисление в порядке расположения кнопок)
1)
Переход на первую запись
2)
Переход на предыдущую запись
3)
Переход на следующую запись
4)
Переход на последнюю запись
5)
Добавить запись
6)
Удалить запись
7)
Редактировать запись
8)
Сохранить изменения
9)
Отменить изменения
10)
Перечитать таблицу

Обратите внимание, что запись (строка) таблицы есть как бы
неделимый квант информации - т.е. отменяются действия
произведенные для всей записи целиком, добавляется или
удаляется тоже строка целиком.

Разберём ещё несколько визуальных компонентов, чтобы
покончить с визуализацией данных и перейти собственно к
программированию.

На закладке Data Controls есть ещё несколько важных компонентов,
давайте поставим их на нашу форму: DBLabel, DBEdit, DBMemo и
DBImage. Все их так же как и DBGrid соединим с DataSource1.
Однако здесь мы обнаружим что этого недостаточно, эти
компоненты работают с отдельной ячейкой в таблице, поэтому мы
должны указать ещё поле (столбец) который они будут
показывать.
Давайте сделаем следуюшие - для каждого из этих компонентов
укажем свойство DataField, например следующим образом:

DBLabel - ассоциируем с полем Category
DBEdit - ассоциируем с полем Common_name
DBMemo - ассоциируем с полем Notes
DBImage - ассоциируем с полем Graphic

Можно откомпилировать программу и немного поиграться с ней.
Итак что мы можем здесь увидеть? Что в каждый данный момент
времени из всей таблицы у нас есть запись которая активная
(текущая) - в DBGrid она показывается треугольничком слева.
Именно с этой единственной записью мы и можем оперировать -
удалять, добавлять, редактировать, именно её содержимое
отображается в DBLabel, DBEdit, DBMemo, DBImage и именно она
может быть изменена при помощи этих компонентов. Описанная
только что структура позволяет работать только с одной записью в
определённый момент времени, если вы переходите на другую
запись то все изменения должны быть либо запомнены либо
отменены! По умолчанию они запоминаются без всяких запросов, в
чём вы можете убедиться меняя значения и переходя на другую
запись.

Работа с таблицами в коде.

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

Итак, начинаем разбирать способы работы с базами данных в
коде.
Прежде всего заметим, что работать мы будем только с
компонентом
Table1. Сразу предупреждаю - КАТЕГОРИЧЕСКИ НЕ СЛЕДУЕТ
ПЫТАТЬСЯ ИЗ
КОДА МЕНЯТЬ ЗНАЧЕНИЯ В ВИЗУАЛЬНЫХ
КОМПОНЕНТАХ, не следует пытаться
менять или читать значения из DBGrid, DBEdit и т.д. Эти
компоненты
существуют только для работы оператора "вручную". Для доступа к
данным из кода надо использовать только невизуальные
компоненты
типа TTable (в дальнейшем мы разберём и другие компоненты для
работы
с данными - но в любом случае это будут не визуальные
компоненты).

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

1) Определить поле задача очень простая. Способов здесь 2:
или по имени:

Table1.FieldByName('Category')

или по номеру столбца

Table1.Fields[1]

Оба выражения являются объектом наследованным от типа TField

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

Table1.FieldByName('Category').AsString
Table1.FieldByName('Category').AsInteger
Table1.FieldByName('Category').AsBoolean
Table1.FieldByName('Category').AsDateTime
Table1.FieldByName('Category').AsFloat

Например, поставте на форму кнопку, и на onClick напишите
следующий код:

Showmessage(Table1.FieldByName('Category').AsString);

При нажатии на кнопку вы увидите содержимое столбца 'Category'
для текущей записи. Аналогично для обращения по номеру:

Showmessage(Table1.Fields[1].AsString);

Обратите внимание, что на этапе компилляции компиллятор
абсолютно
не знает о реальном типе данных в поле таблицы. Это выяснится
только при попытке выполнить строку. Что будет если типы не
совпадают? Если тип можно конвертировать - то ничего
страшного,
например если у вас поле с целым числом 123, то обращение к
полю
через AsString выдаст результат - строку '123'. Но если типы
не совместимы, то будет сгенерирована ошибка, например такая
строка
почти наверняка в нашем приложении приведёт к ошибке:

var i:integer;
...
i:=Table1.FieldByName('Category').AsInteger;
showmessage(inttostr(i));

Потому что реальные данные не могут быть приведены к целому
типу.

Теперь давайте разбираться как нам добраться до нужной строки,
другими словами, до нужной записи. Как я уже говорил мы можем
работать только с одной "активной" записью, поэтому задача
сводится
к установке нужной записи "активной" (К знатокам баз данных - я
упорно
и намеренно обхожу стороной понятие "курсор" и попытаюсь
провести повествование
без его упоминания, с целью упрощения понимания материала и не
хочу углублятся
в материал, без которого можно на первых порах обойтись). Итак,
прежде всего
Table компонент имеет 4 метода которые помогут нам пройти через
все строки
таблицы:

Table1.First - переход на первую запись
Table1.Last - переход на последнюю запись
Table1.Prior - переход на предыдущую запись
Table1.Next - переход на следующую запись

А так же 2 полезных свойства:

Table1.RecordCount - возвращает количество записей (строк) в
таблице
Table1.Eof - возвращает TRUE если достигнута последняя запись, в
остальных случаях FALSE


Давайте на нашу форму положим компонент Memo (на сей раз
обычное, а не dbMemo).

Вот этот простейший код позволит пройти таблицу от начала до
конца и считать
значения одного поля для всех записей в Memo:

Table1.First;//переход на первую запись
While not Table1.eof do //делать цикл пока таблица не закончится
begin
Memo1.lines.add(Table1.fieldbyname('Category').AsString)
; //заносим в Мемо значение поля для текущей записи
Table1.Next;//переходим на следующую запись
end;

Или это же самое можно сделать например так:

Table1.First;
For i=0 to Table1.recordcount-1 do
begin
Memo1.lines.add(Table1.fieldbyname('Category').AsString)
; //заносим в Мемо значение поля для текущей записи
Table1.Next;//переходим на следующую запись
end;

Второй способ гораздо хуже. Он имеет следующие недостатки:

1) Не для всех баз данных метод Table1.recordcount возвращает
правильное
значение. Иногда Table1.recordcount устанавливается только после
перехода на
последнюю запись(это не глюк, это вполне объяснимое поведение,
например если у
вас есть хорошая табличка размером в несколько гигабайт на другом
компьютере,
то Table вовсе не скачивает всю эту таблицу локально, а достаёт
данные только
по мере необходимости). К счастью этот недостаток легко
устраним, надо код
преобразовать до следующего вида:

Table1.Last;
Table1.First;
For i=0 to Table1.recordcount-1 do
begin
Memo1.lines.add(Table1.fieldbyname('Category').AsString)
; //заносим в Мемо значение поля для текущей записи
Table1.Next;//переходим на следующую запись
end;

Несмотря на кажущуюся бессмысленность это работает.

2) А вот другой аспект - всегда надо помнить о
многопользовательском
доступе к таблицам - что случится, если во время прохода по
записям другой пользователь
добавит или удалит запись?



Опубликовал admin
18 Авг, Среда 2004г.



Программирование для чайников.