web-dev-qa-db-ja.com

テーブル列に基づく複数の自動インクリメントID

データベースの設計についてサポートが必要です。

次の表があります。 DB schema

疑似コード:

Table order_status {
  id int[pk, increment]
  name varchar
}

Table order_status_update {
  id int[pk, increment]
  order_id int[ref: > order.id]
  order_status_id int[ref: > order_status.id]
  updated_at datetime
}

Table order_category {
  id int[pk, increment]
  name varchar
}

Table file {
  id int[pk, increment]
  order_id int[ref: > order.id]
  key varchar
  name varchar
  path varchar 
}

Table order {
  id int [pk] // primary key
  order_status_id int [ref: > order_status.id]
  order_category_id int [ref: > order_category.id]
  notes varchar
  attributes json  // no of attributes is not fixed, hence needed a json column
}

すべてが大丈夫でしたが、今度はorder_category_id列の各タイプの自動インクリメントIDが必要です。

たとえば、私が2つのカテゴリの電子機器とおもちゃを持っている場合、私は電子機器1、おもちゃ1、おもちゃ2、電子機器2、電子機器3、おもちゃ3、おもちゃ4、おもちゃ5に関連付けられた値が必要になりますorderテーブルの行。ただし、列のタイプではなく、新しい行ごとに自動インクリメントが行われるため、これは不可能です。

つまり、テーブルorderの代わりに

  id  order_category_id
---------------------
  1       1       
  2       1       
  3       1     
  4       2       
  5       1      
  6       2
  7       1

フォローする必要があります

 id  order_category_id pretty_ids
----------------------------
  1       1       toy-1
  2       1       toy-2
  3       1       toy-3
  4       2       electronics-1
  5       1       toy-4
  6       2       electronics-2
  7       1       toy-5

私が試したもの:

注文カテゴリごとに個別のテーブルを作成しました(理想的なソリューションではありませんが、現在6つの注文カテゴリがあるため、今のところ機能します)

これで、electronics_ordertoys_orderのテーブルができました。カラムは反復的ですが、機能します。しかし、今、私は別の問題を抱えています。他のテーブルとのすべての関係が台無しになりました。 electronics_ordertoys_ordersは同じIDを持つことができるため、id列を使用してorder_status_updateorder_statusfileテーブルを参照することはできません。これらの各テーブルに別の列order_categoryを作成できますが、それは正しい方法ですか?私はデータベース設計の経験がありませんので、他の人がどうやってそれをやっているのか知りたいです。

私も副質問があります

名前を保存するためだけにorder_categoryorder_statusのテーブルが必要ですか?これらの値はあまり変化せず、コードに格納してorderテーブルの列に保存できるためです。

個別のテーブルは柔軟性に優れていることはわかっていますが、orderテーブルに新しい行を挿入する前に、データベースを2回クエリしてorder_statusおよびorder_categoryを名前でフェッチする必要がありました。そして後で、orderテーブルをクエリするための複数結合になります。

-

それが役に立った場合、私はデータベースサーバーとしてフラスコとsqlalchemyを使用し、postgresqlを使用しています。

5
Jashwant

Toys-1 toys-2 toys-3を実行するには、_order_status update_のロジックを繰り返す必要があります。時間またはカウントによって、いくつかのstatusを追跡するのに違いはありません。 _order_status update_の場合、now()をupdated_atに入れるだけで簡単です。たとえば、_order_category_track_を使用すると、最後の値+ 1を取得するか、それぞれ異なるシーケンスを使用します(推奨されません)。これは、データベースオブジェクトとDB内のデータをバインドするためです。スキーマを次のように変更します enter image description here

このスキーマでは、矛盾した状態になっている可能性があります。しかし、あなたのアプリケーションでは、私の意見では、3つの異なるエンティティ「order」、「order_status」、「order category track」があり、それらは自分の生活を送っています。

そしてそれでも、たとえばロックなしでこのタスクの一貫した状態を達成することはほとんど不可能です。このタスクは、次の行がSQLと矛盾する以前の行に依存するという条件によって複雑になります。

1
Vladimir

カテゴリを2レベルの階層に分割することをお勧めします:カテゴリ(おもちゃ、電子)とサブカテゴリ(おもちゃ1、おもちゃ2、電子1など):

したがって、列order_subcategory.full_nameを使用して、コンパイルされた「toy-1」値を含めるか、ビューを作成してこのフィールドをオンザフライで作成できます。

select oc.name || "-" || os.number
from order_category as oc 
join order_subcategory as os on oc.id = os.category_id

enter image description here

https://dbdiagram.io/d/5dd6a132edf08a25543e34f8

「名前を保存するためだけにorder_categoryとorder_statusのテーブルが必要ですか?」という質問について:この種のデータを別のディクショナリテーブルとして保存することをお勧めします。一貫性と信頼性を提供します。これらのテーブルのクエリは、RDBMSにとって非常に高速で簡単なので、自由に使用してください。

