此页面为机器翻译。请阅读英文原文。 English

IBSurgeon 文库

数据类型

NOTICE: This document is the chapter from the book “The InterBase World” which was written by Alexey Kovyazin and Serg Vostrikov.

Несмотря на то, что типы данных подробно описаны в документации (см. [1, глава 4.), необходимо рассмотреть ряд понятий, которые будут часто использоваться в следующих главах этой книги. Помимо общей информации, будут также рассмотрены несколько примеров использования типов данных в базах данных InterBase и рекомендации по их использованию и преобразованию. Мы подробно рассмотрим различия типов данных, существующие в 1-м и 3-м диалектах базы данных InterBase.

О данных

Типы данных являются базовыми элементами любого языка программирования или любого сервера СУБД. InterBase не является исключением. Когда мы говорим, что база данных хранит некоторую информацию, мы должны понимать, что эта информация не может храниться в одном месте. Наоборот, данные должны быть рассортированы и разложены по своим «полкам». Типы данных определяют, что можно положить на определенную «полку», а что нельзя. Когда мы говорим о «полке», прежде всего мы имеем в виду поля таблиц базы данных (см. главу «Таблицы. Первичные ключи и генераторы» (часть 1)), а также переменные внутри триггеров и хранимых процедур и т.д. Каждый тип данных имеет набор операций, которые могут выполняться над значениями этого типа. Поэтому важно правильно выбрать тип данных при проектировании базы данных. Это поможет избежать многих проблем при разработке клиентских программ. В InterBase существует 12 типов данных, которые могут удовлетворить требования разработчика по хранению данных. Эти типы условно подразделяются на следующие 6 групп:

  • для хранения целых чисел - INTEGER и SMALLINT;
  • для хранения вещественных чисел - FLOAT и DOUBLE PRECISION;
  • для хранения чисел с фиксированной точностью - NUMERIC и DECIMAL;
  • для хранения даты, времени и метки времени - DATE, TIME и TIMESTAMP;
  • для хранения символов - CHARACTER (в сокращенной форме - CHAR) и VARYING CHARACTER (VARCHAR);
  • для хранения динамически расширяемых данных - BLOB

Кроме того, можно определять массивы значений примитивных типов, т.е. всех перечисленных типов, кроме BLOB.

Большинство типов данных InterBase соответствуют типам, определенным в стандарте SQL92, однако, помимо этого, есть свои особенности - массивы примитивных типов данных и BLOB. Массивы в InterBase могут содержать набор данных одного типа в одном поле. Например, можно определить массив значений типа INTEGER. Массивы могут иметь несколько измерений. Тип данных BLOB - это динамически расширяемый тип данных, название которого часто расшифровывается как Binary Large OBject. Следует сказать, что BLOB - это изобретение разработчиков InterBase, которое позже распространилось и закрепилось во всех современных SQL-серверах.

Синтаксис определения типов данных

Типы данных используются для описания полей в таблицах, переменных в триггерах и хранимых процедурах. Общий синтаксис определения всех возможных типов данных в InterBase приведен ниже.

< 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])]

