web-dev-qa-db-ja.com

データの整合性のために正規化を犠牲にする必要がありますか?

私はしばしば次の課題に直面し、データの整合性を強化するためにテーブル設計を非正規化することになります。正規化と整合性の両方を実行するデータをモデル化する別の方法があるかどうか知りたいです。

以下は、典型的な(簡略化された)例です。

create table [ProductType]
(
    [ProductTypeId] INT identity(1,1) not null,
    [ProductTypeName] nvarchar(100) not null, 
    constraint [PK_ProductType] primary key ([ProductTypeId])
)

create table [Product]
(
    [ProductId] INT identity(1,1) not null,
    [ProductTypeId] int not null,
    [ProductName] nvarchar(100) not null,
    constraint [PK_Product] primary key ([ProductId]), 
    constraint [AK_Product] unique ([ProductId], [ProductTypeId]),
    constraint [FK_Product_ProductType] foreign key ([ProductTypeId]) references [ProductType]([ProductTypeId])
)

create table [ProductTypeProperty]
(
    [PropertyId] INT identity(1,1) not null,
    [ProductTypeId] int not null,
    [PropertyName] nvarchar(100) not null,
    constraint [PK_ProductTypeProperty] primary key ([PropertyId]), 
    constraint [AK_ProductTypeProperty] unique ([PropertyId], [ProductTypeId]),
    constraint [FK_ProductTypeProperty_ProductType] foreign key ([ProductTypeId]) references [ProductType]([ProductTypeId])
)

create table [ProductPropertyValue]
(
    [ProductId] INT not null,
    [PropertyId] INT not null,
    [ProductTypeId] int not null,
    [PropertyValue] nvarchar(100) not null,
    constraint [PK_ProductPropertyValue] primary key ([ProductId], [PropertyId]),
    constraint [FK_ProductPropertyValue_ProductTypeProperty] foreign key ([PropertyId], [ProductTypeId]) references [ProductTypeProperty]([PropertyId], [ProductTypeId]), 
    constraint [FK_ProductPropertyValue_Product] foreign key ([ProductId], [ProductTypeId]) references [Product]([ProductId], [ProductTypeId])
)

SET IDENTITY_INSERT [dbo].[ProductType] ON
INSERT [dbo].[ProductType] ([ProductTypeId], [ProductTypeName]) VALUES (1, N'Clothing')
INSERT [dbo].[ProductType] ([ProductTypeId], [ProductTypeName]) VALUES (2, N'Drink')
SET IDENTITY_INSERT [dbo].[ProductType] OFF

SET IDENTITY_INSERT [dbo].[ProductTypeProperty] ON
INSERT [dbo].[ProductTypeProperty] ([PropertyId], [ProductTypeId], [PropertyName]) VALUES (1, 1, N'Colour')
INSERT [dbo].[ProductTypeProperty] ([PropertyId], [ProductTypeId], [PropertyName]) VALUES (2, 1, N'Size')
INSERT [dbo].[ProductTypeProperty] ([PropertyId], [ProductTypeId], [PropertyName]) VALUES (3, 2, N'Volume')
SET IDENTITY_INSERT [dbo].[ProductTypeProperty] OFF

SET IDENTITY_INSERT [dbo].[Product] ON
INSERT [dbo].[Product] ([ProductId], [ProductTypeId], [ProductName]) VALUES (1, 1, N'T-shirt')
INSERT [dbo].[Product] ([ProductId], [ProductTypeId], [ProductName]) VALUES (2, 2, N'Milk')
SET IDENTITY_INSERT [dbo].[Product] OFF

INSERT [dbo].[ProductPropertyValue] ([ProductId], [PropertyId], [ProductTypeId], [PropertyValue]) VALUES (1, 1, 1, N'Red')
INSERT [dbo].[ProductPropertyValue] ([ProductId], [PropertyId], [ProductTypeId], [PropertyValue]) VALUES (1, 2, 1, N'XL')
INSERT [dbo].[ProductPropertyValue] ([ProductId], [PropertyId], [ProductTypeId], [PropertyValue]) VALUES (2, 3, 2, N'1 pint')

