データベース

2010年3月 2日 (火)

データ型のマップの続き

以前に、DB2というよりODBCのSQLデータ型やCデータ型にまつわるあれこれ(憂鬱)について色々書いたが、

各DBMSのデータ型がどのようにODBCのSQLデータ型にマップされるか調べてみたdashdash。ちなみに、ODBCでは「ドライバ」毎の独自のSQLデータ型を定義する事を認めている(その場合、どっかに申請しないといけない。つまり、値が衝突しない)。また、「ドライバ」毎と強調したのは、例えば、DB2用のODBCドライバといってもIBM純正のドライバの他にサードパーティ用のドライバがあったりするので注意。

まずは、DB2 Ver 09.07.0000。ドライバはIBM純正のDB2CLI.DLL Ver 09.07.0000。

DB2
DBMSのデータ型 ODBCのSQLデータ型
SMALLINT SQL_SMALLINT
INTEGER SQL_INTEGER
BIGINT SQL_BIGINT
DECIMAL SQL_DECIMAL
REAL SQL_REAL
DOUBLE SQL_DOUBLE
DECFLOAT *SQL_DECFLOAT(-360)
CHAR(FOR BIT DATA) SQL_CHAR(SQL_BINARY)
VARCHAR(FOR BIT DATA) SQL_VARCHAR(SQL_VARBINARY)
LONG VARCHAR(FOR BIT DATA) SQL_LONGVARCHAR(SQL_LONGVARBINARY)
CLOB *SQL_CLOB(-99)
GRAPHIC *SQL_GRAPHIC(-95)
VARGRAPHIC *SQL_VARGRAPHIC(-96)
LONG VARGRAPHIC *SQL_LONGVARGRAPHIC(-97)
DBCLOB *SQL_DBCLOB(-350)
BLOB *SQL_BLOB(-98)
DATE SQL_TYPE_DATE
TIME SQL_TYPE_TIME
TIMESTAMP SQL_TYPE_TIMESTAMP
DATALINK *SQL_DATALINK(-450)
XML *SQL_XML(-370)
UDT *SQL_USER_DEFINED_TYPE(-450)

以後、*はドライバ独自のSQLデータ型を表す。

CHAR、VAR CHAR、LONG VARCHARはFOR BIT DATAオプションを付けるとバイナリ型にマップされる。

次はPostgreSQL Ver 8.3.9。ドライバはPSQLODBC35W.DLL Ver 08.04.0100。

PostgreSQL
DBMSのデータ型 ODBCのSQLデータ型
smallint SQL_SMALLINT
integer SQL_INTEGER
bigint SQL_BIGINT
numeric SQL_NUMERIC
real SQL_REAL
double precision SQL_FLOAT
serial SQL_INTEGER
bigserial SQL_BIGINT
money SQL_FLOAT
"char" SQL_WCHAR
character SQL_WCHAR
character varying SQL_WVARCHAR
text SQL_WLONGVARCHAR
bytea SQL_VARBINARY
timestamp with time zone SQL_TYPE_TIMESTAMP
timestamp without time zone SQL_TYPE_TIMESTAMP
date SQL_TYPE_DATE
time with time zone SQL_WVARHCAR
time without time zone SQL_TYPE_TIME
interval SQL_WVARCHAR
boolean SQL_WVARCHAR
point SQL_WVARCHAR
line SQL_WVARCHAR
lseg SQL_WVARCHAR
box SQL_WVARCHAR
path SQL_WVARCHAR
polygon SQL_WVARCHAR
circle SQL_WVARCHAR
cidr SQL_WVARCHAR
inet SQL_WVARCHAR
macaddr SQL_WVARCHAR
bit SQL_WVARCHAR
bit varying SQL_WVARCHAR
xml SQL_WLONGVARCHAR
uuid SQL_GUID
tsvector SQL_WVARCHAR
tsquery SQL_WVARCHAR
array SQL_WVARCHAR

独自のSQLデータ型は全く定義しておらず、ODBC標準のSQLデータ型にないデータ型はSQL_WVARCHARなどにマップしてるっぽい。どうりで、ヘッダファイルが見つからなかったわけだ・・?。