Характеристики типов данных, такие как размер, точность и диапазон возможных значений, подробно описаны в таблице 4.1 в [1., поэтому мы не будем повторяться. Теперь кратко рассмотрим основные особенности типов данных и сосредоточимся на их возможном применении.

Целочисленные типы

SMALLINT и INTEGER относятся к целочисленным типам. Следует сказать, что SMALLINT представляет собой урезанную версию INTEGER. Его длина составляет 2 байта, в отличие от 4, предназначенных для хранения INTEGER. Обычно не стоит экономить на дисковом пространстве. По этой причине лучше использовать тип INTEGER для хранения целочисленных значений.

Область применения целочисленных типов очевидна: они необходимы для полей, содержащих только целые числа - для хранения счетчиков, номеров и т.д. Обычно тип INTEGER также имеют поля, содержащие первичные ключи.

Вещественные типы данных

Типы FLOAT и DOUBLE PRECISION относятся к вещественным (их также называют числовыми типами с плавающей точкой). Прежде всего, я хочу предостеречь читателя от использования типа FLOAT - его точности недостаточно для хранения большинства дробных значений. Особенно не рекомендуется хранить в нем денежные значения - в переменных типа FLOAT ошибки округления появляются очень быстро, и это может удивить бухгалтера при расчетах. Лучший способ хранения чисел с плавающей точкой (например, в бухгалтерских системах и системах для научных расчетов) в базе данных - хранить их в типе DOUBLE PRECISION.

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

Типы данных с фиксированной точкой

К этим типам данных относятся NUMERIC и DECIMAL. Вопрос о различии между NUMERIC и DECIMAL звучит очень часто. Оба этих типа имеют одинаковую разрядность - от 1 до 18 знаков, одинаковую точность - от нуля до разрядности.

Напомним, что: разрядность - это общее количество цифр, а точность - количество знаков после запятой.

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

Вы легко можете это проверить, запустив утилиту isql и выполнив нижеописанную последовательность действий. Создаем таблицу следующего вида:

SQL> CREATE TABLE test (

CON> Num_field NUMERIC(15,2),

CON> Dec_field DECIMAL(15,2));

Затем даем команду показать структуру таблицы:

SQL> show tables test;

И видим следующее:

NUM_FIELD NUMERIC(15, 2) Nullable

DEC_FIELD NUMERIC(15, 2) Nullable

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

Вот таблица из [1., которая иллюстрирует, как хранятся различные целочисленные типы (таблица 1.1). Как видите, хранение данных в 3-м диалекте отличается для чисел с большой разрядностью:

Таблица 1.1. Хранение чисел с фиксированной точкой

Разрядность Диалект 1 Диалект 3
от 1 до 4 SMALLINT для NUMERIC
INTEGER для DECIMAL
SMALLINT
от 5 до 9 INTEGER INTEGER
от 10 до 18 DOUBLE PRECISION INT64

Теперь мы можем точно сказать, в чем основные различия между типами NUMERIC и DECIMAL: в случае определения поля (или переменной) с малой разрядностью (до четырех) первое хранится как 2-байтовое целое SMALLINT, а второе - как 4-байтовое INTEGER. Таким образом, если разрядность больше четырех, типы DECIMAL и NUMERIC будут эквивалентны.

Обратите внимание на разницу реализации типов с большой разрядностью в первом и третьем диалектах. В первом диалекте число с фиксированной точкой превращалось из целого в вещественное, где применялись механизмы округления. В третьем диалекте эта особенность была устранена - большие целые числа хранятся действительно как целые - с использованием механизма INT64, который может хранить 64-битные числа в диапазоне +/-2^32. Поэтому лучше хранить данные о денежных средствах в базах данных, созданных с использованием 3-го диалекта. Только использование механизма INT64 гарантирует сохранность небольших денежных остатков.

用于存储日期和时间的类型

与4.x和5.x版本相比,InterBase 6.x及其克隆版本中用于存储日期和时间的类型发生了变化。为了避免在这些历史遗留类型中感到困惑,我们将以InterBase第6版的情况来讨论。然后在此基础上简要提及早期版本的情况。这样做是为了那些仍在使用早期版本InterBase的用户。因此,InterBase 6.x中有3种用于存储日期和时间的类型–DATE、TIME和TIMESTAMP。

  • DATE类型存储日期,精确到天。可能值的范围–从公元100年1月1日到32768年2月29日。
  • TIME类型存储时间数据,精确到万分之一秒。可能值的范围–从00:00 AM到23:59.9999 PM。
  • TIMESTAMP类型表示DATE和TIME类型的组合。

如何处理日期?如果问题是在服务器层面(存储过程或触发器)进行工作,一切都非常简单–我们总是可以声明所需类型的变量,并从表中为其赋值,反之亦然。然而,有必要将数据从数据库传输到应用程序,然后再传回来。在这种情况下有两种方法:使用应用InterBase日期原始格式的库来访问这些类型的对象,并将这种格式转换为普通的语言内部日期/时间类型(FIBPlus就是这种库的一个例子),或者使用InterBase内置的将日期转换为字符串的机制。

如果你需要从完整日期中只提取年份或月份,你会怎么做?为此,你必须使用EXTRACT函数组(在InterBase 6.x的所有克隆版本中可用),它允许你从日期中只提取所需的部分。这些函数的使用方式如下:

EXTRACT (MONTH FROM DATE_FIELD)

EXTRACT (YEAR FROM DATE_FIELD)

EXTRACT函数中参数的完整列表如下:YEAR、MONTH、DAY、HOUR、MINUTE、SECOND、WEEKDAY、YEARDAY。它们的功能从其名称即可看出,因此我们在此不再赘述。

用于存储文本的数据类型

InterBase中有两种用于存储文本信息的类型–CHAR和VARCHAR。它们的全称是CHARACTER和CHARACTER VARYING,但没有理由使用长名称–即使是isql工具中的Show tables命令也给出类型的简短名称。

为了定义符号类型的字段或变量,有必要在类型名称后的括号中指定将在所定义对象中使用的符号数量,或者省略符号数量–这样将创建一个长度为1个符号的字段。

CREATE TABLE testCHARLen(

Field1 CHAR(255),

Field2 CHAR);

当创建此表时,Field1的长度将为255个符号,而Field2为1个符号。

CHAR和VARCHAR类型在许多方面相似–两者都可以包含多达32768个符号,但也有一些区别。尽管这两种类型在数据库中的存储方式相同,但InterBase对它们的处理方式不同。以下示例说明了这一点:

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;

结果,我们将得到以下内容:

(Test ) (Test)

如你所见,从c1字段中选择的值’Test’之后有空格。这意味着当从CHAR类型的字段中选择数据时,返回的值会被补充空格直到字段的完整长度。很难想象为什么这种会导致网络流量(网络负载)大幅增长的行为是必要的。无论如何,VARCHAR是推荐使用的符号类型。

符号类型的主要特征之一是其字符集–CHARACTER SET。字符集是为整个数据库定义的,如果创建字段时没有显式重新定义,则默认用于所有符号字段。为了创建带有显式字符集指示的符号字段,有必要在列的描述中添加字符集的描述(在CREATE TABLE或ALTER TABLE语句中)。字符集WIN1251通常用于支持俄语(如果你想详细了解在InterBase中使用俄语的情况,请参阅“InterBase俄语化”一章(第1部分))。以下是一个包含显式描述字符集WIN1251的符号字段的表示例:

CREATE TABLE TestCHARSET(

Field1 VARCHAR(255),

Field2 VARCHAR(255) CHARACTER SET win1251);

这里Field1是没有显式字符集指示的字段,因此创建数据库时指定的字符集将用于它。对于Field2,明确确定它将存储WIN1251编码中的符号。

除了为符号字段指定字符集外,你还可以指定排序规则(collation order),它定义此数据集中的符号将如何排序。对于俄语,有两种排序变体–WIN1251和PXW_CYRL。关于COLLATION ORDER使用的更多细节,请参阅“InterBase俄语化”一章。

你可以在文档[1,第13章]中找到完整的字符集列表及其适用的COLLATION ORDER。

注意!根据InterBase 6的文档,有4种符号类型:除了上述数据类型外,还有2种–NCHAR和NCHAR VARYING,但同一文档下面解释说,后两种类型与CHAR和VARCHAR相同,只是默认使用字符集ISO8859.1。这意味着实际上使用伪类型NCHAR等同于应用CHAR DEFAULT CHARACTER SET ISO8859.1。类似地,对于NCHAR VARYING,只是用VARCHAR代替CHAR。显然,这些伪类型的应用是为西欧和美国的用户设计的,那里创建了字符集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(二进制语言表示,参见术语表和“InterBase数据库结构”一章)等。用户还可以定义可以具有负值的BLOB子类型。过滤器可以应用于每种类型。它将把一个子类型的字段转换为另一个子类型。

应该指出,使用BLOB字段通常是存储相对于数据库而言的外部文件的替代方案。至于BLOB过滤器,由于面向狭窄的任务类别,它们使用得相当少。

数组

InterBase DBMS是最早出现数组的数据库之一。数据库中数组的支持是传统关系模型的扩展。数组的存在允许简化对同一数据类型集合的处理。

数组是同一类型值的集合,具有一个共同的名称,并允许根据其编号访问任何数组元素。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 DBMS的客户端应用程序中并不常用。这是因为Delphi和C++ Builder附带的访问库(如BDE和IBX)无法处理数组。根据InterBase文档,可以使用预处理器gpre处理数组,但这对于Delphi/C++ Builder开发人员来说并不是最方便的方式。幸运的是,在FIBPlus库中支持InterBase中的数组字段。您可以在“FIBPlus特殊功能”一章中详细了解。客户端库IBProvider允许使用Microsoft公司的开发工具创建InterBase客户端应用程序,它也支持数组操作(参见“使用Microsoft OLE DB技术开发InterBase DBMS客户端应用程序”一章(第3部分))。

结论

应当指出,在不提前涉及的情况下很难谈论数据类型,因为它们渗透到与数据库应用程序开发相关的所有关键领域。因此,在阅读本书时,最好将本章作为快速参考,每当您需要刷新InterBase基础知识时都可以查阅。