個別の行としてではなく、1つの行の1つのフィールドに複数の値を格納することの利点


11

前回の毎週の会議中に、データベース管理のバックグラウンド経験がない人がこの質問を持ち出しました。

「データを複数行ではなくインライン(文字列)に保存することを正当化するシナリオはありますか?」

countryStates国の州を保存する場所と呼ばれるテーブルがあるとします。この例では米国を使用します。怠惰にするためにすべての国をリストすることはしません。

そこには2つの列があります。1つが呼び出さCountryれ、もう1つが呼び出されましたStatesここで説明、@ srutzkyの回答で提案されているように、これはISO 3166-1 alpha-3でPK定義されたコードになります。

テーブルは次のようになります。

+---------+-----------------------+-------------------------------------------------------+
| Country | States                | StateName                                             |
+---------+-----------------------+-------------------------------------------------------+
| USA     | AL, CA, FL,OH, NY, WY | Alabama, California, Florida, Ohio, New York, Wyoming |
+---------+-----------------------+-------------------------------------------------------+

この同じ質問を友人の開発者に尋ねたところ、データトラフィックサイズの観点からは、これは役立つかもしれませんが、このデータを操作する必要がある場合はそうではないと述べました。この場合、リスト内のこの文字列を変換できるアプリケーションコードにインテリジェンスが必要です(このテーブルにアクセスできるソフトウェアがコンボボックスを作成する必要があるとしましょう)。

このモデルはあまり有用ではないと結論付けましたが、これを有効にする方法があるのではないかと疑いました。

私が聞きたいのは、実際に機能する方法で、このようなことをすでに見たり聞いたりしたりしていないかどうかです。


次に、2番目のテーブル "sales"があるとします。これには、発生したすべての販売のデータと、販売が発生した州コードが含まれています。列(StateName、TotalSalesAmount)を含むレポートを生成するクエリをどのように記述しますか?難しいですよね?
zgguy

丁度。私もこのモデルに同意しません。あらゆる種類のデータ(または必要に応じて有用なデータ)を復元する必要がある時点で行き詰まります。
Human_AfterAll 2016年

考えられるシナリオは、変数を格納することです。ストアはa;b;c、あなたが得るあなたの文字列を解析するためのフロントエンドを使用しabc多分、彼らと何かをやって、実行上のキャリー?。それがそのような特定の種類の特定のニーズに合うかもしれないと感じてください...考え直して、いいえ。常にIDを保存し、テーブルに結合して、FEにコンテンツを送信できる連結された文字列を作成できます...
Nelz

公平を期すために(少なくとも私にとっては;-)、他の回答では2文字の国コード :-) を使用することを提案しました。
ソロモンルツキー2016年

2
「州STATEの名前にはN番目の文字C」の列STATE、N&Cを持つ個別のテーブルを用意するのではなく、列に値「Alabama」を格納することに誰も気をつけていないことに注意してください。なぜなら、1。名前の文字についてクエリを実行するつもりはないか、2。関数NTH_CHAR(N、S)を呼び出して、名前のあるすべての行で「文字列SのN番目の文字」を返すことを気にしないからです。 。(対JOINおよびその他の関係演算子は、追加のテーブルを介してそのような行をいくつか削除します。)整数およびNTH_DIGIT(N、I)の同上。特定のデータベースの何が関係的にアトミックであるかについては、常に判断の呼びかけです。
philipxy 2016年

回答:


13

まず、「データを列ではなく文字列として保存する」という現在の質問のタイトルは少し混乱しています。データを何かの代わりに文字列として格納することについて話すとき、それは通常すべてを適切な/強いデータ型(INTまたはまたはDATETIME)ではなく文字列形式にシリアル化することを指します。しかし、個別の行ではなく単一のフィールドに複数の値としてデータを格納することについて尋ねる場合、それは少し異なります。公平に言えば、値の連結は文字列を使用すると最も簡単に行えますが、ビットマスキングまたは同様に特定の位置を予約してさまざまな意味を持たせることでINT、およびを使用して行うこともできBINARYます。2番目の解釈は実際に尋ねられていることなので、質問のテキストに基づいて、それを取り上げましょう。

つまり、いいえ。実際のデータポイントを格納している場合は、コードとパフォーマンスの点で不必要な面倒な作業であるため、問題が生じるだけです。これが1つの単位としてのみ保存され、1つの単位として更新され、データベース内で分解されない値である場合、画像またはPDFの保存とほぼ同じなので、問題はありません。それ以外の場合は、データを解析しようとすると、(たとえば、使用して任意のインデックスを使用して無効にするLIKE '%something%'、またはCHARINDEX、またはPATINDEX、またはSUBSTRINGなど、)。