次はSQL Server 2008 Ver 10.00.1600。ドライバはMS純正のNative Clientのsqlncli10.dll Ver 10.00.1600。

SQL Server
DBMSのデータ型 ODBCのSQLデータ型
tinyint SQL_TINYINT
smallint SQL_SMALLINT
int SQL_INTEGER
bigint SQL_BIGINT
decimal SQL_DECIMAL
smallmoney SQL_DECIMAL
money SQL_DECIMAL
bit SQL_BIT
real SQL_REAL
float SQL_FLOAT
char SQL_CHAR
varchar SQL_VARCHAR
text SQL_LONGVARCHAR
nchar SQL_WCHAR
nvarchar SQL_WVARCHAR
ntext SQL_WLONGVARCHAR
binary SQL_BINARY
varbinary SQL_VARBINARY
image SQL_LONGVARBINARY
date SQL_TYPE_DATE
time *SQL_SS_TIME2(-154)
smalldatetime SQL_TYPE_TIMESTAMP
datetime SQL_TYPE_TIMESTAMP
datetime2 SQL_TYPE_TIMESTAMP
datetimeoffset *SQL_SS_TIMESTAMPOFFSET(-155)
cursor ?
timestamp SQL_BINARY
hierarchyid *SQL_SS_UDT(-151)
uniqueidentifier SQL_GUID
sql_variant *SQL_SS_VARIANT(-150)
xml *SQL_SS_XML(-152)
table *SQL_SS_TABLE(-153)
udt *SQL_SS_UDT(-151)

と、ざっと見る限り、独自のSQLデータ型の値は衝突してないし。なるへそ。

ちなみに、これらマッピングは設定で変えられたり、データベースの文字コードによって、変わるかもしれないので・・

また、Cデータ型については独自のCデータ型を認めていないと書いてあるんだが、DB2やSQL Serverの独自のSQLデータ型に対応する独自のCデータ型がそれぞれ定義されているdashdash。独自のCデータ型を定義できるようになったのは最新のODBC 3.8 かららしいが・・

後はOracleとMySQLとFirebirdぐらいは調べたいんだが、いずれ気が向いたらインストールして調べるかdash


2009年9月24日 (木)

ODBCヘッダー

また、さぼってたwobbly。なんというかプログラミングに対する情熱?自体が最近なくなってきたwobbly。要するに作りたいものがなくなってきた。

そんな事はさておき、今回は以前やりかけの事がたくさんあると書いたが、その内のとりあえず公開できそうな物を公開してみる。ということで、今回はODBC(Open DataBase Connectivity)のDelphi用ヘッダーファイル。Cのヘッダーファイルを以前に頑張って移植したbearing

まぁ、今更ODBC?みたいな感があるが・・・Unicodeに対応するDelphi2009以前のDelphi用ですのであしからず。また、ODBCの関数を使う時は現状、LoadLibraryで自分でDLLをロードして下さいsad。ダウンロードはSkyDrive から。

本当はODBCのコンポーネントを作っていたのだが、まだ、公開できる状態ではないので、ヘッダーファイルだけでも・・・

ところでDelphiの最新版であるDelphi 2010が発売されたのであるが、Unicodeアプリを作れる環境がほしいので久しぶりに購入しようかなと思ってたり。でも、作りたいものがなくなってきたこともあり悩んでます。

2009年8月 8日 (土)

間隔(インターバル)型

ちょっと、データベースのデータ型のお話。

SQL92では2つの日時の差つまり期間を表すデータ型として間隔(インターバル)型が定義されているが、これらは次の2つに分類される。

  • 年月間隔(year-month間隔)
  • 日時間隔(day-time間隔)

年月間隔とは2つの日時の差を年、月で表すデータ型で例えば、「123年4ヶ月」、「48ヶ月」は年月間隔である。同様に、日時間隔は2つの日時の差を日、時、分、秒で表すデータ型で、「235日」、「64時間23分12秒」などは日時間隔である。Oracleでは年月間隔の間隔型としてINTERVAL YEAR TO MONTH型、日時間隔の間隔型としてINTERVAL DAY TO SECOND型が用意されている。DB2には残念ながら間隔型は用意されていない。

