web-dev-qa-db-ja.com

この大きなテーブルを3つの小さなテーブルに分割する必要がありますか?

ユーザーがアップロードした写真に関する情報を含むPostgreSQLデータベースを設計しています。すべてのユーザーは、少なくとも1枚のメイン写真と、オプションで1枚以上のパブリックおよび/またはプライベート写真を持っています。私の最初のスキーマは次のとおりです:

user
----
id (PK)

photo
-----
id (PK)
user_id   (FK to user)
photo_id  (unique identifier such as "aK1q9")
type      ("main" or "other")
access    ("public" or "private")

最も実行されるクエリは次のとおりです。

SELECT p.photo_id FROM photo p INNER JOIN user u ON p.user_id = u.id WHERE p.type = 'main' AND u.id = (some user id);

次に人気のあるクエリは次のとおりです。

SELECT p.photo_id FROM photo p INNER JOIN user u ON p.user_id = u.id WHERE p.type = 'other' AND p.access = 'public' AND u.id = (some user id);

私が予測する問題は、Photoテーブルにはすべてのユーザーがアップロードしたすべての公開写真と非公開写真が含まれるため、時間の経過とともに非常に大きくなることです。最も人気のあるクエリはメインの写真IDのみを検索するため、写真のテーブルを3つのテーブルに分割する方が理にかなっていますか?

main_photo
----------
id (PK)
user_id   (FK to user but in a one-to-one relationship to user)
photo_id

other_public_photo
------------------
id (PK)
user_id   (FK to user)
photo_id

other_private_photo
-------------------
id (PK)
user_id   (FK to user)
photo_id

私はこの後者のスキーマが望ましいと思います。1)各写真のタイプとアクセス情報は、保存場所によって明示的になり、クエリ内の追加のANDを排除します。 2)クエリが1つの巨大なテーブルではなく3つの小さなテーブルのいずれかに対して実行されるため、クエリの実行が速くなります。パフォーマンスの観点から最適なアプローチはどれですか?

ありがとう。

5
Ray

これはデータベース技術というよりは設計上の問題だと思います。それで、私は答えに現在どのDBテクノロジーが取り組んでいるかを提供しました。しかし、あなたはそれを疑似を使って説明することを好むので、ここで別の答えに行きます。 SQL Serverの人がこのページにアクセスした場合、彼がそれを高く評価できるように、私たちは決して知りません。

テーブル名:ユーザー
理由:要件は、常にメインの写真をユーザーに関連付けることです。ユーザーのレコードとともにメインの写真IDの参照のみを保存するのが理にかなっています。

Columns
----------------
UserId (PK)
PrimaryPhotoId (FK to Photo table)

テーブル名:写真
説明:単一のテーブルを管理する方がはるかに簡単です。ツールチップ、Altテキスト、画像の説明などの詳細情報を保存する場合に備えて、将来このテーブルの列を拡張することは間違いなく可能です。他のテーブルは影響を受けず、まったく変更を必要としません。 。

Columns
----------------
PhotoId (PK)

テーブル名:UserPhoto
説明:この表は、1対多の関係を持つ柔軟性を提供します。また、フラグ(IsPrivate列)を使用して、すべての写真がプライベートかパブリックかを示すレコードも識別します。すべてのユーザーが同じ写真を2度関連付けないように制限したい場合は、提案された複合一意キーが意味をなします。そうでない場合は、一意キーを拡張する必要がありますが、テーブルのデザインは同じままにする必要があります。

Columns
----------------
UserPhotoId (PK, AutoIncrement)
UserId (FK to User table)       - Composite Unique Key
PhotoId (FK to Photo table)     - Composite Unique Key
IsPrivate (Bit Data type to store 0 or 1. 0 value represents for 'Public', 1 value represents for 'Private' photo)
0
Coder Absolute

@CoderAbsoluteの答えはあなたのテーブルに良いデザインを与えます。 なぜこのアプローチの方が良いのか詳細に触れなかったため、別の答えを追加する価値があると思いました。

まず、データがどのように適合するかに従ってテーブル構造を設計します。種類の異なるものを1つのテーブルにまとめようとしないでください。同じ種類のレコードに複数のテーブルを追加しないでください。 「正規化」してみてください-テーブルが同じ情報を何度も繰り返す場合は、その情報を別のテーブルに移動し、最初のテーブルから外部キーを使用してリンクします。最初の正規形、2番目の正規形、3番目の正規形が何であるかを知っておく必要があります。実際のデータベースの多くはこれらの標準に完全には適合していませんが、何を目指すべきかを認識することで、よりクリーンな設計を行うことができます。

正しい設計が完成するまで、最適化については心配しないでください。まず、テーブルにいくつのエントリがあるかまだわかりません。第2に、データベースエンジンは、多数のエントリがある場合でも、クエリをできるだけ高速にするように設計されています。データベースソフトウェアの開発者を推測してはいけません。

