5

製品、色、およびサイズを制御する 3 つのテーブルがあります。製品には色とサイズがあってもなくてもかまいません。色にはサイズがある場合とない場合があります。

product      color                           size
-------      -------                         -------
id           id                              id
unique_id    id_product (FK from product)    id_product (FK from version)
stock        unique_id                       id_version (FK from version)
title        stock                           unique_id
                                             stock

すべてのunique_idテーブルに存在する列はシリアル型 (オートインクリメント) であり、そのカウンターは 3 つのテーブルで共有され、基本的にそれらの間のグローバル ユニーク ID として機能します。

正常に動作しますが、に基づいていくつかのフィールドを選択する必要がある場合、クエリのパフォーマンスを向上させようとしていますunique_id

unique_id探しているのがどこにあるのかわからないので、UNION以下のように使用しています:

select title, stock
from product 
where unique_id = 10

UNION

select p.title, c.stock
from color c
join product p on c.id_product = p.id
where c.unique_id = 10

UNION

select p.title, s.stock
from size s
join product p on s.id_product = p.id
where s.unique_id = 10;

これを行うより良い方法はありますか?ご提案ありがとうございます。

編集1

@ErwinBrandstetter と @ErikE の回答に基づいて、以下のクエリを使用することにしました。主な理由は次のとおりです。

1)unique_idすべてのテーブルにインデックスがあるため、パフォーマンスが向上します

2) unique_idi を使用すると製品コードが見つかるため、別の単純な結合を使用して必要なすべての列を取得できます

SELECT 

    p.title,
    ps.stock

FROM (

    select id as id_product, stock
    from product 
    where unique_id = 10

    UNION

    select id_product, stock
    from color
    where unique_id = 10

    UNION

    select id_product, stock
    from size
    where unique_id = 10

) AS ps

JOIN product p ON ps.id_product = p.id;
4

5 に答える 5

5

PL/pgSQL 関数

当面の問題を解決するには、次のような plpgsql 関数の方が高速です。

CREATE OR REPLACE FUNCTION func(int)
  RETURNS TABLE (title text, stock int) LANGUAGE plpgsql AS
$BODY$
BEGIN

RETURN QUERY
SELECT p.title, p.stock
FROM   product p
WHERE  p.unique_id = $1; -- Put the most likely table first.

IF NOT FOUND THEN
    RETURN QUERY
    SELECT p.title, c.stock
    FROM   color c
    JOIN   product p ON c.id_product = p.id
    WHERE  c.unique_id = $1;
END;

IF NOT FOUND THEN
    RETURN QUERY
    SELECT p.title, s.stock
    FROM   size s
    JOIN   product p ON s.id_product = p.id
    WHERE  s.unique_id = $1;
END IF;

END;
$BODY$;

OUTパラメーターとの名前の競合を避けるために、テーブル修飾された列名で関数を更新しました。

RETURNS TABLEPostgreSQL 8.4 がRETURN QUERY必要です。バージョン 8.2 が必要です。両方を古いバージョンに置き換えることができます。

関連するすべてのテーブルの列にインデックスを付ける必要があることは言うまでもありません。自動的に索引付けされ、主キーになります。unique_idid


再設計

理想的には、ID だけでどのテーブルかがわかります。1 つの共通シーケンスを使用し続けることができますが100000000、最初のテーブル、2000000002 番目300000000、3 番目のテーブル、またはニーズに合ったものを追加できます。このようにして、数値の最下位部分を簡単に区別できます。

単純な整数は、-2147483648 から +2147483647 までの数値にまたがりbigintます。それが十分でない場合は、に移動してください。integerただし、可能であれば、ID に固執します。bigintまたはよりも小さく、高速ですtext


CTE (実験的!)

何らかの理由で関数を作成できない場合は、この純粋な SQL ソリューションで同様のトリックを実行できます。

WITH x(uid) AS (SELECT 10) -- provide unique_id here
    , a AS (
    SELECT title, stock
    FROM   x, product 
    WHERE  unique_id = x.uid
    )
    , b AS (
    SELECT p.title, c.stock
    FROM   x, color c
    JOIN   product p ON c.id_product = p.id
    WHERE  NOT EXISTS (SELECT 1 FROM a)
    AND    c.unique_id = x.uid
    )
    , c AS (
    SELECT p.title, s.stock
    FROM   x, size s
    JOIN   product p ON s.id_product = p.id
    WHERE  NOT EXISTS (SELECT 1 FROM b)
    AND    s.unique_id = x.uid
    )