と、これら間隔型を理解するのはそう難しくないと思うが、1点注意する事がある。それは、年月間隔に属する間隔型と日時間隔に属する間隔型には互換性がないのが基本?である。つまり、年月間隔に属する間隔型と日時間隔に属する間隔型を比較したり、相互に変換できないのである。一見すると

年月間隔の「123年4ヶ月」>日時間隔の「36ヶ月」

のように比較したり、

年月間隔の「1年」->日時間隔の「365日」

のように変換できそうだが、できないのである。

なぜできないのかは、少し考えてみれば分かるように、月によって日数(28,29,30,31日)が変わったり、平年とうるう年では日数が違うからである。つまり、年月間隔の「1ヶ月」といっても1月なら31日あるし、平年の2月なら29日あるし、日時間隔の日数に変換しようとしても決定できないのである。

と当たり前の事を書いてみる。

最後に実際にOracleで年月間隔と日時間隔の間隔を比較してみる。

Interval1_1

月によって日数が変わるといっても、年月間隔の「12年」と日時間隔の「36日」じゃどう転んでも、「12年」の方が長いのであるが、年月間隔と日時間隔は互換性がないのでエラーになるのである。

2008年6月 7日 (土)

数値型の憂鬱(2)

前回の続きである。

高精度の数値の演算つまり多倍長演算のルーチンを自作するのもいいのだが、なるべくなら既存の信頼におけるものを使いたいものである。Googleなどの検索サイトで多倍長演算ライブラリを検索すれば、様々なライブラリが見つかり、開発環境の言語に合わせて選択できるが、WindowsのOS自体に用意されているライブラリを使う事もできる。

正確に言うと、WindowsのOS自体というより、WindowsのOLEオートメーションで主に使われるバリアントのために用意されているライブラリで、oleaut32.dll

  • VarDecAdd(加算)
  • VarDecSub(減算)
  • VarDecMul(乗算)
  • VarDecDiv(除算)
  • VarDecAbs(絶対値)
  • VarDecFix
  • VarDecInt
  • VarDecRound
  • VarDecNeg
  • VarDecCmp(比較)

などのAPIを使って多倍長演算を行うことができる。これらのAPIは、主に、DECIMAL構造体へのポインタを引数に取り、DECIMAL構造体は次のように宣言されている。

Numeric2_1_3 

wReservedフィールドはバリアントに格納されているデータの型を表し、Decimal型の場合、vt_decimal(14)、scaleフィールドは小数点の位置を表し、signフィールドを符号を表し、hi32、low32、mid32フィールドにスケーリングされた数値が整数値として格納される。

これとODBCを組み合わせてもよいのだが、上述のDecimal構造体のスケーリングされた整数値を格納する領域が96ビットなので、2の96乗つまり最大精度28桁までの数値しか表現できないwobbly。28桁だと最大精度38桁のOracleのNUMBER型はもとよりDB2のNUMBER型すら完全に表現できない・・・

んー。OSで多倍長演算ももっとサポートしてほしいのだが・・あまり、そのような要望はないのだろうか?

2008年6月 6日 (金)

数値型の憂鬱

今回は数値型の憂鬱である。

たいていのデータベースには、高精度の数値を表すデータ型が用意されている。DB2で言えば、DECIMAL/NUMERIC型で、最大精度31桁の巨大な数値を表現することができる。何が憂鬱かというと、これら高精度の数値をクライアント・アプリケーションで入出力するだけなら、高精度の数値を文字列として入出力すれば楽であるが、クライアント・アプリケーションでこられの数値を演算したり比較したりするとなると、途端に面倒になるのである。

というのも、たいていのクライアント・アプリケーションの開発環境では、このような高精度の数値を表現するデータ型がデフォルトで用意されていないからである(JavaならBigIntegerBigDecimalクラス、.NETならDecimal型が用意されているが・・)。