0
Alex

ここでは、orderorder_statusorder_categoryの3つのテーブルのみに焦点を当てます。新しいレコードの新しいテーブルを作成するのは正しい方法ではありません。説明として、orderテーブルとorder_categoryテーブルを多対多の関係で使用しようとしていると思います。その場合、必要なのは次のようなピボットテーブルです。 table relationship

現在、注文テーブルにorder_status列を追加しています。必要に応じて、これらのテーブルの1つにこの列を追加できます。
副質問:order_statusの場合、注文ステータスが固定されている場合、(ACTIVEINACTIVEのように、それができない将来的にはより多くの値になる)ENUMタイプの列を使用する方が良いでしょう。

0
Kyi Moe Min

簡単な答えは、質問に直接答えることです。しかし、私はそれがこの場合良いことだとは思いません。だから私はそうしません。多分全体の概念が間違っていると思います。

まず最初に、ビジネスニーズとアサーションを明確にします。

  • 1つの注文に複数のカテゴリを含めることができます
  • 1つのカテゴリが複数の注文に関係する場合があります

  • 1つの注文は一度に1つのステータスしか持てませんが、複数のステータスがあります。

  • 1つのステータスを複数の注文で使用できます

  • 1つの注文がファイルに対応している(おそらく請求証明)

  • 1つのファイルは1つの注文のみに関係します

第二:アドバイス

第三:構想ソリューション enter image description here

これは通常、良いスタートを切るのに十分です。でももう少し楽しみたい:)だから...

第4:必要なパフォーマンスに関する質問

  • 1日あたりの負荷の見積もり(月あたり1000万行?)

