Veri türleri
NOTICE: Bu belge, Alexey Kovyazin ve Serg Vostrikov tarafından yazılan “The InterBase World” kitabından bir bölümdür.
Veri türleri dokümantasyonda ayrıntılı olarak açıklanmış olsa da (bkz. [1, bölüm 4.), bu kitabın sonraki bölümlerinde sıkça kullanılacak bazı kavramları ele almak gerekir. Genel bilgilerin yanı sıra, InterBase veritabanlarında veri türlerinin kullanımına ilişkin birkaç örnek ve bunların kullanımı ile dönüştürülmesi hakkında öneriler de incelenecektir. 1. ve 3. InterBase veritabanı lehçesinde var olan veri türü farklılıklarını ayrıntılı olarak ele alacağız.
Veriler hakkında
Veri türleri, herhangi bir programlama dilinin veya herhangi bir DBMS sunucusunun temel öğeleridir. InterBase bir istisna değildir. Veritabanının bazı bilgileri depoladığını söylediğimizde, bu bilgilerin tek bir yerde saklanamayacağını anlamalıyız. Tam tersine; veriler sınıflandırılmalı ve kendi “rafına” yerleştirilmelidir. Veri türleri, belirli bir “rafa” neyin konulabileceğini ve neyin konulamayacağını tanımlar. “Raf” hakkında konuştuğumuzda, her şeyden önce veritabanı tablolarının alanlarını (bkz. “Tablolar. Birincil anahtarlar ve üreteçler” (bölüm 1)) ve ayrıca tetikleyiciler ve saklı prosedürler içindeki değişkenleri kastediyoruz. Her veri türünün, bu türün değerleri üzerinde yürütülebilecek bir dizi işlemi vardır. Bu nedenle, bir veritabanı tasarlarken doğru veri türünü seçmek önemlidir. Bu, istemci programları geliştirirken birçok sorunun önlenmesine yardımcı olacaktır. InterBase’de, geliştiricinin veri depolama gereksinimlerini karşılayabilecek 12 veri türü vardır. Bu türler koşullu olarak aşağıdaki 6 gruba ayrılır:
- tam sayıları depolamak için - INTEGER ve SMALLINT;
- gerçek sayıları depolamak için - FLOAT ve DOUBLE PRECISION;
- sabit hassasiyetli sayıları depolamak için - NUMERIC ve DECIMAL;
- tarih, saat ve zaman damgasını depolamak için - DATE, TIME ve TIMESTAMP;
- sembolleri depolamak için - CHARACTER (kısaltılmış haliyle - CHAR) ve VARYING CHARACTER (VARCHAR);
- dinamik olarak genişleyen verileri depolamak için - BLOB
Ayrıca, ilkel türlerin değer dizilerini tanımlamak mümkündür, yani BLOB hariç tüm sıralanan türler.
InterBase veri türlerinin çoğu, SQL92 standardında tanımlanan türlere karşılık gelir; ancak bunun dışında kendine özgü özellikleri de vardır - ilkel veri türlerinin dizileri ve BLOB. InterBase’deki diziler, tek bir alanda aynı türden bir dizi veri içerebilir. Örneğin, INTEGER türünde değerler dizisi tanımlayabiliriz. Diziler birden çok boyuta sahip olabilir. BLOB veri türü, dinamik olarak genişleyen bir veri türüdür ve adı genellikle Binary Large OBject olarak deşifre edilir. BLOB’un, daha sonra tüm çağdaş SQL sunucularına yayılan ve yerleşen InterBase geliştiricilerinin bir icadı olduğu söylenmelidir.
Veri türlerini tanımlama sözdizimi
Veri türleri, tablolardaki alanları, tetikleyicilerdeki ve saklı prosedürlerdeki değişkenleri tanımlamak için kullanılır. InterBase’deki tüm olası veri türlerini tanımlamanın genel sözdizimi aşağıda verilmiştir.
< 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])]
Boyut, hassasiyet ve olası değer aralığı gibi veri türü özellikleri, [1., tablo 4.1’de ayrıntılı olarak açıklanmıştır, bu nedenle burada kendimizi tekrarlamayacağız. Şimdi veri türlerinin ana özelliklerini kısaca gözden geçirelim ve olası uygulamalarına odaklanalım.
Tam sayı türleri
SMALLINT ve INTEGER tam sayı türlerine aittir. SMALLINT’in, INTEGER’ın sakatlanmış bir sürümünü temsil ettiğini söylemeliyim. Uzunluğu, INTEGER depolamak için ayrılan 4 baytın aksine 2 bayttır. Genellikle disk alanından tasarruf etmemelisiniz. Bu nedenle, tam sayı değerlerini depolamak için INTEGER türünü kullanmak daha iyidir.
Tam sayı türlerinin uygulama alanı açıktır: yalnızca tam sayılar içeren alanlar için gereklidirler - sayaçları, numaraları vb. depolamak için. Genellikle INTEGER türü, birincil anahtarları içeren alanlara da sahiptir.
Gerçek veri türleri
FLOAT ve DOUBLE PRECISION türleri gerçek olanlarla ilgilidir (bunlara kayan noktalı sayı türleri de denir). İlk olarak, okuyucuyu FLOAT türünü kullanmaya karşı uyarmak istiyorum - hassasiyeti, çoğu kesirli değeri depolamak için yeterli değildir. Özellikle para değerlerini içinde depolamak önerilmez - FLOAT türü değişkenlerinde yuvarlama hataları çok hızlı ortaya çıkar ve bu, hesaplamalar yaparken bir muhasebeciyi şaşırtabilir. Bir veritabanında kayan noktalı sayıları (örneğin, muhasebe sistemlerinde ve bilimsel hesaplama sistemlerinde) depolamanın en iyi yolu, bunları DOUBLE PRECISION türünde depolamaktır.
3. lehçedeki InterBase’de, 64 baytlık sabit uzunluklu noktaya sahip türleri depolamak için bir mekanizma olduğunu dikkate almalısınız. Bu mekanizma para değerlerini depolamak için kullanılır. Bu türlerin kullanımı en iyi hassasiyeti sağlar.
Sabit noktalı veri türleri
NUMERIC ve DECIMAL bu veri türlerine aittir. NUMERIC ve DECIMAL arasındaki farkla ilgili soru çok sık sorulur. Bu her iki tür de aynı basamak kapasitesine sahiptir - 1’den 18’e kadar işaret, aynı hassasiyet - sıfırdan basamak kapasitesine kadar.
Hatırlatalım ki: basamak kapasitesi, arasındaki toplam basamak sayısıdır ve hassasiyet - virgülden sonraki işaret sayısıdır.
En komik olan şey, bu türlerin dokümantasyona göre maksimum basamak kapasitesinde farklılık göstermesidir, ancak aslında neredeyse aynı şekilde gerçekleştirilirler ve aralarında hiçbir fark yoktur.
Bunu, isql yardımcı programını başlatıp aşağıda belirtilen işlem sırasını yaparak kolayca kontrol edebilirsiniz. Aşağıdaki görünümde bir tablo oluşturuyoruz:
SQL> CREATE TABLE test (
CON> Num_field NUMERIC(15,2),
CON> Dec_field DECIMAL(15,2));
Ardından tablonun yapısını göstermek için bir komut veriyoruz:
SQL> show tables test;
Ve şunu görüyoruz:
NUM_FIELD NUMERIC(15, 2) Nullable
DEC_FIELD NUMERIC(15, 2) Nullable
Gördüğünüz gibi, InterBase verilen her iki sütunun da NUMERIC türünde olduğunu bildirir. Bu davranışın nedenleri, sabit noktalı veri türlerinin gerçekleştirilmesinde yatmaktadır. Mesele şu ki, InterBase’in herhangi bir tam sayı ifadesini depolamak için yalnızca üç mekanizması vardır ve tüm türler, ne adlandırılırsa adlandırılsın, bu varyantlara göre gerçekleştirilir.
İşte [1.‘den, çeşitli tam sayı türlerinin (tablo 1.1) nasıl depolandığını gösteren tablo. Gördüğünüz gibi, 3. lehçede veri depolama, büyük basamak kapasiteli sayılar için farklıdır:
Tablo 1.1. Sabit noktalı sayıların depolanması
| Basamak kapasitesi | Lehçe 1 | Lehçe 3 |
|---|---|---|
| 1’den 4’e | NUMERIC için SMALLINT DECIMAL için INTEGER |
SMALLINT |
| 5’ten 9’a | INTEGER | INTEGER |
| 10’dan 18’e | DOUBLE PRECISION | INT64 |
Şimdi NUMERIC ve DECIMAL türleri arasındaki ana farkların ne olduğunu kesin olarak söyleyebiliriz: küçük basamak kapasiteli (dörde kadar) bir alan (veya değişken) tanımlanması durumunda, ilki 2 baytlık tam sayı SMALLINT olarak, ikincisi ise 4 baytlık INTEGER olarak depolanır. Bu nedenle, basamak kapasitesi dörtten fazlaysa, DECIMAL ve NUMERIC türleri eşdeğer olacaktır.
Birinci ve üçüncü lehçelerde büyük basamak kapasiteli türlerin gerçekleştirilmesindeki farklılığa dikkat edin. Birinci lehçede, sabit noktalı sayı tam sayıdan gerçek sayıya dönüştürülür ve burada yuvarlama mekanizmaları uygulanırdı. Üçüncü lehçede bu özellik ortadan kaldırılmıştır - büyük tam sayılar gerçekten tam sayı olarak depolanır - +/-2^32 aralığında 64 bitlik sayıları depolayabilen INT64 mekanizması kullanılarak. Bu nedenle, 3. lehçe kullanılarak oluşturulan veritabanlarında para fonlarıyla ilgili verileri depolamak daha iyi olacaktır. Yalnızca INT64 mekanizmasını kullanmak, küçük para bakiyelerinin güvenliğini garanti edecektir.
Veri ve zaman depolama türleri
Tarih ve saat depolama türleri, InterBase 6.x sürümünde ve klonlarında 4.x ve 5.x ile karşılaştırıldığında değişmiştir. Bu türlerle ilgili tarihsel ağlarda kafa karışıklığı yaşamamak için, InterBase 6. sürümündeki durumu ele alacağız. Ardından buna dayanarak daha önce ne olduğunu kısaca belirteceğiz. Bu, hala InterBase’in erken sürümlerini kullanan kullanıcılar için yapılır. Yani, InterBase 6.x’te tarih ve saat depolamak için 3 tür vardır - DATE, TIME ve TIMESTAMP.
- DATE türü, tarihleri gün hassasiyetinde depolar. Olası değer aralığı - 1 Ocak 100 MS’den 29 Şubat 32768’e kadar.
- TIME türü, saniyenin on binde biri hassasiyetinde zaman verilerini depolar. Olası değer aralığı - 00:00 AM’den 23:59.9999 PM’e kadar.
- TIMESTAMP türü, DATE ve TIME türlerinin bir kombinasyonunu temsil eder.
Tarihlerle nasıl çalışılır? Sorun sunucu düzeyinde saklı prosedürlerde veya tetikleyicilerde çalışmakla ilgiliyse, her şey oldukça basittir - gerekli türde bir değişken her zaman bildirebilir ve tablolardan ayarlayabiliriz ve bunun tersini de yapabiliriz. Ancak, verileri bir veritabanından uygulamaya ve geri aktarmak gerekir. Bu durumda iki yaklaşım vardır: bu türlerin nesnelerine erişmek için InterBase tarihlerinin orijinal biçimini uygulayan ve bu biçimi sıradan dil içi tarih / saat türlerine dönüştüren kitaplıkları kullanmak (FIBPlus böyle bir kitaplığın örneğidir) veya InterBase’e gömülü tarihleri satırlara dönüştürme mekanizmasını kullanmak.
Tam tarihten yalnızca yılı veya ayı çıkarmanız gerekirse ne yapacaksınız? Bu amaçla, tarihten yalnızca gerekli kısmı çıkarmanıza izin veren bir EXTRACT fonksiyon grubu kullanmanız gerekecektir (InterBase 6.x’in tüm klonlarında mevcuttur). Bu fonksiyonlar şu şekilde kullanılır:
EXTRACT (MONTH FROM DATE_FIELD)
EXTRACT (YEAR FROM DATE_FIELD)
EXTRACT fonksiyonundaki parametrelerin tam listesi şudur: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, WEEKDAY, YEARDAY. İşlevleri adlarından anlaşılır, bu nedenle bunları burada yorumlamayacağız.
Metin depolama veri türleri
InterBase’de metin bilgilerini depolamak için iki tür vardır - CHAR ve VARCHAR. Tam adları - CHARACTER ve CHARACTER VARYING’dir, ancak uzun adları kullanmak için bir neden yoktur - isql yardımcı programındaki Show tables komutu bile türlerin kısa adlarını verir.
Sembolik türde bir alan veya değişken tanımlamak için, tür adından sonra parantez içinde tanımlanan nesnede kullanılacak sembol sayısını belirtmek veya sembol sayısını atlamak gerekir - böylece 1 sembol uzunluğunda bir alan oluşturulur.
CREATE TABLE testCHARLen(
Field1 CHAR(255),
Field2 CHAR);
Bu tablo oluşturulduğunda, Field1 255 sembol uzunluğunda ve Field2 - 1 sembol olacaktır.
CHAR ve VARCHAR türleri birçok yönden benzerdir - her ikisi de 32768 sembole kadar içerebilir, ancak bazı farklılıklar vardır. Bu her iki tür de bir veritabanında aynı şekilde depolanmasına rağmen, InterBase onlarla farklı şekilde çalışır. Aşağıdaki örnek bunu gösterir:
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;
Sonuç olarak, şunu elde ederiz:
(Test ) (Test)
Gördüğünüz gibi, c1 alanından seçilen ‘Test’ değerinden sonra boşluklar vardı. Bu, CHAR türündeki bir alandan veri seçerken, döndürülen değerin alanın tam uzunluğuna kadar boşluklarla tamamlandığı anlamına gelir. Ağ trafiğinde (ağ yükünde) önemli bir artışa yol açan bu davranışın neden gerekli olduğunu varsaymak zordur. Her durumda, VARCHAR kullanılması önerilen sembolik türdür.
Sembolik türün en önemli özelliklerinden biri karakter setidir - CHARACTER SET. Karakter seti tüm veritabanı için tanımlanır ve bir alan oluşturulurken açıkça yeniden tanımlanmazsa tüm sembolik alanlar için varsayılan olarak kullanılır. Karakter setinin açıkça belirtildiği bir sembolik alan oluşturmak için, bir sütunun tanımına (CREATE TABLE veya ALTER TABLE cümlelerinde) bir karakter seti açıklaması eklemek gerekir. Rusça desteği için genellikle WIN1251 karakter seti kullanılır (Rusça’nın InterBase’de kullanımı hakkında ayrıntılı bilgi edinmek istiyorsanız “InterBase Ruslaştırması” (bölüm 1) bölümüne bakın). İşte açıkça tanımlanmış WIN1251 karakter setine sahip sembolik bir alan içeren bir tablo örneği:
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.