ちなみに、ODBCではこれら高精度の数値を表現するCデータ型としてSQL_C_NUMERICが用意されていて、SQL_NUMERIC_STRUCT構造体として入出力することもできる。SQL_NUMERIC_STRUCT構造体は次のようになる。

Numeric1_3   

precisionフィールドは有効桁数、scaleフィールドは小数点の位置、signフィールドは符号を表し、valフィールドにはスケールされた数値(整数値として)が格納される。ちなみに、valフィールドは16バイト長なので、2の128(16*8)乗つまり最大精度約38桁までの数値を表現できることになる。

と、高精度の数値を表すCデータ型が用意されているのであるが、加減乗除などの演算や比較を行うためのルーチンが用意されていないので、自前で用意するなどしなければならず頭が痛いのであるsad。ちなみに、SQL_NUMBER_STRUCTの使い方はここ

2008年5月17日 (土)

文字列型の憂鬱(3)

前回、データベースの文字コードとクライアントアプリケーションの文字コードが異なると文字コードの変換が発生し、文字列データの長さが拡張または縮小する可能性があるので、切り捨てが発生しないように必要なバッファ量を見積もるのが多少面倒であることを書いたが、そのような事を考える必要なしに文字列データを取得する方法がある。

SQLGetData関数を使う方法である。

SQLGetData関数は通常、長いデータと呼ばれるSQL_LONGVARCHARやSQL_LONGVARBINARYのSQLデータ型のデータを分けて取り出すために使われる(SQL_INTEGERやSQL_TYPE_TIMESTAMP等の固定長データもこの関数を使って取り出すことができるが、分けて取り出す事はできない)が、この関数を使い長いデータを取り出す時と同様に、SQLGetData関数がSQL_SUCCESSを返すまで複数回呼び出して取り出せば、取り出したデータを組み立てる必要はあるが、文字列データの拡張や縮小といった問題を考える必要なく取り出すことができる。

と、この関数を使ってデータを取り出す方が楽に思えるが、SQLGetData関数を使って取り出す列は通常は、SQLBindCol関数でバインドしてはならず、また、列番号の小さい列から大きい列に向かって、つまり、昇順に取り出さなければいけない等の制限があり、別の問題が発生するのである。頭痛い・・wobblyちなみに、どのような制限が課せられるかは、ドライバ次第で、この情報はSQLGetInfo関数(InfoType=SQL_GET_DATA_EXTENSIONS)で取得できる。

2008年5月15日 (木)

文字列型の憂鬱(2)

以前の文字列型の憂鬱の続きである。

文字列型の憂鬱では、文字列型と言うより、可変長データ型(文字列型やバイナリ型)の悩ましさ?を書いたが、バイナリ型ではない可変長データ型の文字列型のデータの送受信には、更に悩ましいことがある。

例えば、DB2でCHARACTER(6)として宣言された列は、SQLデータ型としてSQL_CHARとして報告される。SQLDescribeColまたはSQLColAttributes関数で列のサイズを問い合わせると、6が返されるので、データの切り捨てが発生しないように取り出すには7バイトのバッファを割り当れば良いように思える(+1バイトはNULL終端文字の領域で基本的にCLI/ODBCでSQL_C_CHARなどの文字列のCデータ型にマップする場合はNULL終端文字の領域が必要)。

が、話はこう単純ではないのである。

というのもDB2に限った話ではないが、データベースの文字コード(DB2で言えば、データベース・コードページ)とクライアントアプリケーションの文字コード(同じくアプリケーション・コードページ)が異なると通常、データベースによって文字コードの変換が行なわれ、文字列データのバイト長が縮小または拡張する可能性があるからである。

例えば、データベースの文字コードがShiftJISでアプリケーションの文字コードがEUC-JPの場合、全角文字の「あいう」はデータベースの文字コードShiftJISでは6バイト長(2バイト文字*3文字)であるが、アプリケーション側でデータを取り出すとEUC-JPの9バイト長(3バイト文字*3文字)に拡張するのである。よって、7バイトのバッファしか割り当ててないと切り捨てが発生するのである。要するに、SQLDescribeColまたはSQLColAttributes関数で返されるサイズとはあくまでもデータベースの文字コードにおけるサイズなのである。

