Tipi di dati
NOTICE: Questo documento è il capitolo del libro “The InterBase World” scritto da Alexey Kovyazin e Serg Vostrikov.
Nonostante il fatto che i tipi di dati siano descritti in dettaglio nella documentazione (vedi [1, capitolo 4.), è necessario considerare una serie di nozioni che verranno usate frequentemente nei capitoli seguenti di questo libro. Oltre alle informazioni generali, verranno anche esaminati diversi esempi di utilizzo dei tipi di dati nei database InterBase e raccomandazioni sul loro uso e conversione. Considereremo in dettaglio le differenze tra i tipi di dati esistenti nel 1° e 3° dialetto del database InterBase.
Informazioni sui dati
I tipi di dati sono elementi fondamentali di qualsiasi linguaggio di programmazione o di qualsiasi server DBMS. InterBase non fa eccezione. Quando diciamo che un database memorizza alcune informazioni, dobbiamo renderci conto che queste informazioni non possono essere memorizzate in un unico posto. Al contrario, i dati devono essere ordinati e messi sul loro “ripiano”. I tipi di dati definiscono cosa può essere messo su un certo “ripiano” e cosa no. Quando parliamo di “ripiano”, intendiamo principalmente i campi delle tabelle del database (vedi capitolo “Tabelle. Chiavi primarie e generatori” (parte 1)), così come le variabili all’interno di trigger e procedure memorizzate, ecc. Ogni tipo di dati ha un insieme di operazioni che possono essere eseguite sui valori di questo tipo. Pertanto, è importante scegliere il tipo di dati giusto quando si progetta un database. Questo aiuterà a evitare molti problemi nello sviluppo dei programmi client. In InterBase ci sono 12 tipi di dati che possono soddisfare le esigenze dello sviluppatore nella memorizzazione dei dati. Questi tipi sono condizionatamente suddivisi nei seguenti 6 gruppi:
- per memorizzare numeri interi - INTEGER e SMALLINT;
- per memorizzare numeri reali - FLOAT e DOUBLE PRECISION;
- per memorizzare numeri con precisione fissa - NUMERIC e DECIMAL;
- per memorizzare data, ora e timestamp - DATE, TIME e TIMESTAMP;
- per memorizzare simboli - CHARACTER (in forma abbreviata - CHAR) e VARYING CHARACTER (VARCHAR);
- per memorizzare dati dinamici in estensione - BLOB
Inoltre, è possibile definire array di valori di tipi primitivi, cioè tutti i tipi enumerati tranne BLOB.
La maggior parte dei tipi di dati InterBase corrisponde ai tipi definiti nello standard SQL92, tuttavia, oltre a questo, ci sono peculiarità proprie - array di tipi di dati primitivi e BLOB. Gli array in InterBase possono contenere un insieme di dati dello stesso tipo in un unico campo. Ad esempio, possiamo definire un array di valori di tipo INTEGER. Gli array possono avere più dimensioni. Il tipo di dati BLOB è un tipo di dati dinamico in estensione, il cui nome è spesso decifrato come Binary Large OBject. Va detto che BLOB è un’invenzione degli sviluppatori di InterBase che in seguito si è diffusa e si è stabilita in tutti i server SQL contemporanei.
Sintassi per definire i tipi di dati
I tipi di dati sono usati per descrivere campi nelle tabelle, variabili in trigger e procedure memorizzate. La sintassi comune per definire tutti i possibili tipi di dati in InterBase è fornita di seguito.
< datatype> =
{SMALLINT | INTEGER | FLOAT | DOUBLE PRECISION}[ ]
| {DATE | TIME | TIMESTAMP} [ ]
| {DECIMAL | NUMERIC} [( precision [, scale])] [ ]
| {CHAR | CHARACTER | CHARACTER VARYING | VARCHAR} [( int)
[ ] [CHARACTER SET charname]
| {NCHAR | NATIONAL CHARACTER | NATIONAL CHAR}
[VARYING] [( int)] [ ]
| BLOB [SUB_TYPE { int | subtype_name}] [SEGMENT SIZE int]
[CHARACTER SET charname]
| BLOB [(seglen [, subtype])]
Le caratteristiche dei tipi di dati come dimensione, precisione e intervallo di valori possibili sono descritte in dettaglio nella tabella 4.1 in [1., quindi non ci ripeteremo qui. Ora esaminiamo brevemente le principali peculiarità dei tipi di dati e ci concentriamo sulla loro possibile applicazione.
Tipi interi
SMALLINT e INTEGER si riferiscono ai tipi interi. Devo dire che SMALLINT rappresenta una versione ridotta di INTEGER. La sua lunghezza è di 2 byte, a differenza dei 4 previsti per memorizzare INTEGER. Di solito non si dovrebbe risparmiare sullo spazio su disco. Su questa base, è meglio usare il tipo INTEGER per memorizzare valori interi.
L’area di applicazione dei tipi interi è ovvia: sono necessari per campi che contengono solo numeri interi - per memorizzare contatori, numeri, ecc. Di solito il tipo INTEGER ha anche i campi che contengono chiavi primarie.
Tipi di dati reali
I tipi FLOAT e DOUBLE PRECISION si riferiscono a quelli reali (sono anche chiamati tipi numerici con virgola mobile). In primo luogo, voglio mettere in guardia il lettore dall’uso del tipo FLOAT - la sua precisione non è sufficiente per memorizzare la maggior parte dei valori frazionari. In particolare, non è consigliabile memorizzare valori monetari in esso - negli errori di arrotondamento delle variabili di tipo FLOAT appaiono molto rapidamente e potrebbe sorprendere un contabile quando fa calcoli. Il modo migliore per memorizzare numeri con virgola mobile (ad esempio, in sistemi contabili e sistemi per calcoli scientifici) in un database è memorizzarli nel tipo DOUBLE PRECISION.
Dovresti tenere conto che nel 3° dialetto di InterBase esiste un meccanismo per memorizzare tipi con punto fisso di 64 byte. Questo meccanismo è usato per memorizzare valori monetari. L’uso di questi tipi fornisce la migliore precisione.
Tipi di dati con punto fisso
NUMERIC e DECIMAL si riferiscono a questi tipi di dati. La domanda sulla differenza tra NUMERIC e DECIMAL viene posta molto spesso. Entrambi questi tipi hanno una capacità di cifre identica - da 1 a 18 segni, precisione identica - da zero fino alla capacità di cifre.
Ricordiamo che: la capacità di cifre è il numero totale di cifre, e la precisione - il numero di segni dopo la virgola.
La cosa più divertente è che questi tipi differiscono nella capacità di cifre massima secondo la documentazione, ma in realtà sono realizzati praticamente allo stesso modo e non c’è differenza tra loro.
Puoi facilmente verificarlo avviando l’utilità isql ed eseguendo la sequenza di azioni indicata di seguito. Creiamo la tabella della seguente vista:
SQL> CREATE TABLE test (
CON> Num_field NUMERIC(15,2),
CON> Dec_field DECIMAL(15,2));
Poi diamo un comando per mostrare la struttura della tabella:
SQL> show tables test;
E vediamo quanto segue
NUM_FIELD NUMERIC(15, 2) Nullable
DEC_FIELD NUMERIC(15, 2) Nullable
Come puoi vedere, InterBase informa che entrambe le colonne date hanno tipo NUMERIC. Le ragioni di tale comportamento risiedono nella realizzazione dei tipi di dati con punto fisso. Il fatto è che InterBase ha solo tre meccanismi per memorizzare qualsiasi espressione intera, e tutti i tipi, qualunque sia il loro nome, sono realizzati secondo queste varianti.
Ecco la tabella da [1., che illustra come vengono memorizzati vari tipi interi (tabella 1.1). Come puoi vedere, la memorizzazione dei dati nel 3° dialetto è diversa per numeri con grande capacità di cifre:
Tabella 1.1. Memorizzazione di numeri con punto fisso
| Capacità di cifre | Dialetto 1 | Dialetto 3 |
|---|---|---|
| da 1 a 4 | SMALLINT per NUMERIC INTEGER per DECIMAL |
SMALLINT |
| da 5 a 9 | INTEGER | INTEGER |
| da 10 a 18 | DOUBLE PRECISION | INT64 |
Ora possiamo dire con certezza quali sono le principali differenze tra i tipi NUMERIC e DECIMAL: nel caso di definizione di un campo (o variabile) con piccola capacità di cifre (fino a quattro), il primo è memorizzato come intero a 2 byte SMALLINT, e il secondo - come INTEGER a 4 byte. Quindi, se la capacità di cifre è superiore a quattro, i tipi DECIMAL e NUMERIC saranno equivalenti.
Presta attenzione alla differenza di realizzazione dei tipi con grande capacità di cifre nel primo e terzo dialetto. Nel primo dialetto, il numero con punto fisso si trasformava da intero a reale, dove venivano applicati i meccanismi di arrotondamento. Nel terzo dialetto questa peculiarità è stata eliminata - i grandi interi sono memorizzati realmente come interi - usando il meccanismo INT64 che può memorizzare numeri a 64 bit in un intervallo +/-2^32. Pertanto, sarebbe meglio memorizzare i dati su fondi monetari nei database creati con l’uso del 3° dialetto. Solo usando il meccanismo INT64 sarà garantita la sicurezza dei piccoli resti monetari.
Tipi per memorizzare data e ora
I tipi per memorizzare data e ora sono cambiati nella versione InterBase 6.x e nei suoi cloni rispetto a 4.x e 5.x. Per non confonderci nelle ragnatele storiche con questi tipi, considereremo la situazione nella versione InterBase 6. Poi, basandoci su essa, menzioneremo brevemente cosa c’era prima. Questo è fatto per quegli utenti che lavorano ancora con versioni precedenti di InterBase. Quindi, ci sono 3 tipi in InterBase 6.x per memorizzare data e ora - DATE, TIME e TIMESTAMP.
- Il tipo DATE memorizza date con precisione al giorno. Un intervallo di valori possibili - dal 1 gennaio 100 d.C. al 29 febbraio 32768.
- Il tipo TIME memorizza i dati sull’ora con precisione al decimillesimo di secondo. Un intervallo di valori possibili - dalle 00:00 alle 23:59.9999.
- Il tipo TIMESTAMP rappresenta una combinazione dei tipi DATE e TIME.
Come lavorare con le date? Se la questione riguarda il lavoro a livello di server in procedure memorizzate o trigger, tutto è abbastanza semplice - possiamo sempre dichiarare una variabile del tipo richiesto e impostarla dalle tabelle e viceversa. Tuttavia, è necessario trasferire i dati da un database all’applicazione e viceversa. In questo caso ci sono due approcci: usare librerie che applicano un formato originale delle date InterBase per l’accesso agli oggetti di questi tipi e convertire questo formato in normali tipi data/ora intralinguistici (FIBPlus è un esempio di tale libreria), o usare il meccanismo di trasformazione delle date in stringhe, incorporato in InterBase.
Cosa farai se hai bisogno di estrarre solo l’anno o il mese dalla data completa? Dovrai usare un gruppo di funzioni EXTRACT per questo scopo (disponibili in tutti i cloni di InterBase 6.x) che ti permettono di estrarre solo la parte richiesta dalla data. Queste funzioni sono usate nel seguente modo:
EXTRACT (MONTH FROM DATE_FIELD)
EXTRACT (YEAR FROM DATE_FIELD)
L’elenco completo dei parametri nella funzione EXTRACT è il seguente: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, WEEKDAY, YEARDAY. La loro funzione segue dal loro nome, quindi non li interpreteremo qui.
Tipi di dati per memorizzare testo
Ci sono due tipi in InterBase destinati a memorizzare informazioni testuali - CHAR e VARCHAR. I loro nomi completi - CHARACTER e CHARACTER VARYING, tuttavia non c’è motivo di usare nomi lunghi - anche il comando Show tables nell’utilità isql dà nomi brevi dei tipi.
Per definire un campo o una variabile di tipo simbolico, è necessario specificare tra parentesi dopo il nome del tipo un numero di simboli che saranno usati in un oggetto definito, oppure omettere il numero di simboli - in questo caso verrà creato un campo con lunghezza di 1 simbolo.
CREATE TABLE testCHARLen(
Field1 CHAR(255),
Field2 CHAR);
Quando questa tabella viene creata, Field1 avrà lunghezza di 255 simboli e Field2 - 1 simbolo.
I tipi CHAR e VARCHAR per molti aspetti sono simili - entrambi possono contenere fino a 32768 simboli, tuttavia ci sono alcune differenze. Sebbene entrambi questi tipi siano memorizzati in un database nello stesso modo, InterBase lavora con essi in modi diversi. Il seguente esempio lo dimostra:
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;
Come risultato, otterremo quanto segue:
(Test ) (Test)
Come puoi vedere, dopo il valore ‘Test’, selezionato da un campo c1, ci sono spazi vuoti. Ciò significa che quando si selezionano i dati da un campo di tipo CHAR, il valore restituito viene integrato con spazi fino alla lunghezza completa del campo. È difficile supporre perché tale comportamento che porta a una crescita sostanziale del traffico di rete (carico della rete) sia necessario. In ogni caso, VARCHAR è un tipo simbolico raccomandato da usare.
Una delle caratteristiche principali del tipo simbolico è il suo set di caratteri - CHARACTER SET. Il set di caratteri è definito per l’intero database e usato per impostazione predefinita per tutti i campi simbolici se non viene ridefinito esplicitamente quando si crea un campo. Per creare un campo simbolico con l’indicazione esplicita di un set di caratteri, è necessario aggiungere la descrizione di un set di caratteri nella descrizione di una colonna (nelle frasi CREATE TABLE o ALTER TABLE). Il set di caratteri WIN1251 è di solito usato per il supporto del russo (se vuoi sapere in dettaglio sull’uso del russo in InterBase vedi il capitolo “Russificazione di InterBase”(parte 1)). Ecco un esempio di tabella contenente un campo simbolico con il set di caratteri WIN1251 descritto esplicitamente:
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.