単一の行の単一のフィールドに個別の値を格納する必要がある場合は、XMLまたはJSONを使用するより適切な方法があります。これらは解析可能な形式(XML / JSON)であり、XMLにインデックスを付けることもできます。しかし、理想的には、このデータは適切に入力されたフィールドに格納され、本当に役立つようになります。

また、RDBMSの目的は、ACIDに準拠することによって課せられる制約の範囲内で、データをできるだけ効率的に取得および操作できるようにデータを格納することであることを忘れないでください。最初に値を解析する必要があるため、連結された値を取得するのは十分に良くありません。これはインデックス化できません。ただし、多くの場合、操作とは、ブロブ全体を置き換えて、その一部を更新することを意味します(関数で使用するパターンが存在しない場合)。XMLデータ型では、少なくとも単純に更新するためのXML DMLが可能ですが、適切にモデル化されたデータの単純な更新ほど高速ではありません。REPLACE

また、上記の質問に示されているようなシナリオで、すべてのStateCodeを連結すると、これらの値を(どちらの方向にも)外部キーにできなくなります。

また、ビジネス要件が時間の経過とともに変化し、これらのアイテムの追加のプロパティを追跡する必要がある場合はどうでしょうか。「州」の観点から、首都、人口、ソート順、またはその他についてはどうですか?行として適切に保存され、プロパティを追加するために列を追加できます。もちろん、解析|StateCode,Capital,Population |StateCode,Capital,Populate|...可能なデータのレベルが複数ある場合もありますが、問題が指数関数的に制御不能に拡大しているのを誰もが見ることができれば幸いです。もちろん、この特定の問題は、XMLおよびJSON形式でかなり簡単に処理できます。これは、前述のようにそれらの値です。ただし、これらのいずれかをモデリングの最初の手段として使用するには、個別の行で個別のフィールドを使用するほど効率的ではないため、非常に十分な理由が必要です。


9

私は実際にそのようなものを非常に限られた目的で使用しました。出力ファイルのヘッダーのテーブルを作成しました。それらは特別に構築されており、ほとんどが列見出しでしたが、完全ではありません。したがって、データは次のようになりました

OutputType   OutputHeader
PersonalData Name|Address|City|State|Zip
JobInfo      Name|JobName|JobTitle

本質的には、区切られたリストのように見えました。そして、ある意味ではそうでした。しかし、私たちの目的では、それは単一の長い文字列でした。

それがここのトリックです。リストを解析する予定がない場合は、リストを保存する価値があります。ただし、リストを解析する必要がある場合、またはリストを解析する必要がある場合は、リストを分割して別の行に保存するのに余分なスペースと時間を費やす価値があります。


1

たとえば、かなり小さいテーブルで一度使用しました。

CREATE TABLE t1 (
  ID number,
  some_feature   varchar2(100),
  valid_channels  varchar2(100));

CREATE TABLE channel_def (
  channel varchar2(100));

次に、値CRM,SMS,SELF-CAREをに保存しますvalid_channel

テーブル全体には、10レコードのようなものがあります。 valid_channel多対多の関係を表すリンクテーブルに実際にあるはずの値が含まれています。テーブルt1は集中的に使用されることはないので、この道を行くことにしました。しかし、いくつかの政治がこの決定に関与しました(以下を参照)。

しかし、一般的に私はそれを避けます、それは3NFではありません。

私が現在働いている場所には、そのような柱が何十本もあり、彼らの正当化は、それが彼らのクエリをより簡単にするということです:リンクテーブルを使用して3つのテーブルを結合する代わりに、彼らはを使用して定義テーブルに直接行くことができますLIKE。例えば

SELECT * 
  FROM t1 
 INNER JOIN channel_def cd
    ON ','||t1.valid_channels||',' LIKE '%,'||cd.channel||',%';

恐ろしい+ Oracleでは、起動のためにインデックスの使用を無効にします'%,'


どちらが遅くなりますLIKEか:または単純な結合?
Human_AfterAll 2016年

インデックスが付けられているか、少なくとも参照制約(FK)が設定されている列に結合することをお勧めします。さらに、結合は通常、他のテーブルのPKで行われます。これは、デフォルトでインデックスが付けられています(少なくともOracleでは)。目前の特定のケースについて質問している場合(上記を参照)、小さなテーブルであったため、実行プランはおそらく同じであると言います。
Robotron、2016年