と切り捨てが発生しないように文字列型のデータに必要な最大バッファ量を見積もるのは面倒なのである。

ちなみに、DB2 CLIでは、上記の問題に対し、専用のSQLGetSQLCAという関数を使って、文字列データの送受信時に、どれくらいの拡張または縮小する可能性があるかを示す拡張係数(Expansion Factor)というものを取得でき、これを使って、最大バッファ量を簡単に見積もれるようになっている。例えば、拡張係数が2であれば、最大でデータ長が2倍に拡大する可能性があるので、

(SQLDescribeColで返されるサイズ + 1)*2(拡張係数)バイト

のバッファを割り当てればよいことになる。また、SQLGetSQLCAでアプリケーション・コードページやデータベース・コードページも取得できる(SQLGetInfoでも取得できる)が、それらのコードページからクライアントアプリケーションでどれくらい拡張・縮小するかを見積もれないこともないが、面倒なので素直に拡張係数を使った方が無難である。

文字列って本当にめんどいsad

2007年11月16日 (金)

SQLGetInfo

今回はDB2 CLI/ODBCのSQLGetInfo関数である。ODBCは接続先のデータベースの種類によらない汎用的なアプリケーション・プログラミング・インターフェースを開発者に提供するために開発されたインターフェースであるので、実際の接続先のデータソースやドライバに関する情報を返す関数が用意されている。それが、SQLGetInfo関数である。

SQLGetInfo関数の呼び出し時に第2引数として指定するパラメータ(InfoType)の値により様々な情報が取得できる。指定できる値に例えば、次のようなものがある。

  • SQL_DATABASE_NAME
  • SQL_DBMS_NAME
  • SQL_DBMS_VER

  • SQL_TXN_CAPABLE
  • SQL_DEFAULT_TXN_ISOLATION
  • SQL_TXN_ISOLATION_OPTION
  • SQL_SCROLL_OPTIONS
  • SQL_CURSOR_COMMIT_BEHAVIOUR
  • SQL_CURSOR_ROLLBACK_BEHAVIOUR

上記は用意されている値のほんの一部である。最初の3つはDBMS Prodcut Informationと呼ばれる値で、SQL_DATABASE_NAMEは接続中のデータベースの名前、SQL_DBMS_NAMEは接続中のデータベース管理システムの名前、SQL_DBMS_VERはそのバージョンを表す情報を返す。

残りは、Data Source Informationと呼ばれる値で、SQL_TXN_CAPABLEはトランザクションの能力、SQL_DEFAULT_TXN_ISOLATIONはデフォルトのトランザクションの分離レベル、SQL_TXN_ISOLATION_OPTIONはサポートしているトランザクションの分離レベル、SQL_SCROLL_OPTIONSはサポートしているカーソルの種類(前方専用、静的、キーセット駆動、動的カーソルなど)を、SQL_CURSOR_COMMIT_BEHAVIOUR、SQL_CURSOR_ROLLBACK_BEHAVIOURは、トランザクションのコミットまたはロールバック時に、既存のオープン中のカーソルがどう振舞うかを表す情報を返す。

で、実際にDB2 Express CのSAMPLEデータベースに接続して上記の値を指定して情報を取得してみた。

Sqlgetinfo_2 

上のイメージより、サポートしている分離レベルはREAD UNCOMITTED、READ COMMITTED、REPETABLE READ、SERIALIZABLEのすべて、サポートしているカーソルの種類は前方専用、静的、キーセット駆動の3つ。また、SQL_CURSOR_COMMIT_BEHAVIOURのSQL_SC_PRESERVEは、トランザクションのコミット時にオープン中のカーソルが保存され、コミット後も引き続きフェッチできることを表す。同様に、SQL_CURSOR_ROLLBACK_BEHAVIOURのSQL_CB_CLOSEはロールバック時には、オープン中のカーソルが閉じられることを表す。前後するが、SQL_TXN_CAPABLEのSQL_TC_ALLはトランザクションにDML文のみならず、DDL文を含むことができることを表す(DB2はDDL文含むことできる??)。