-- NOTE THAT THE FKS ON [PRODUCTPROPERTYVALUE] MEAN YOU CANNOT RUN EITHER OF THESE
-- WHICH TRY TO ASSIGN A PROPERTY TO A PRODUCT THAT DOESN'T BELONG TO ITS TYPE
INSERT [dbo].[ProductPropertyValue] ([ProductId], [PropertyId], [ProductTypeId], [PropertyValue]) VALUES (1, 3, 1, N'Red')
INSERT [dbo].[ProductPropertyValue] ([ProductId], [PropertyId], [ProductTypeId], [PropertyValue]) VALUES (2, 2, 1, N'XL')

これが私がモデル化しようとしているものです:

  1. 商品には商品タイプがあります(「衣類」、「ドリンク」など)。
  2. 製品タイプにはいくつかの特性があります(たとえば、「衣類」には「色」と「サイズ」があります)。
  3. 製品には、そのタイプに属するすべてまたはすべてのプロパティのプロパティ値があります
  4. 製品は、そのタイプに属さないプロパティのプロパティ値を持つことはできません

ポイント4は、問題を引き起こすポイントです。これを達成するための外部キーを作成するために([FK_ProductProperty_ProductTypeProperty]および[FK_ProductPropertyValue_Product])、「不要な」一意制約を非正規化して追加しました。

  1. [ProductTypeId]を[ProductPropertyValue]に追加しました
  2. [PropertyId]、[ProductTypeId]に一意の制約を[ProductPropertyValue]に追加しました
  3. [ProductId]、[ProductTypeId]に一意の制約を[Product]に追加しました

しかし、これらなしでは上記の4つのポイントすべてを達成することは不可能のようです...

この課題は、私のデザインが単純な分岐「スノーフレーク」から逸脱し、関係に「ループ」が必要なときに発生するようです。

P ---------> PT
^            ^
|            |
|            |
PPV ------> PTP

注:これは4つのテーブルを持つ単純な例です。多くのレベルの関連テーブルがある実際の設計では、問題が拡大します(複数の列に対する一意の制約、テーブルごとに複数の「不要な」一意の制約など)。

4
Laurence

私はしばしば次の課題に直面し、最終的にデータの整合性を強化するためにテーブル設計を非正規化します。

これは矛盾のように聞こえます。通常、整合性を適用するのは正規化です。そして私の意見では、あなたは誤解を持っています。デザインを非正規化していません。正常に正規化されています。

正規化と整合性の両方を実行するデータをモデル化する別の方法があるかどうか知りたいです。

特定の問題(私はこれをひし形と呼んでいます)はかなり頻繁に発生しますが、多くの人はそれを問題として認識または認識せず、設計で強制した制約を強制しません。

そして、いくつかの詳細を無視して、他に方法はないと思います。 「祖父母」テーブルの一意のキー(この場合はPropertyType)は、「親」テーブルの一意の制約の一部である必要があります(ここではProductProperty)そして、あなたがしたように、「子」テーブル(ProductPropertyValue)に表示されます。

ポイント4は、問題を引き起こすポイントです。これを達成するための外部キーを作成するために([FK_ProductProperty_ProductTypeProperty]および[FK_ProductPropertyValue_Product])、「不要な」一意制約を非正規化して追加しました。

「不必要」については反対です。これらは、最初に_FOREIGN KEY_制約を定義して機能させるために必要です。

あなたのユニークな制約は問題なく、あなたが説明した完全性の理由のために必要です:
ProductPropertyValueProductPropertyに関連付けられていることを確認するには、両方の(ProductProperty)は同じPropertyTypeに属します。

設計の冗長性は、ProductおよびPropertyのID列に対する主キー制約です。完全性を失うことなく削除できます。自動インクリメント(SQL Serverではidentity)列にもUNIQUEまたは_PRIMARY KEY_制約があることはよくあることです。しかし、それは本当にそれを必要としません。ただし、パフォーマンス上の理由から、この制約を維持することをお勧めします。プロパティタイプとの関連がない他のいくつかのテーブルでは、列を外部キーとして使用することもできます(したがって、PropertyTypeIdは不要です)。ただし、論理的な設計と正規化の観点からは、正規化の問題はありません。これは単なる実装であり、物理的な設計の詳細です。

「他の関連するテーブルに既に存在するテーブルに列を追加する」という意味で「テーブルデザインの非正規化」という語句を使用しました(つまり、特定の行の値は、関連する行から導出できます)。正規化の1つの側面は、冗長な列の削除ではありませんか?