SELECT * FROM a
UNION ALL
SELECT * FROM b
UNION ALL
SELECT * FROM c;

期待どおりに追加のスキャンが回避されるかどうかはわかりません。テストする必要があります。このクエリには、少なくとも PostgreSQL 8.4 が必要です。


アップグレード!

先ほど学んだように、OP は PostgreSQL 8.1 で動作します。
単独でアップグレードすると、操作が大幅に高速化されます。


PostgreSQL 8.1 のクエリ

オプションが限られているため、plpgsql 関数は使用できないため、この関数は使用している関数よりも優れたパフォーマンスを発揮するはずです。テストEXPLAIN ANALYZE - v8.1 で利用可能。

SELECT title, stock
FROM   product 
WHERE  unique_id = 10

UNION ALL
SELECT p.title, ps.stock
FROM   product p
JOIN  (
    SELECT id_product, stock
    FROM   color
    WHERE  unique_id = 10

    UNION ALL
    SELECT id_product, stock
    FROM   size
    WHERE  unique_id = 10
    ) ps ON ps.id_product = p.id;
于 2012-07-10T21:55:45.627 に答える
3

再設計の時期だと思います。

ある点では基本的にすべて同じ (SerialNumberItems である) 項目のバーコードとして使用しているものがありますが、他の点では異なるため、複数のテーブルに分割されています。

私はあなたのためにいくつかのアイデアを持っています:

デフォルトの変更

必要な各製品を 1 つのカラー「ノーカラー」と 1 つのサイズ「ノーサイズ」にするだけです。次に、必要な情報を見つけたい任意のテーブルにクエリを実行できます。

スーパータイプ/サブタイプ

あまり変更しなくても、スーパータイプ/サブタイプ データベース設計パターンを使用できます。

その中には、すべての個別の詳細レベル識別子が存在する親テーブルがあり、サブタイプ テーブルの共有列はスーパータイプ テーブルに入ります (すべてのアイテムが同じ方法)。アイテムを区別するさまざまな方法ごとに 1 つのサブタイプ テーブルがあります。サブタイプの相互排他性が必要な場合 (Color または Size を指定できますが、両方を指定することはできません)、親テーブルには TypeID 列が指定され、サブタイプ テーブルには ParentID と TypeID の両方に対する FK があります。あなたの設計を見ると、実際には相互排他性を使用することはありません。

スーパータイプ テーブルのパターンを使用する場合、最初にスーパータイプ、次にサブタイプの 2 つの部分に挿入する必要があるという問題があります。削除する場合も、逆の順序で削除する必要があります。ただし、1 回のクエリでスーパータイプ テーブルから Title や Stock などの基本情報を取得できるという大きなメリットがあります。

挿入、更新、および削除をベース テーブル + 子テーブルの操作に変換する代わりにトリガーを使用して、サブタイプごとにスキーマ バインド ビューを作成することもできます。

より大きな再設計

色とサイズを製品に関連付ける方法を完全に変更できます。

まず、「has-a」のパターンは次のとおりです。

  • 製品(何も持っていません)
  • 商品->色
  • 商品->サイズ
  • 商品->色->サイズ

ここに問題があります。明らかに、製品は他のもの (色とサイズ) を持つメイン アイテムですが、色にはサイズがありません。それは任意の割り当てです。サイズには色があると言った方がいいかもしれませんが、違いはありません。これは、親子タイプの関係で直交データをモデル化しようとしているため、テーブルの設計が最適ではない可能性があることを示しています。実際、製品には ColorAndSize があります。

また、製品に色とサイズがある場合uniqueid、カラー表の は何を意味しますか? このような商品は、サイズがなく、色だけで注文できますか?このデザインは、注文することを決して許可されるべきではないものに (私には思われる) 一意の ID を割り当てています。しかし、この情報をカラー テーブルから見つけることはできません。まず、カラー テーブルとサイズ テーブルを比較する必要があります。問題です。

