2

sqlite データベースを使用して、埋め込み C++ アプリケーションに結果を保存しています。

「ドメイン」テーブルと呼ぶ単一列のテーブルがいくつかあり、他のテーブルの列はそれらを外部キーとして参照します。これらは基本的に、初期化時に一度だけ変更される列挙型のテーブルです。たとえば、ステータス データ タイプを格納する 1 つのテーブルを次に示します。

CREATE TABLE status_domain (status TEXT PRIMARY KEY NOT NULL UNIQUE);
INSERT INTO status_domain VALUES ('pending');
INSERT INTO status_domain VALUES ('in_progress');
INSERT INTO status_domain VALUES ('error');
INSERT INTO status_domain VALUES ('complete');  
.
.
CREATE TABLE my_other_table (
    .
    .
    status  TEXT NOT NULL,     
    .
    .
    FOREIGN KEY (status) REFERENCES status_domain(status)
);

ドメイン テーブルの目的は、sqlite の外部キー制約 (参照整合性) を利用することです。

これらのテーブルに書き込む C++ コードは、スキーマを認識しません。これらのテーブルを C++ で複製するのは悪い設計ではないかと思います。例えば:

enum StatusEnum { pending, in_progress, error, complete };

次の 4 つのオプションが表示されます。

  1. 挿入してmy_other_tableいるステータス値が有効かどうかを知らずに挿入します。ステータス値が無効な場合、これは実行時に失敗します。
  2. を C++ 列挙型で複製しstatus_domainて、コンパイラが無効なステータスで挿入を実行できないようにします。スキーマが変更された場合、両方の場所で変更を行う必要があるため、これは DRY の原則に違反しています。
  3. テーブルを破棄status_domainし、C++ 列挙型に有効なデータ型を適用させます。C++ コードは、これらのテーブルに挿入する唯一の場所になるため、これは妥当と思われます。ただし、スキーマで明示的に宣言されたステータス タイプがあると便利です。
  4. sqlite ラッパー コードをよりデータベース/スキーマに対応させます。これは努力する価値がないと思います。

私はオプション 2 に傾いていますが、2 つの異なる場所で変更される可能性のあるものを保存しているため、躊躇しています。

注: 私が共有していない、このような (より長い) テーブルが他にもいくつかあります。

4

3 に答える 3

1

あなたが示したもので、私はオプション 3 (スクラップstatus_domain) に傾倒します。参考までに、そのテーブルが実際に提供するものはわかりません (my_other_table結合やその他の操作に必要な、まだ持っていない関連データはありません)。

my_other_tableあなたはいつでもできる:

status TEXT NOT NULL CHECK (status IN ('pending', 'in_progress', ...))

この目的で文字列を使用する必要もありません。C++ の値は正常に機能し、制約enumによってチェックすることもできます。CHECK

于 2013-01-06T05:05:10.313 に答える
1

データベーススキーマと手続き型言語コードの間の一貫性を維持するというよく知られた問題について話しています。この問題には適切な解決策がありません。Microsoft の EntityFramework のようないくつかのアプローチがあります。それらのどれも完璧ではありません。

次の解決策を検討することをお勧めします。

  1. 列挙型から SQL クエリを生成する C++ コードを記述します (C++ にはリフレクションはありませんが、すべての列挙型メンバーがswitchステートメントに存在することを確認するオプションがあります)。列挙型を変更したら、テーブルを再構築してこのクエリを実行する SQL クエリを再作成する必要があります。

  2. テーブルの新しい状態から列挙型を使用して C++ コードの一部を生成する SQL クエリを記述します。テーブルを変更したら、クエリを再実行し、その後 C++ コードを再コンパイルする必要があります。

これらの手順は完全に自動化されているわけではありませんが、少なくとも従うことができるポリシーを提供します。

于 2013-01-06T04:34:13.100 に答える
0

次に、オプション 2 を実行したいようです。その理由は、共有できないテーブルにあると思います。その場合、GNU autogenを使用して、次のようにします。

status.def

autogen 定義のステータス。

ステータス = { num="1"; 名前="保留中"; };
ステータス = { num="2"; 名前="進行中"; };
ステータス = { num="3"; 名前="エラー"; };
ステータス = { num="4"; 名前="完了"; };

gen.tpl

[+ autogen5 テンプレート
sql=%s.sql
h=%sh
(setenv "SHELL" "/bin/sh") +][+ CASE (サフィックス) +][+ == sql +]
CREATE TABLE status_domain (id INTEGER PRIMARY KEY, status TEXT NOT NULL UNIQUE);
[+ FOR status "\n" +]INSERT INTO status_domain VALUES ([+num+], '[+name+]');[+
ENDFOR+]
CREATE TABLE my_other_table (
     .
     .
    status_domain_id INTEGER、     
     .
     .
    FOREIGN KEY (status_domain_id) REFERENCES status_domain(id)
);
[+ == h +]enum StatusDomainEnum {
[+ FOR status ",\n" +] [+name+] = [+num+][+ENDFOR+]
};
[+ESAC+]

どの出力:

status.h

列挙型 StatusDomainEnum {
    保留中 = 1、
    進行中 = 2、
    エラー = 3、
    完了 = 4
};

status.sql

CREATE TABLE status_domain (id INTEGER PRIMARY KEY, status TEXT NOT NULL UNIQUE);
status_domain 値 (1、「保留中」) に挿入します。
status_domain 値 (2, 'in_progress') に挿入します。
status_domain 値 (3、「エラー」) に挿入します。
status_domain 値 (4、「完了」) に挿入します。
CREATE TABLE my_other_table (
    .
    .
    status_domain_id INTEGER、     
    .
    .
    FOREIGN KEY (status_domain_id) REFERENCES status_domain(id)
);
于 2013-01-07T05:16:08.603 に答える