はい、そうです。ただし、エンティティ、属性、機能の依存関係から始める必要があります。代理キーを追加する前に-代理キーを追加すると、DBMSが一意性の追加機能を備えていることを考えると、それらを外部キー列としてどこでも使用したくなるでしょう。そのため、他の固有の列または組み合わせを使用できない場合があります。たとえば、_(ProductTypeId, ProductName)_も製品を識別できる可能性があるため、一意の制約がある可能性があります。ビジネスルールの詳細はわかりません。そうであれば、ProductPropertyValueの外部キーとして代わりに使用できます。 Propertyについても同様です。

nvarachar(100)のような長い列はインデックスに最適ではないため、それがより効率的であるかどうかは別の問題です。したがって、その一意の制約があったとしても、効率上の理由から、最初に選択したものを使用してしまう可能性があります。 (ProductPropertyの2つのnvarcharがある場合は、4 + 4 + 4 = 12と4 + 200 + 200 = 404を検討してください。)12バイトと404バイトのどちらかを選択するインデックスは難しい選択ではありません。 ProductPropertyValueが技術的に3NFにない場合でも、すべてが12サイズでうまくいくと思います。

「技術的に」と言い、ProductpropertyValueが3NFにないかのように見える唯一の理由は、DBMSがID列(ProductIdPropertyIdを提供したためです。 for ProductProperty)には、Uniquenessの素晴らしい追加機能があります(SQL Serverでは実際には保証されていません。他のDBMSで_IDENTITY_INSERT ON/OFF_または同様のプロパティを設定してプッシュすることもできます) ID列で一意ではない値。一意性は、設定した一意の制約によってのみ保証されます。)

この課題は、私のデザインが単純な分岐「スノーフレーク」から逸脱し、関係に「ループ」が必要なときに発生するようです。

_P ---------> PT
^            ^
|            |
|            |
PPV ------> PTP
_

だからこそ「ひし形」と呼んでいます!

そして、そこにはループがないことに注意してください!
-矢印に従って、好きなところから始めてください。始めたところに戻れますか?
-いいえ。ループはありません。

注:これは4つのテーブルを持つ単純な例です。多くのレベルの関連テーブルがある実際の設計では、問題が拡大します(複数の列に対する一意の制約、テーブルごとに複数の「不要な」一意の制約など)。

まあ、それは問題です。ルールが複雑で、複数のレベルの多くのエンティティが関係する場合、結果も複雑なモデルになり、複数列の一意制約と複数列の外部キーが含まれます。これはパフォーマンスに影響を与える可能性があるだけでなく、多くのDBMSは複数列の制約をあまり好まない(たとえば、インデックスの幅が広い、オプティマイザが十分に機能しない、または複数列の外部キーをまったく認識しない、クエリの書き換えルールを欠くなど)等。)。そして、ORMについては話さないでください。ORMのほとんどは、すべてのテーブルの主キーとしてidという名前の単一の列を期待しています...

それについてできることはあまりありません。多くの場合、複雑なモデルで制約を適用するか、設計から一部の制約を削除して簡素化します。

また、さまざまなレベルのルールが欠落している複数の設計をテストして、パフォーマンスのバランスを取り、使いやすさと開発を容易にし、制約を適用することもできます。しかし、少なくとも、どこを単純化したか、どの制約がデータベースレベルで強制されていないかがわかります。


別の質問で議論された同様の問題を参照してください: リレーショナルモデルで冗長な外部キーを処理するための最良のデータモデリングアプローチ 。 。グラフを見るだけで、類似性は明らかです。

_        PropertyType                     Survey
           /   \                          /   \
          /     \                        /     \
         /       \                      /       \          
    Product     Property       PersonSurvey   QuestionSurevy
         \       /                      \       /
          \     /                        \     /
           \   /                          \   /
    ProductPropertyValue                 Response
_

列と両方の問題の制約をチェックすると、詳細も一致します。違いは、MDCCLが論理設計から開始され、論理設計と正規化を完了した後、物理設計の次の段階で考慮すべきidentityまたはその他の実装の詳細が表示されないことです。 。

4
ypercubeᵀᴹ