@Human_AfterAllはLIKE、特にデータがのTINYINTPKフィールドを使用するように適切にモデル化されている場合は遅くなりますchannel_def。次に、2つのテーブル間で1バイトを比較するだけで済みます。ここでは、文字列を文字ごとに(少なくとも条件が満たされるまで)解析する必要があり、大文字と小文字を区別しない検索が行われます(指定されたテーブルの定義に基づいて、_BIN2使用されている照合を示していません)。これにより、SQL Serverのインデックスも無効になります。私の回答では、解析ではインデックスを使用できないと述べて、これに対処しました。わかりやすくするために回答を更新しました。
ソロモンルツキー2016年

1
@Human_AfterAllこのモデリングの決定は、経験と知識の欠如(そして時には怠惰)から生まれたと言えるでしょう。保存されるのは追加のJOINだけですが、犠牲になるのは、完全に偽のデータが入るのを防ぐ外部キーの機能です(LIKE句に一致せず、奇妙な結果を生成する場合でも、他の問題が発生する可能性があります)。少なくともデバッグを難しく/長くしてください)。また、valid_channelsフィールドの更新がより複雑になります。これが機能しないと言っているのではなく、それを行う正当な理由がないだけです。
ソロモンルツキー2016年

「経験不足」 -最悪何が...この特定の設計決定は上級スタッフによって課されたことがある
Robotron

1

これはSEで行われました。Marc Gravellが書いているように

...いくつかの検討と検討の結果、パイプ(バー)で区切られた自然表現で、先頭/末尾のパイプが決まったため、「。net c#」は単に「| .net | c#|」になります。これには利点があります:

  • 解析が非常に簡単
  • タグの一括更新と削除は、単純な置換で実行できます(パイプを含め、タグの中間一致の置換を回避します)。
  • ...

この「新しい形式」は、少し異なる「古い形式」の次のステップであり、SQL Serverのフルテキスト検索機能を利用するために選択されたため、ゼロから行う場合、いくつかの利点は関係ありません。

彼らはおそらく、作業量とパフォーマンスの両方の理由から、事物を完全に正規化しなかったでしょう。


0

文字列やその他のデータ型を使用することの主な利点の1つは、純粋なパフォーマンスが必要な場合に、SQLCLRを使用してSQL ServerからC#、C、C ++(など)に送信することです。ビューまたはストアドプロシージャを作成して、リレーショナルデータを非リレーショナルに表すこともできます。これは、まさにこの目的のための上記の例と同じです。

この例を見てください:

http://aboutsqlserver.com/2013/07/22/clr-vs-t-sql-performance-considerations/

ウィキペディアごと:SQL CLRまたはSQLCLR(SQL共通言語ランタイム)は、SQL Server内でMicrosoft .NET共通言語ランタイムエンジンをホストするためのテクノロジーです。SQLCLRを使用すると、マネージコードをMicrosoft SQL Server環境でホストし、実行することができます。


2
こんにちは。ここで詳細を教えてください。これが従来とは異なる方法でデータを保存することの利点であるかどうかはわかりません。どちらかと言えば、存在する必要がある場合に代替データ形式をより適切に処理できることは、SQLCLRの利点です。しかし、それが代替データ形式を好む理由ではありません。そのため、これで問題が解決することはないと思います。
ソロモンルツキー2016年

記事のリンクは長所と短所の利点を説明しています。また、データをリレーショナルに格納すること、およびCLRがそれをビューまたはストアドプロシージャを使用して非リレーショナルに変換する目的で言及しました。あなたの質問は、「データを複数行ではなくインライン(文字列)に格納することを正当化するシナリオはありますか?」でした。そして、私の答えは「はい」でしたが、CLRと対話するためには、ビューまたはストアドプロシージャを好みます。
2016年

0

私の見解では、答えはノーです。私はこのアプローチを使用していなかったので、回避しました。そのルートを下る理由を考えることができません。あなたは配列を使ってJSON / NoSQLの世界に傾いています。

以前の役割でも同様の設計の選択肢があり、建築家チームは区切られてバイナリに変換された「データ」フィールドが必要でした。いくつかの理由により、最終的にそのルートを下ることはありませんでした。

このタイプのデータに参加する必要がある場合、それは1つの醜い経験になります。文字列の単一の要素を更新することも不快です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.