Ta strona została przetłumaczona maszynowo. Przeczytaj oryginał angielski. English

Biblioteka IBSurgeon

Typy danych

_NOTICE: Ten dokument jest rozdziałem z książki „The InterBase World”, która została napisana przez Aleksieja Kowiazina i Sierga Wostrikowa.

Pomimo tego, że typy danych są szczegółowo opisane w dokumentacji (patrz [1, rozdział 4.), konieczne jest rozważenie szeregu pojęć, które będą często używane w kolejnych rozdziałach tej książki. Oprócz ogólnych informacji, zostaną również omówione kilka przykładów użycia typów danych w bazach danych InterBase oraz zalecenia dotyczące ich stosowania i konwersji. Rozważymy szczegółowo różnice w typach danych istniejące w 1. i 3. dialekcie bazy danych InterBase.

O danych

Typy danych są podstawowymi elementami każdego języka programowania lub serwera DBMS. InterBase nie jest wyjątkiem. Kiedy mówimy, że baza danych przechowuje pewne informacje, musimy zdawać sobie sprawę, że informacje te nie mogą być przechowywane w jednym miejscu. Jest wręcz przeciwnie; dane muszą być posortowane i umieszczone na swojej „półce”. Typy danych określają, co można umieścić na danej „półce”, a co nie. Mówiąc o „półce”, mamy na myśli przede wszystkim pola tabel bazy danych (patrz rozdział „Tabele. Klucze podstawowe i generatory” (część 1)), a także zmienne w triggerach i procedurach składowanych itp. Każdy typ danych ma zestaw operacji, które można wykonać na wartościach tego typu. Dlatego ważne jest, aby wybrać odpowiedni typ danych podczas projektowania bazy danych. Pomoże to uniknąć wielu problemów podczas tworzenia programów klienckich. W InterBase istnieje 12 typów danych, które mogą zaspokoić wymagania programisty dotyczące przechowywania danych. Typy te są warunkowo podzielone na 6 następujących grup:

  • do przechowywania liczb całkowitych - INTEGER i SMALLINT;
  • do przechowywania liczb rzeczywistych - FLOAT i DOUBLE PRECISION;
  • do przechowywania liczb ze stałą precyzją - NUMERIC i DECIMAL;
  • do przechowywania daty, czasu i znacznika czasu - DATE, TIME i TIMESTAMP;
  • do przechowywania znaków - CHARACTER (w skrócie - CHAR) i VARYING CHARACTER (VARCHAR);
  • do przechowywania dynamicznie rozszerzanych danych - BLOB

Dodatkowo możliwe jest definiowanie tablic wartości typów pierwotnych, tj. wszystkich wymienionych typów z wyjątkiem BLOB.

Większość typów danych InterBase odpowiada typom zdefiniowanym w standardzie SQL92, jednak oprócz tego istnieją ich własne cechy szczególne - tablice typów pierwotnych i BLOB. Tablice w InterBase mogą zawierać zestaw danych tego samego typu w jednym polu. Na przykład możemy zdefiniować tablicę wartości typu INTEGER. Tablice mogą mieć kilka wymiarów. Typ danych BLOB jest dynamicznie rozszerzanym typem danych, którego nazwa jest często rozszyfrowywana jako Binary Large OBject. Należy powiedzieć, że BLOB jest wynalazkiem twórców InterBase, który później rozprzestrzenił się i zadomowił we wszystkich współczesnych serwerach SQL.

Składnia definiowania typów danych

Typy danych są używane do opisywania pól w tabelach, zmiennych w triggerach i procedurach składowanych. Poniżej podano ogólną składnię definiowania wszystkich możliwych typów danych w InterBase.

< typ danych> =

{SMALLINT | INTEGER | FLOAT | DOUBLE PRECISION}[ ]

| {DATE | TIME | TIMESTAMP} [ ]

| {DECIMAL | NUMERIC} [( precyzja [, skala])] [ ]

| {CHAR | CHARACTER | CHARACTER VARYING | VARCHAR} [( int)

[ ] [CHARACTER SET nazwa_znaków]

| {NCHAR | NATIONAL CHARACTER | NATIONAL CHAR}

[VARYING] [( int)] [ ]

| BLOB [SUB_TYPE { int | nazwa_podtypu}] [SEGMENT SIZE int]

[CHARACTER SET nazwa_znaków]

| BLOB [(długość_segmentu [, podtyp])]

Charakterystyki typów danych, takie jak rozmiar, precyzja i zakres możliwych wartości, są szczegółowo opisane w tabeli 4.1 w [1., dlatego nie będziemy się tutaj powtarzać. Teraz krótko omówimy główne cechy typów danych i skupimy się na ich możliwym zastosowaniu.

Typy całkowite

SMALLINT i INTEGER należą do typów całkowitych. Muszę powiedzieć, że SMALLINT jest okrojoną wersją INTEGER. Jego długość wynosi 2 bajty, w przeciwieństwie do 4 bajtów przeznaczonych do przechowywania INTEGER. Zwykle nie powinno się oszczędzać na miejscu na dysku. Z tego powodu lepiej jest używać typu INTEGER do przechowywania wartości całkowitych.

Obszar zastosowania typów całkowitych jest oczywisty: są one niezbędne dla pól zawierających tylko liczby całkowite - do przechowywania liczników, numerów itp. Zazwyczaj typ INTEGER mają również pola zawierające klucze podstawowe.

Typy rzeczywiste

Typy FLOAT i DOUBLE PRECISION należą do rzeczywistych (nazywane są również typami liczbowymi z punktem zmiennoprzecinkowym). Na początku chcę ostrzec czytelnika przed używaniem typu FLOAT - jego precyzja nie jest wystarczająca do przechowywania większości wartości ułamkowych. Szczególnie nie zaleca się przechowywania w nim wartości pieniężnych - w zmiennych typu FLOAT błędy zaokrągleń pojawiają się bardzo szybko i mogą zaskoczyć księgowego podczas obliczeń. Najlepszym sposobem przechowywania liczb z punktem zmiennoprzecinkowym (na przykład w systemach księgowych i systemach do obliczeń naukowych) w bazie danych jest przechowywanie ich w typie DOUBLE PRECISION.

Należy wziąć pod uwagę, że w 3. dialekcie InterBase istnieje mechanizm przechowywania typów ze stałym punktem o długości 64 bajtów. Mechanizm ten jest używany do przechowywania wartości pieniężnych. Użycie tych typów zapewnia najlepszą precyzję.

Typy danych ze stałym punktem

NUMERIC i DECIMAL należą do tych typów danych. Pytanie o różnicę między NUMERIC i DECIMAL pojawia się bardzo często. Oba te typy mają identyczną pojemność cyfrową - od 1 do 18 znaków, identyczną precyzję - od zera do pojemności cyfrowej.

Przypomnijmy, że: pojemność cyfrowa to całkowita liczba cyfr, a precyzja - liczba znaków po przecinku.

Najzabawniejsze jest to, że typy te różnią się maksymalną pojemnością cyfrową według dokumentacji, ale w rzeczywistości są zrealizowane prawie identycznie i nie ma między nimi różnicy.

Możesz to łatwo sprawdzić, uruchamiając narzędzie isql i wykonując poniższą sekwencję działań. Tworzymy tabelę o następującym widoku:

SQL> CREATE TABLE test (

CON> Num_field NUMERIC(15,2),

CON> Dec_field DECIMAL(15,2));

Następnie wydajemy polecenie pokazania struktury tabeli:

SQL> show tables test;

I widzimy następujące:

NUM_FIELD NUMERIC(15, 2) Nullable

DEC_FIELD NUMERIC(15, 2) Nullable

Jak widać, InterBase informuje, że obie podane kolumny mają typ NUMERIC. Przyczyny takiego zachowania leżą w realizacji typów danych ze stałym punktem. Rzecz w tym, że InterBase ma tylko trzy mechanizmy przechowywania dowolnych wyrażeń całkowitych, a wszystkie typy, niezależnie od nazwy, są zrealizowane zgodnie z tymi wariantami.

Oto tabela z [1., która ilustruje, jak przechowywane są różne typy całkowite (tabela 1.1). Jak widać, przechowywanie danych w 3. dialekcie różni się dla liczb o dużej pojemności cyfrowej:

Tabela 1.1. Przechowywanie liczb ze stałym punktem

Pojemność cyfrowa Dialekt 1 Dialekt 3
od 1 do 4 SMALLINT dla NUMERIC
INTEGER dla DECIMAL
SMALLINT
od 5 do 9 INTEGER INTEGER
od 10 do 18 DOUBLE PRECISION INT64

Teraz możemy z całą pewnością powiedzieć, jakie są główne różnice między typami NUMERIC i DECIMAL: w przypadku definiowania pola (lub zmiennej) o małej pojemności cyfrowej (do czterech) pierwszy jest przechowywany jako 2-bajtowa liczba całkowita SMALLINT, a drugi - jako 4-bajtowa INTEGER. Zatem, jeśli pojemność cyfrowa jest większa niż cztery, typy DECIMAL i NUMERIC będą równoważne.

Zwróć uwagę na różnicę w realizacji typów o dużej pojemności cyfrowej w pierwszym i trzecim dialekcie. W pierwszym dialekcie liczba ze stałym punktem zmieniała się z całkowitej na rzeczywistą, gdzie stosowano mechanizmy zaokrąglania. W trzecim dialekcie ta cecha została zlikwidowana - duże liczby całkowite są przechowywane naprawdę jako liczby całkowite - za pomocą mechanizmu INT64, który może przechowywać 64-bitowe liczby w zakresie +/-2^32. Dlatego lepiej byłoby przechowywać dane dotyczące środków pieniężnych w bazach danych utworzonych z użyciem 3. dialektu. Tylko użycie mechanizmu INT64 zagwarantuje bezpieczeństwo małych reszt pieniężnych.

Typy do przechowywania daty i czasu

Typy do przechowywania daty i czasu zmieniły się w wersji InterBase 6.x i jej klonach w porównaniu z 4.x i 5.x. Aby nie pogubić się w historycznych zawiłościach związanych z tymi typami, rozważymy sytuację w wersji InterBase 6. Następnie na jej podstawie krótko wspomnimy, co było wcześniej. Robimy to dla tych użytkowników, którzy nadal pracują na wczesnych wersjach InterBase. Tak więc w InterBase 6.x istnieją 3 typy do przechowywania daty i czasu - DATE, TIME i TIMESTAMP.

  • Typ DATE przechowuje daty z dokładnością do dnia. Zakres możliwych wartości - od 1 stycznia 100 r. n.e. do 29 lutego 32768 r.
  • Typ TIME przechowuje dane o czasie z dokładnością do dziesięciotysięcznej części sekundy. Zakres możliwych wartości - od 00:00 do 23:59.9999.
  • Typ TIMESTAMP stanowi połączenie typów DATE i TIME.

Jak pracować z datami? Jeśli pytanie dotyczy pracy na poziomie serwera w procedurach składowanych lub triggerach, wszystko jest dość proste - zawsze możemy zadeklarować zmienną wymaganego typu i przypisać ją z tabel i odwrotnie. Jednak konieczne jest przesyłanie danych z bazy danych do aplikacji i z powrotem. W tym przypadku istnieją dwa podejścia: użycie bibliotek, które stosują oryginalny format dat InterBase do dostępu do obiektów tych typów i konwertują ten format na zwykłe wewnątrzjęzykowe typy daty/czasu (przykładem takiej biblioteki jest FIBPlus), lub użycie mechanizmu konwersji dat na ciągi znaków, wbudowanego w InterBase.

Co zrobisz, jeśli potrzebujesz wyciąć tylko rok lub miesiąc z pełnej daty? Będziesz musiał użyć grupy funkcji EXTRACT (dostępnych we wszystkich klonach InterBase 6.x), które pozwalają wyodrębnić tylko wymaganą część z daty. Funkcje te są używane w następujący sposób:

EXTRACT (MONTH FROM DATE_FIELD)

EXTRACT (YEAR FROM DATE_FIELD)

Pełna lista parametrów funkcji EXTRACT jest następująca: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, WEEKDAY, YEARDAY. Ich funkcja wynika z ich nazwy, dlatego nie będziemy ich tutaj interpretować.

Typy danych do przechowywania tekstu

W InterBase istnieją dwa typy przeznaczone do przechowywania informacji tekstowej - CHAR i VARCHAR. Ich pełne nazwy - CHARACTER i CHARACTER VARYING, jednak nie ma powodu, aby używać długich nazw - nawet polecenie Show tables w narzędziu isql podaje krótkie nazwy typów.

Aby zdefiniować pole lub zmienną typu symbolicznego, należy podać w nawiasach po nazwie typu liczbę znaków, które będą używane w zdefiniowanym obiekcie, lub pominąć liczbę znaków - wtedy zostanie utworzone pole o długości 1 znaku.

CREATE TABLE testCHARLen(

Field1 CHAR(255),

Field2 CHAR);

Po utworzeniu tej tabeli Field1 będzie miało długość 255 znaków, a Field2 - 1 znak.

Typy CHAR i VARCHAR są pod wieloma względami podobne - oba mogą zawierać do 32768 znaków, jednak istnieją pewne różnice. Chociaż oba te typy są przechowywane w bazie danych w ten sam sposób, InterBase pracuje z nimi inaczej. Poniższy przykład to demonstruje:

SQL> create table testCHAR ( c1 char(10), c2 varchar(10));

SQL> insert into testCHAR(c1,c2) values(‘Test’,‘Test’);

SQL> SELECT ‘(’||c1.|’)’, ‘(’||c2.|’)’ from testCHAR;

W wyniku otrzymamy następujące:

(Test ) (Test)

Jak widać, po wartości ‘Test’, wybranej z pola c1, pojawiły się spacje. Oznacza to, że podczas wybierania danych z pola typu CHAR zwracana wartość jest uzupełniana spacjami do pełnej długości pola. Trudno zgadnąć, dlaczego takie zachowanie, które prowadzi do znacznego wzrostu ruchu sieciowego (obciążenia sieci), jest konieczne. W każdym razie VARCHAR jest typem symbolicznym zalecanym do użycia.

Jedną z najważniejszych cech typu symbolicznego jest jego zestaw znaków - CHARACTER SET. Zestaw znaków jest definiowany dla całej bazy danych i używany domyślnie dla wszystkich pól symbolicznych, jeśli nie zostanie jawnie przedefiniowany podczas tworzenia pola. Aby utworzyć pole symboliczne z jawnym wskazaniem zestawu znaków, należy dodać opis zestawu znaków w opisie kolumny (w zdaniach CREATE TABLE lub ALTER TABLE). Zestaw znaków WIN1251 jest zwykle używany do obsługi języka rosyjskiego (jeśli chcesz poznać szczegóły dotyczące używania języka rosyjskiego w InterBase, zobacz rozdział „Rusyfikacja InterBase” (część 1)). Oto przykład tabeli zawierającej pole symboliczne z jawnie opisanym zestawem znaków WIN1251:

CREATE TABLE TestCHARSET(

Field1 VARCHAR(255),

Field2 VARCHAR(255) CHARACTER SET win1251);

Здесь Field1 - это поле без явного указания набора символов, поэтому для него будет использоваться набор символов, заданный при создании базы данных. Для поля Field2 очевидно определено, что оно будет хранить символы в кодировке WIN1251.

Помимо указания набора символов для символьных полей, можно также задать порядок сортировки (collation), который определяет, как будут сортироваться символы этого набора данных. Для русского языка существуют два варианта сортировки - WIN1251 и PXW_CYRL. Более подробно об использовании COLLATION ORDER см. в главе «Русификация InterBase».

Полный список наборов символов и применяемых для них COLLATION ORDER можно найти в документации [1, глава 13..

Внимание! Согласно документации по InterBase 6 существует 4 символьных типа: помимо вышеупомянутых типов данных существуют еще 2 - NCHAR и NCHAR VARYING, однако в той же документации ниже объясняется, что последние два типа - это те же типы, что и CHAR и VARCHAR, только с набором символов ISO8859.1 по умолчанию. Это означает, что фактически использование псевдотипа NCHAR эквивалентно применению CHAR DEFAULT CHARACTER SET ISO8859.1. Аналогично для NCHAR VARYING, только вместо CHAR используется VARCHAR. Очевидно, что применение этих псевдотипов предназначено для пользователей в Западной Европе и США, где набор символов ISO8859.1 создан для поддержки языков.

Тип данных BLOB

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

CREATE TABLE testBLOB(

myBlobField BLOB);

В результате будет создано поле myBlobField, в котором можно хранить данные значительного размера. Несмотря на то, что поля BLOB не отличаются от других по способу определения, их реализация внутри базы данных сильно отличается. Не-BLOB-поля располагаются на странице данных (см. главу «Структура базы данных InterBase» (часть 4)) близко друг к другу, а на странице данных хранится только идентификатор BLOB, сам же BLOB размещается на специальной странице. Такая структура данных позволяет хранить данные нефиксированного размера.

Тип BLOB имеет возможность определения набора нескольких подтипов и специальных процедур, называемых фильтрами (BLOB filters), для работы с этими подтипами. В InterBase встроено несколько предопределенных подтипов BLOB. Все эти подтипы имеют неотрицательные номера, например подтип 0 - данные неопределенного типа, подтип 1 - текст, подтип 2 - BLR (Binary Language Representation, см. глоссарий и главу «Структура базы данных InterBase») и т. д. Пользователь также может определять подтипы BLOB, которые могут иметь отрицательные значения. Фильтр может применяться к каждому типу. Он преобразует поле одного подтипа в другой.

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

Массивы

СУБД InterBase была одной из первых, в которых появились массивы. Поддержка массивов в базе данных является расширением традиционной реляционной модели. Наличие массивов позволяет упростить работу с наборами данных одного типа.

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

Для создания поля массива чисел INTEGER в таблице следует написать примерно следующее:

CREATE TABLE test(

myOneDimArray INTEGER[12.,

myTwoDimArray INTEGER[5,4.,

myThreeDimArray INTEGER[2,10,8.);

Таким образом, будут созданы три поля типа массив: myOneDimArray - поле, содержащее одномерный массив из 12 чисел, myTwoDimArray - содержащее двумерный массив (матрицу) - 5х4 целых чисел, и myThreeDimArray - поле, содержащее трехмерный массив 2х10х8. Следует отметить, что при таком определении элементы массива нумеруются начиная с «единицы», т. е. первый элемент имеет номер 1, второй - номер 2 и т. д. Если кто-то хочет задать границы массива самостоятельно, например от 0 до 5, следует указать определение поля следующим образом:

myArray INTEGER[0:5.

Массивы реализованы на основе полей типа BLOB, поэтому не стоит опасаться, что многомерный массив «засорит» вашу таблицу большим объемом данных: InterBase аккуратно разместит данные массива на отдельных страницах для оптимизации операций ввода-вывода в этих полях. Как использовать массивы? Они предоставляют удобный механизм для хранения объектов одного типа. Однако в 80 % случаев вместо массивов разработчики предпочитают хранить множественные данные в подчиненных (детальных) таблицах, поэтому массивы не так часто используются в клиентских приложениях СУБД InterBase. Это происходит потому, что библиотеки доступа, поставляемые с Delphi и C ++ Builder, такие как BDE и IBX, не умеют работать с массивами. Согласно документации по InterBase, с массивами можно работать с помощью препроцессора gpre, однако это не самый удобный способ для разработчика на Delphi/C ++ Builder. К счастью, в библиотеке FIBPlus есть поддержка полей-массивов в InterBase. Подробно об этом можно прочитать в главе «Особые возможности FIBPlus». Клиентская библиотека IBProvider, позволяющая создавать клиентские приложения для InterBase с использованием средств разработки компании Microsoft, также поддерживает работу с массивами (см. главу «Разработка клиентских приложений СУБД InterBase с использованием технологии Microsoft OLE DB» (часть 3)).

Заключение

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