第5:物理的なソリューションの提案

  • 別のテーブルスペースでのアーカイブ(キャンセルまたは終了したときにトリガー=>アーカイブ)
  • 別のテーブルスペースのインデックス(dbaはそれを感謝します)
  • 注文テーブルの可能なパーティション分割( https://pgxn.org/dist/pg_partman/doc/pg_partman.htmlhttps://www.postgresql.org/docs/current/ddl -partitioning.html
  • ハードウェアとオプションの選択(可用性が高いか、災害管理か?ある場合:詳細な調査はさらに必要ですが、ほとんどありません)
  • データの転置(本当に必要ですか?必要な場合:詳細な調査はさらに必要ですが、ほとんどありません)

最終的なコードダウン! (良い音楽で)

-- as a postgres user
CREATE DATABASE command_system;
CREATE SCHEMA in_prgoress_command;
CREATE SCHEMA archived_command;
--DROP SCHEMA public;
-- create tablespaces on other location than below
CREATE TABLESPACE command_indexes_tbs location 'c:/Data/indexes';
CREATE TABLESPACE archived_command_tbs location 'c:/Data/archive';
CREATE TABLESPACE in_progress_command_tbs location 'c:/Data/command';

CREATE TABLE in_prgoress_command.command
(
    id bigint /*or bigserial if you use a INSERT RETURNING clause*/ primary key
    , notes varchar(500)
    , fileULink varchar (500)
)
TABLESPACE in_progress_command_tbs;

CREATE TABLE archived_command.command
(
    id bigint /*or bigserial if you use a INSERT RETURNING clause*/ primary key
    , notes varchar(500)
    , fileULink varchar (500)
)
TABLESPACE archived_command_tbs;

CREATE TABLE in_prgoress_command.category
(
    id int primary key
    , designation varchar(45) NOT NULL
)
TABLESPACE in_progress_command_tbs;
INSERT INTO in_prgoress_command.category 
VALUES (1,'Toy'), (2,'Electronic'), (3,'Leather'); --non-exaustive list

CREATE TABLE in_prgoress_command.status
(
    id int primary key
    , designation varchar (45) NOT NULL
)
TABLESPACE in_progress_command_tbs;

INSERT INTO in_prgoress_command.status 
VALUES (1,'Shipping'), (2,'Cancel'), (3,'Terminated'), (4,'Payed'), (5,'Initialised'); --non-exaustive list

CREATE TABLE in_prgoress_command.command_category
(
    id bigserial primary key
    , idCategory int 
    , idCommand bigint
)
TABLESPACE in_progress_command_tbs;

ALTER TABLE in_prgoress_command.command_category
ADD CONSTRAINT fk_command_category_category FOREIGN KEY (idCategory) REFERENCES in_prgoress_command.category(id);

ALTER TABLE in_prgoress_command.command_category
ADD CONSTRAINT fk_command_category_command FOREIGN KEY (idCommand) REFERENCES in_prgoress_command.command(id);

CREATE INDEX idx_command_category_category ON in_prgoress_command.command_category USING BTREE (idCategory) TABLESPACE command_indexes_tbs;
CREATE INDEX idx_command_category_command ON in_prgoress_command.command_category USING BTREE (idCommand) TABLESPACE command_indexes_tbs;

CREATE TABLE archived_command.command_category
(
    id bigserial primary key
    , idCategory int 
    , idCommand bigint
)
TABLESPACE archived_command_tbs;

ALTER TABLE archived_command.command_category
ADD CONSTRAINT fk_command_category_category FOREIGN KEY (idCategory) REFERENCES in_prgoress_command.category(id);

ALTER TABLE archived_command.command_category
ADD CONSTRAINT fk_command_category_command FOREIGN KEY (idCommand) REFERENCES archived_command.command(id);

CREATE INDEX idx_command_category_category ON archived_command.command_category USING BTREE (idCategory) TABLESPACE command_indexes_tbs;
CREATE INDEX idx_command_category_command ON archived_command.command_category USING BTREE (idCommand) TABLESPACE command_indexes_tbs;

CREATE TABLE in_prgoress_command.command_status
(
    id bigserial primary key
    , idStatus int 
    , idCommand bigint
    , change_timestamp timestamp --anticipate if you can the time-zone problematic
)
TABLESPACE in_progress_command_tbs;

ALTER TABLE in_prgoress_command.command_status
ADD CONSTRAINT fk_command_status_status FOREIGN KEY (idStatus) REFERENCES in_prgoress_command.status(id);

ALTER TABLE in_prgoress_command.command_status
ADD CONSTRAINT fk_command_status_command FOREIGN KEY (idCommand) REFERENCES in_prgoress_command.command(id);

CREATE INDEX idx_command_status_status ON in_prgoress_command.command_status USING BTREE (idStatus) TABLESPACE command_indexes_tbs;
CREATE INDEX idx_command_status_command ON in_prgoress_command.command_status USING BTREE (idCommand) TABLESPACE command_indexes_tbs;
CREATE UNIQUE INDEX idxu_command_state ON in_prgoress_command.command_status USING BTREE (change_timestamp, idStatus, idCommand) TABLESPACE command_indexes_tbs;

CREATE OR REPLACE FUNCTION sp_trg_archiving_command ()
    RETURNS TRIGGER
language plpgsql
as $function$
DECLARE
BEGIN
    -- Copy the data
    INSERT INTO archived_command.command
    SELECT *
    FROM in_prgoress_command.command
    WHERE new.idCommand = idCommand;    

    INSERT INTO archived_command.command_status (idStatus, idCommand, change_timestamp)
    SELECT idStatus, idCommand, change_timestamp
    FROM in_prgoress_command.command_status
    WHERE idCommand = new.idCommand;    

    INSERT INTO archived_command.command_category (idCategory, idCommand)
    SELECT idCategory, idCommand
    FROM in_prgoress_command.command_category
    WHERE idCommand = new.idCommand;    

    -- Delete the data
    DELETE FROM in_prgoress_command.command_status
    WHERE idCommand = new.idCommand;    
    DELETE FROM in_prgoress_command.command_category
    WHERE idCommand = new.idCommand;    
    DELETE FROM in_prgoress_command.command
    WHERE idCommand = new.idCommand;    
END;
$function$;

DROP TRIGGER IF EXISTS t_trg_archiving_command ON in_prgoress_command.command_status;
CREATE TRIGGER t_trg_archiving_command
AFTER INSERT
ON in_prgoress_command.command_status
FOR EACH ROW
WHEN (new.idstatus = 2 or new.idStatus = 3)
EXECUTE PROCEDURE sp_trg_archiving_command();

CREATE TABLE archived_command.command_status
(
    id bigserial primary key
    , idStatus int 
    , idCommand bigint
    , change_timestamp timestamp --anticipate if you can the time-zone problematic
)
TABLESPACE archived_command_tbs;

ALTER TABLE archived_command.command_status
ADD CONSTRAINT fk_command_command_status FOREIGN KEY (idStatus) REFERENCES in_prgoress_command.category(id);

ALTER TABLE archived_command.command_status
ADD CONSTRAINT fk_command_command_status FOREIGN KEY (idCommand) REFERENCES archived_command.command(id);

CREATE INDEX idx_command_status_status ON archived_command.command_status USING BTREE (idStatus) TABLESPACE command_indexes_tbs;
CREATE INDEX idx_command_status_command ON archived_command.command_status USING BTREE (idCommand) TABLESPACE command_indexes_tbs;
CREATE UNIQUE INDEX idxu_command_state ON archived_command.command_status USING BTREE (change_timestamp, idStatus, idCommand) TABLESPACE command_indexes_tbs;

結論:

  • 多くの場合、キーの配置が心配なのは、キーが適切な場所にないためです。車も同じです! :D
  • どんな解決策も予言的解決策としてとらないでください:それをベンチマークしてください。
0
Jaisus