DBモデルからヌル可能な列を排除するためのオプション(SQLの3値ロジックを回避するため)?
-
28-09-2019 - |
質問
少し前に、私は本を読んでいます SQLおよびリレーショナル理論 CJ日付まで. 。著者は、SQLの3価値のロジック(3VL)を批判することで有名です。1)
著者は、SQLで3VLを避ける必要がある理由についていくつかの強みを挙げていますが、彼は概説しません nullable列が許可されていない場合、データベースモデルがどのように見えるか. 。私はこれについて少し考えて、次のソリューションを思いつきました。他のデザインオプションを見逃した場合、それらについて聞きたいです!
1) SQLの3VLに対する日付の批判は、順番にも批判されています。 クロード・ルービンソンによるこの論文 (CJ日付までの元の批評を含む)。
例の表:
例として、次のテーブルを使用して、nullable 1つの列が1つあります(DateOfBirth
):
# +-------------------------------------------+
# | People |
# +------------+--------------+---------------+
# | PersonID | Name | DateOfBirth |
# +============+--------------+---------------+
# | 1 | Banana Man | NULL |
# +------------+--------------+---------------+
オプション1:エミュレート NULL
フラグとデフォルト値を介して:
列を無効にする代わりに、デフォルト値が指定されています(例: 1900-01-01
)。追加の BOOLEAN
列は、値を指定します DateOfBirth
単に無視する必要がありますか、それとも実際にデータが含まれているかどうか。
# +------------------------------------------------------------------+
# | People' |
# +------------+--------------+----------------------+---------------+
# | PersonID | Name | IsDateOfBirthKnown | DateOfBirth |
# +============+--------------+----------------------+---------------+
# | 1 | Banana Man | FALSE | 1900-01-01 |
# +------------+--------------+----------------------+---------------+
オプション2:Nullable列を別のテーブルに変える:
Nullable列は新しいテーブルに置き換えられます(DatesOfBirth
)。レコードにその列のデータがない場合、新しいテーブルにはレコードがありません。
# +---------------------------+ 1 0..1 +----------------------------+
# | People' | <-------> | DatesOfBirth |
# +------------+--------------+ +------------+---------------+
# | PersonID | Name | | PersonID | DateOfBirth |
# +============+--------------+ +============+---------------+
# | 1 | Banana Man |
# +------------+--------------+
これはより良い解決策のように思えますが、これはおそらく1つのクエリのために結合する必要がある多くのテーブルをもたらすでしょう。以来 OUTER JOIN
Sは許可されません(紹介するためです NULL
結果セット)に、必要なデータはすべて、以前と同じように単一のクエリだけでフェッチしなくなる可能性があります。
質問:排除するための他のオプションはありますか NULL
(もしそうなら、彼らは何ですか)?
解決
デートの同僚であるヒュー・ダーウェンは、この問題について、「nullを使用せずに欠落している情報を処理する方法」でこの問題を議論しました。 3番目のマニフェストWebサイト.
彼の解決策は、2番目のアプローチのバリアントです。 6番目の通常の形式で、生年月日と不明の識別子の両方を保持する表があります。
# +-----------------------------+ 1 0..1 +----------------------------+
# | People' | <-------> | DatesOfBirth |
# +------------+----------------+ +------------+---------------+
# | PersonID | Name | | PersonID | DateOfBirth |
# +============+----------------+ +============+---------------+
# | 1 | Banana Man | ! 2 | 20-MAY-1991 |
# | 2 | Satsuma Girl | +------------+---------------+
# +------------+----------------+
# 1 0..1 +------------+
# <-------> | DobUnknown |
# +------------+
# | PersonID |
# +============+
# | 1 |
# +------------+
その後、人から選択するには、生年月日を示すためにボイラープレートを含む3つのテーブルすべてに参加する必要があります。
もちろん、これはやや理論的です。最近のSQLの状態は、これらすべてを処理するためにまだ十分に進歩していません。ヒューのプレゼンテーションは、これらの欠点をカバーしています。彼が言及していることの1つは完全に正しいわけではありません。SQLのいくつかのフレーバーは複数の割り当てをサポートしています - たとえば Oracleのすべての構文を挿入します.
他のヒント
私はあなたがあなたのオプション2に行くことをお勧めします2.私はあなたがしていることが完全に正常化しているので、私はあなたのオプション2に行くことをお勧めします。 6nf, 、可能な限り最高の通常の形 日付は導入の共同で責任を負いました。私は2番目に推奨されました ダーウェンの論文 不足している情報の処理について。
外側の結合は許可されないため(結果セットにnullを導入するため)、必要なデータはすべて、以前と同じように1回のクエリだけでフェッチできなくなる可能性があります。
…これは事実ではありませんが、アウター結合の問題がダーウェン論文で明示的に言及されていないことに同意します。それは私が欲しかった一つのことでした。明示的な答えは、別の日付の本にあります…
まず、日付とダーウェン自身の真に関係の言語に注意してください チュートリアルd 自然な結合である1つの結合タイプがあります。正当化は、実際に1つの結合タイプのみが必要であるということです。
私がほのめかした日付の本は素晴らしいです SQLとリレーショナル理論:正確なSQLコードの書き方:
4.6:外側の結合に関する発言:「関係的に言えば、[外側の結合は]ショットガンの結婚のようなものです。それはテーブルを一種の結合に強制します。組合の通常の要件に準拠している...事実上、組合を行う前にヌルでテーブルの一方または両方をパディングして、結局それらの通常の要件に準拠させることにより、これを行います。しかし、そのパディングの理由はありません。ヌルの代わりに適切な値で行うべきではありません
例とデフォルト値「1900-01-01」を「パディング」として使用すると、外側の結合に代わるものは次のようになります。
SELECT p.PersonID, p.Name, b.DateOfBirth
FROM Person AS p
INNER JOIN BirthDate AS b
ON p.PersonID = b.PersonID
UNION
SELECT p.PersonID, p.Name, '1900-01-01' AS DateOfBirth
FROM Person AS p
WHERE NOT EXISTS (
SELECT *
FROM BirthDate AS b
WHERE p.PersonID = b.PersonID
);
ダーウェンの紙は、2つの明示的なテーブルをプロッソしています BirthDate
と BirthDateKnown
, 、しかし、SQLはそれほど違いはありません。 BirthDateKnown
セミの違いの代わりに BirthDate
その上。
上記の使用に注意してください JOIN
と INNER JOIN
標準のSQL-92のために NATURAL JOIN
と UNION CORRESPONDING
実生活のSQL製品では広く実装されていません(引用は見つかりませんが、IIRC Darwenは後者の2つに主に責任を負っています)。
さらに注意してください。上記の構文は、一般的にSQLが長くなっているという理由だけで長く見えるように見えます。純粋なリレーショナル代数では、それは(擬似コード)に似ています。
Person JOIN BirthDate UNION Person NOT MATCHING BirthDate ADD '1900-01-01' AS DateOfBirth;
私はそれを読んでいませんが、呼ばれる記事があります S-by-cを使用して欠落情報を処理する方法 に 3番目のマニフェスト Hugh DarwenとCJ Dateが運営するWebサイト。これはCJ日付によって書かれたものではありませんが、それはそのウェブサイトの記事の1つであるため、おそらく彼の意見に似ていると思います。
1つの選択肢があります エンティティアトリブの価値 モデル:
entity attribute value
1 name Banana Man
1 birthdate 1968-06-20
生年月日が不明な場合は、その行を省略するだけです。
オプション3:レコードライターの責任:
CREATE TABLE Person
(
PersonId int PRIMARY KEY IDENTITY(1,1),
Name nvarchar(100) NOT NULL,
DateOfBirth datetime NOT NULL
)
目標がそれらを排除することであるときに、ヌル表現を許可するモデルをゆがめるのはなぜですか?
排除できます null
使用して出力も同様です COALESCE
.
SELECT personid /*primary key, will never be null here*/
, COALESCE(name, 'no name') as name
, COALESCE(birthdate,'no date') as birthdate
FROM people
すべてのデータベースがcoalesceをサポートしているわけではありませんが、ほとんどすべてが呼ばれるフォールバックオプションがあります
IFNULL(arg1, arg2)
または同じことをするシミュレーション (ただし、2つの議論のみ).
1つのオプションは、明示的な使用です オプションタイプ, 、Haskellに似ています Maybe
ファンクタ。
残念ながら、既存のSQL実装の多くは、ユーザー定義の代数データ型に対するサポートが不十分であり、これをきれいに行う必要があるユーザー定義型コンストラクターのサポートがさらに低くなります。
これは、あなたが明示的にそれを求める属性のみに対して、ある種の「ヌル」を回復しますが、 null
愚かな3値のロジック。 Nothing == Nothing
は True
, 、 いいえ unknown
また null
.
ユーザー定義の代数タイプのサポートは、情報が欠落している理由がいくつかある場合にも役立ちます。たとえば、次のHaskellタイプに相当するデータベースは、明らかなアプリケーションの良いソリューションです。
data EmploymentStatus = Employed EmployerID | Unemployed | Unknown
(もちろん、これをサポートするデータベースは、それに伴うより複雑な外国のキーの制約をサポートする必要があります。)