と、様々な情報が取得できる。

2007年10月21日 (日)

文字列型の憂鬱

先に日付・時刻型の事を書いたが、日付・時刻型より頻繁に使われるであろう文字列型も悩ましいのである。ODBCでは文字列を表すSQLデータ型として、

  • SQL_CHAR(SQL_WCHAR)
  • SQL_VARCHAR(SQL_WVARCHAR)
  • SQL_LONGVARCHAR(SQL_WVARCHAR)

が用意されている。これらに対応するアプリケーション側のCデータ型としてSQL_C_CHARがある。日付・時刻型と違い専用の構造体は用意されておらず、CやC++で文字列を扱う時と同様に、char *として扱う。括弧内はUnicode文字バージョンのSQLデータ型である。

名前を見れば分かるように、SQL_CHARは固定長の文字列、SQL_VARCHARは可変長の文字列、SQL_LONGVARCHARはより長い可変長の文字列を表すSQLデータ型で、ドライバ依存だが、DB2 CLIでは、DB2のCHAR型はSQL_CHAR、VARCHAR型はSQL_VARCHAR、LONG VARCHAR型はSQL_LONGVARCHAR、CLOB型はSQL_CLOBとして報告される。データベースのデータ型をODBCのどのSQLデータ型にマップするかは、ドライバ依存で、バイト数もしくは文字数がN以上ならSQL_LONGVARCHARにマップしなければいけないとかそのような規則はMicrosoftのODBCのヘルプを見る限りなさそうである。が、慣習的にLOB型などの非常に大きなサイズを許可するデータ型は、ほとんどのODBCドライバはLONGが付くSQL_LONGVARCHARなどにマップするようである。もしくは、DB2 CLIのように独自のSQLデータ型に(LOBの場合、SQL_CLOB、SQL_BLOB、SQL_DBLOB)。

悩ましいのここからで、列のデータを取り出すのに割り当てるメモリのサイズをどうすればいいかで頭を痛める。

例えば、可変長のデータ型に限ったことではないが、SQL_VARCHARのSQLデータ型として報告される列を取り出す場合を考える。SQL文をユーザーが発行できるツールなど汎用的なアプリの場合、事前に実際の列データのサイズを知ることはできないので、データの切り捨てが発生しないよう普通は列の情報を問い合わせたときに返される最大サイズ分のメモリ領域を割り当てる。想像通り、返される最大サイズが小さければいいが、2GBとか巨大なサイズが返されると・・しかも、可変長のデータ型の場合、最大サイズが巨大でも、実際に格納されている列データのサイズが小さいと、メモリの無駄である。

と色々悩ましいのである。ちなみに、DB2のCHAR、VARCHARやLONG VARCHAR型は定義できる最大サイズが約32KBなので、返される最大サイズ分のメモリを一度に割り当てて列データを取り出しても問題ないとは思う。DB2で問題なのは最大サイズが約2GBのCLOB型などのLOB型のデータ型である。

これらのDB2のデータ型に対して、DB2 CLIはLOBデータを操作するためにLOBロケータというものを用意しアプリケーション側から効率的にLOBデータにアクセスできるようになっている(他のデータベースにもあるが)。LOBロケータとしてマップするためのDB2 CLI独自のCデータ型として

  • SQL_C_CLOB_LOCATOR
  • SQL_C_BLOB_LOCATOR
  • SQL_C_DBLOB_LOCATOR

が用意され、これらのC言語における定義はSQLINTEGERつまり4バイトの整数値となっている。

つまり、アプリケーション側が見れば、LOBロケータはそれらを識別する単なる4バイトの整数値として扱え、LOBデータを取り出す時に、とりあえず、LOBロケータを格納する4バイトのメモリ領域だけ割り当て、そこにLOBロケータを取り出し、実際にそのLOBロケータによって参照されるLOBデータを取り出す必要がある時に、最大のサイズではなく実際のLOBデータのサイズだけを返すDB2 CLI専用の関数を呼び、必要最小限のメモリだけを割り当てて取り出すことができる(もちろん、分割して取り出すこともできる)。実際のLOBデータが巨大だったら、まぁ、どうにかしないといけないが・・・・