本当にボトルネックがあることがわかったら、まずindexingを確認する必要があります。データベーステーブルは、配列のようなものと考えています。実際には、各行に1つずつ、「ブロブ」の束として保存できます。これは、mightすべてがディスク上の異なる場所にあり、必ずしもそうとは限りません順番に。 (これはDB内部の非常に正確な技術的説明ではありません。しかし、ピースがどのように組み合わされるかをより明確に示すはずです。)データベースはどのようにすべてのblobを見つけるのですか?どこかに、各blobの検索場所を指示する「ポインター」のリストがあります。そのため、通常、テーブルのすべての行を見つけることは効率的なプロセスです。リストを調べて、それぞれを記録します。

ここで、特定のユーザーのすべての写真を最も一般的に取得するとします。 Photoテーブルのすべての行を調べ、UserIdフィールドを確認する必要があるため、これは処理が遅くなる可能性があります。この場合、そのフィールドにインデックスを追加する必要があります。インデックスはルックアップテーブルのようなものです。これにより、ソフトウェアは指定されたUserIdを持つすべての行の場所をすばやく見つけることができます。したがって、すべての行を順番にチェックする必要はありません。

インデックスにはさまざまな種類があります。一部は、特定の値に一致するように最適化されています。これはおそらくUserId列に必要なタイプです。ある値よりも大きいまたは小さいものを見つけるために最適化されているものもあります。これはタイムスタンプに意味があります。 (先月のすべての写真を教えてください。)定期的にクエリを実行する場合は、複数の列にインデックスを付けることができます。または、1つ以上の列の一部の関数にインデックスを付けることもできます。一部のインデックスは、同様のアイテムがディスク上で互いに近くに格納されていることを意味し、キャッシュを利用してより迅速に取得します。

DBMSの可能性について理解しておく必要があります。正確なスピードアップは設定とハードウェア構成にも依存するため、実験もここで非常に価値があります。

2
jwg

SQL Server 2012では、このようなスキーマを設計しました。

メインの写真はユーザーと密接に関連付けられています。

CREATE TABLE [dbo].[User](
        [UserId] [INT] IDENTITY(1,1) NOT NULL,
        [PrimaryPhotoId] [INT] NOT NULL,
     CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED 
    (
        [UserId] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]

    GO

    ALTER TABLE [dbo].[User]  WITH CHECK ADD  CONSTRAINT [FK_User_Photo] FOREIGN KEY([PrimaryPhotoId])
    REFERENCES [dbo].[Photo] ([PhotoId])
    GO

    ALTER TABLE [dbo].[User] CHECK CONSTRAINT [FK_User_Photo]
    GO

写真情報を保持する一般的なテーブル。将来的には、ツールチップ、ALTテキスト、説明などの情報を保持することを希望する場合があり、他の2つのテーブルに触れる必要はありません。

CREATE TABLE [dbo].[Photo](
        [PhotoId] [INT] IDENTITY(1,1) NOT NULL,
     CONSTRAINT [PK_Photo] PRIMARY KEY CLUSTERED 
    (
        [PhotoId] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]

アクセス識別子に基づいてオプションの写真を保持するテーブル。

CREATE TABLE [dbo].[UserPhoto](
    [UserPhotoId] [INT] IDENTITY(1,1) NOT NULL,
    [UserId] [INT] NOT NULL,
    [PhotoId] [INT] NOT NULL,
    [IsPrivate] [BIT] NOT NULL,
 CONSTRAINT [PK_UserPhoto] PRIMARY KEY CLUSTERED 
(
    [UserPhotoId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[UserPhoto]  WITH CHECK ADD  CONSTRAINT [FK_UserPhoto_Photo] FOREIGN KEY([PhotoId])
REFERENCES [dbo].[Photo] ([PhotoId])
GO

ALTER TABLE [dbo].[UserPhoto] CHECK CONSTRAINT [FK_UserPhoto_Photo]
GO

上記のスキーマ設計に基づいて、次のクエリが役に立ちます!

--To get primary photo listing only
SELECT  Photo.*
FROM    [User]
        INNER JOIN Photo ON [User].PrimaryPhotoId = Photo.PhotoId;

--To get optional photo listing (just for PRIVATE)
SELECT  *
FROM    UserPhoto
        INNER JOIN Photo ON UserPhoto.PhotoId = Photo.PhotoId
                            AND UserPhoto.IsPrivate = 1

--To get optional photo listing (just for PUBLIC)
SELECT  *
FROM    UserPhoto
        INNER JOIN Photo ON UserPhoto.PhotoId = Photo.PhotoId
                            AND UserPhoto.IsPrivate = 0

--To get optional photo listing
SELECT  *
FROM    UserPhoto
        INNER JOIN Photo ON UserPhoto.PhotoId = Photo.PhotoId

写真を1つのテーブルに保持し、他のテーブルを参照することは問題ないと思います。それが極端に大きくなる場合は、パーティショニングの概念について考えてください。 UserPhotoテーブルで使用できるインデックスについては考慮していません。

1
Coder Absolute