これを次のように設計します: Table ProductSizeこれまでの製品で可能なすべての異なるサイズをリストした表。Colorこれまでの製品で可能なすべての異なる色をリストした表。OrderableProductProductIdColorIDSizeID、およびUniqueID(バーコード値)を含む表。さらに、各商品には 1 つの色と 1 つのサイズが必要であり、そうでない場合は存在しません。

基本的に、Color と Size は X 座標と Y 座標をグリッドに合わせたようなものです。許容される組み合わせであるボックスに記入しています。どれが行で、どれが列であるかは関係ありません。確かに、一方は他方の子ではありません。

一般に、製品のさまざまなサブグループに適用できる色やサイズについて合理的な規則がある場合、ProductType テーブルと ProductTypeOrderables テーブルには、新しい製品を作成するときに OrderableProduct を設定できるユーティリティがある可能性があります。標準セットを含む表 - カスタマイズすることはできますが、新しく作成するよりも変更する方が簡単な場合があります。または、許容される色とサイズの範囲を定義することもできます。ProductTypeAllowedColor および ProductTypeAllowedSize テーブルを個別に必要とする場合があります。たとえば、T シャツを販売している場合、XXXS、XXS、XS、S、M、L、XL、XXL、XXXL、および XXXXL を許可する必要があります。ただし、ほとんどの商品でこれらすべてのサイズが使用されることはありません。ただし、清涼飲料の場合、サイズは 6 パック 8 オンス、24 パック 8 オンス、2 リットルなどです。

この新しいスキームでは、正しい注文可能な製品を見つけるために照会するテーブルが 1 つだけあります。適切なインデックスを使用すると、非常に高速になるはずです。

あなたの質問

あなたは尋ねました:

PostgreSQL では、unique_id にインデックスを使用すると、満足のいくパフォーマンスが得られると思いますか?

データを繰り返し検索するために使用する列または列のセットには、インデックスが必要です。他のパターンでは、毎回完全なテーブル スキャンが発生し、パフォーマンスが低下します。これらのインデックスを使用すると、テーブルごとにリーフ レベルの読み取りが 1 回しか行われないため、クエリが非常に高速になると確信しています。

于 2012-07-10T22:30:47.420 に答える
1

これは少し異なります。在庫が複数の{product、color、zsize}テーブルに存在する場合、意図した動作がわかりません。(UNIONは重複を削除しますが、たとえば{product_id、stock}タプルなど、全体としての行の場合、それは私には意味がありません。最初のものを取得します。(ファンキーな自己結合に注意してください!!)

SELECT p.title
        , COALESCE (p2.stock, c.stock, s.stock) AS stock
FROM product p
LEFT JOIN product p2 on p2.id = p.id AND p2.unique_id = 10
LEFT JOIN color c on c.id_product = p.id AND c.unique_id = 10
LEFT JOIN zsize s on s.id_product = p.id AND s.unique_id = 10
WHERE COALESCE (p2.stock, c.stock, s.stock) IS NOT NULL
        ;
于 2012-07-11T16:26:48.350 に答える
1

unique_id、すべてのテーブルにインデックスがあり、結合する列にインデックスがある限り、クエリはかなり効率的です。

UNIONこれらを変更することはできますがUNION ALL、このクエリではパフォーマンスに違いはありません。

于 2012-07-10T22:05:37.463 に答える
1

3 つの個別の auto_increment 列を使用して、一意の ID を生成する簡単な方法があります。ID を一意にするために、ID の前に文字を追加するだけです。

色:

 C0000001
 C0000002
 C0000003

サイズ:

 S0000001
 S0000002
 S0000003
 ...

製品:

 P0000001
 P0000002
 P0000003
 ...

いくつかの利点:

  • 一意性を確保するために、テーブル全体で ID の作成をシリアル化する必要はありません。これにより、パフォーマンスが向上します。
  • 実際に文字をテーブルに保存する必要はありません。同じテーブル内のすべての ID は同じ文字で始まるため、数字のみを保存する必要があります。これは、通常のauto_increment列を使用して ID を生成できることを意味します。
  • ID がある場合は、最初の文字を確認するだけで、それがどのテーブルにあるかを確認できます。それが製品 ID なのかサイズなのかを知りたいだけの場合は、データベースにクエリを実行する必要さえありません。 ID。

短所:

  • もはや数字ではありません。ただし、C、S、P の代わりに 1、2、3 を使用することで回避できます。
于 2012-07-10T21:54:04.590 に答える