また、ODBCには大きなサイズのデータを扱うための仕組みが用意され、それを使うこともできるが、専用の関数を使った方がプログラム的には楽である(と思う・・)。

2007年10月20日 (土)

日付・時刻型の憂鬱

DB2 CLIつまりODBCでは、日付・時刻に関連するデータベース側のデータ型(ODBCではSQLデータ型と呼ぶ)として、

  • SQL_TYPE_DATE
  • SQL_TYPE_TIME
  • SQL_TYPE_TIMESTAMP
  • SQL_INTERVALで始まる間隔型

が用意されている。

SQL_TYPE_DATEは年、月、日を要素として持つ日付を表すSQLデータ型で、データをアプリケーション側のデータ型(ODBCでは、Cデータ型と呼ぶ)にマップする時に、SQL_C_TYPE_DATEという、Cデータ型としてマップすることができる。SQL_C_TYPE_DATEというCデータ型は、以下のように定義されている。

Odbcdate

上記の構造体にマップすることができる。また、文字列(char *)を表すCデータ型であるSQL_C_CHARにもマップすることもできる。上記のSQL_C_TYPE_DATEにマップする場合、32ビット環境では、2バイト*3フィールド=6バイトのメモリ領域があればいいが、文字列としてSQL_C_CHARにマップする場合、切り捨てが発生しないよう日付を文字列として表現できる十分なメモリを確保する必要がある(日付の区切り文字等を考慮しないといけない)。

SQL_TYPE_TIMEは、時、分、秒を要素として持つ時刻を表すSQLデータ型でSQL_TYPE_DATE同様、Cデータ型として、SQL_C_TYPE_TIMEというデータ型が用意されている。

Odbctime_2

ちなみに、秒は、小数部を持つことができる。

以下同様、SQL_TYPE_TIMESAMPSQL_C_TYPE_TIMESTAMPというCデータ型が用意されている。

Odbctimestamp

で、前回書いたように、DB2のデータ型であるTIMEはSQL_TYPE_TIME、DATEはSQL_TYPE_DATE、TIMESTAMPはSQL_TYPE_TIMESTAMPとして報告されるのである。

悩ましいのはここからで、自分が使ってるデータベースのフレームワークでは、さらに、フレームワークのデータ型にマップさせなければいけないのだが、例えば、フレームワークに用意されている日付型の範囲は0001/01/01~9999/12/31までで、他方、上記のDATE_STRUCT構造体のyearフィールドの型を見ると、SQLSMALLINTで紀元前も許可するではないか・・と、重箱の隅をつつくような事で悩んでいるのである(時刻型の秒の小数点以下の桁数も同様)。幸い、DB2のDATE型は0001年から9999年までであるが・・・(OracleのDATE型は紀元前も許可する)。で、日付・時刻の精度について色々調べたら、こんなブログを発見。では、日付型をフレームワークの文字列型にマップしてはどうか?と考えると、大半の他のアプリケーションがそうであるように、Windowsのロケールの設定から日付や時刻の区切り文字を取得して、文字列を組み立てたくなる(フレームワークの日付・時刻型はそうなっている)。更に本格的にやるとなると、表示後、Windowsのロケールの設定が変更されたら、それを感知し文字列を再組み立てしたくもなる(フレームワークはそうしてる)。

とまぁ、なんて賢いフレームワークなんだ!!と。

ちなみに、DB2には、2つの日付・時刻の差を表す間隔型はないが、ODBCではSQL_INTERVALという接頭辞で始まるSQLデータ型が用意されている。対応するCデータ型もSQL_C_INTERVALという接頭辞が付いており、構造体が用意されている。あいにく、自分の使っているフレームワークではそのような型は用意されていないので、文字列として扱う必要がある。

と色々、汎用的なアプリを作ろうとなると、データベースとアプリケーション側でのデータ型の表現能力の違いで色々悩ましいのである。

より以前の記事一覧

自作ソフトウェア

無料ブログはココログ

メモ