17

特定のシステム操作の詳細を記録するために、MS SQL Server 2005 でテーブルを作成したいと考えています。以下のテーブル デザインからわかるように、Detailsis 以外のすべての列は null 不可です。

CREATE TABLE [Log]
(
[LogID] [int] IDENTITY(1,1) NOT NULL,
[ActionID] [int] NOT NULL,
[SystemID] [int] NOT NULL,
[UserID] [int] NOT NULL,
[LoggedOn] [datetime] NOT NULL,
[Details] [varchar](max) NULL
)

Details列に常にデータが含まれているとは限らないためです。この列を別のテーブルに格納し、代わりにリンクを提供する方が効率的ですか?

CREATE TABLE [Log]
(
[LogID] [int] IDENTITY(1,1) NOT NULL,
[ActionID] [int] NOT NULL,
[SystemID] [int] NOT NULL,
[UserID] [int] NOT NULL,
[LoggedOn] [datetime] NOT NULL,
[DetailID] [int] NULL
)       

CREATE TABLE [Detail]
(
[DetailID] [int] IDENTITY(1,1) NOT NULL,
[Details] [varchar](max) NOT NULL
)

小さいデータ型の場合はあまり考慮しませんが、varchar(max)これを行うとテーブルのサイズを小さく保つのに役立ちますか? それとも、データベースを賢くしようとしているだけで、何も達成していませんか?

4

6 に答える 6

30

インラインにしてください。内部的には、SQL Server は SQL 2005 以降、MAX 列を別の「アロケーション ユニット」に格納しています。テーブルとインデックスの編成を参照してください。これは事実上、MAX 列を独自のテーブルに保持することとまったく同じですが、明示的に保持することの欠点はありません。

明示的なテーブルを使用すると、実際には速度が低下し (外部キー制約のため)、より多くのスペースを消費します(DetaiID の重複のため)。言うまでもなく、より多くのコードが必要であり、バグはコードを書くことによって導入されます。

代替テキスト http://i.msdn.microsoft.com/ms189051.3be61595-d405-4b30-9794-755842d7db7e(ja-jp,SQL.100).gif

アップデート

データの実際の場所を確認するには、簡単なテストで確認できます。

use tempdb;
go

create table a (
  id int identity(1,1) not null primary key,
  v_a varchar(8000),
  nv_a nvarchar(4000),
  m_a varchar(max),
  nm_a nvarchar(max),
  t text,
  nt ntext);
go

insert into a (v_a, nv_a, m_a, nm_a, t, nt)
values ('v_a', N'nv_a', 'm_a', N'nm_a', 't', N'nt');
go

select %%physloc%%,* from a
go

疑似列には、行の実際の物理的な場所が表示されます。%%physloc%%私の場合は 200 ページでした。

dbcc traceon(3604)
dbcc page(2,1, 200, 3)

Slot 0 Column 2 Offset 0x19 Length 3 Length (physical) 3
v_a = v_a                            
Slot 0 Column 3 Offset 0x1c Length 8 Length (physical) 8
nv_a = nv_a                          
m_a = [BLOB Inline Data] Slot 0 Column 4 Offset 0x24 Length 3 Length (physical) 3
m_a = 0x6d5f61                       
nm_a = [BLOB Inline Data] Slot 0 Column 5 Offset 0x27 Length 8 Length (physical) 8
nm_a = 0x6e006d005f006100            
t = [Textpointer] Slot 0 Column 6 Offset 0x2f Length 16 Length (physical) 16
TextTimeStamp = 131137536            RowId = (1:182:0)                    
nt = [Textpointer] Slot 0 Column 7 Offset 0x3f Length 16 Length (physical) 16
TextTimeStamp = 131203072            RowId = (1:182:1)   

TEXT と NTEXT 以外のすべての列値は、MAX 型を含めてインラインで格納されていました。
テーブル オプションを変更して新しい行を挿入した後 (sp_tableoption は既存の行には影響しません)、MAX 型は独自のストレージに削除されました。

sp_tableoption 'a' , 'large value types out of row', '1';
insert into a (v_a, nv_a, m_a, nm_a, t, nt)
values ('2v_a', N'2nv_a', '2m_a', N'2nm_a', '2t', N'2nt');    
dbcc page(2,1, 200, 3);

m_a 列と nm_a 列が LOB アロケーション ユニットへの Textpointer になっていることに注意してください。

Slot 1 Column 2 Offset 0x19 Length 4 Length (physical) 4
v_a = 2v_a                           
Slot 1 Column 3 Offset 0x1d Length 10 Length (physical) 10
nv_a = 2nv_a                         
m_a = [Textpointer] Slot 1 Column 4 Offset 0x27 Length 16 Length (physical) 16
TextTimeStamp = 131268608            RowId = (1:182:2)                    
nm_a = [Textpointer] Slot 1 Column 5 Offset 0x37 Length 16 Length (physical) 16
TextTimeStamp = 131334144            RowId = (1:182:3)                    
t = [Textpointer] Slot 1 Column 6 Offset 0x47 Length 16 Length (physical) 16
TextTimeStamp = 131399680            RowId = (1:182:4)                    
nt = [Textpointer] Slot 1 Column 7 Offset 0x57 Length 16 Length (physical) 16
TextTimeStamp = 131465216            RowId = (1:182:5)                    

完成のために、max 以外のフィールドの 1 つを行外に強制することもできます。

update a set v_a = replicate('X', 8000);
dbcc page(2,1, 200, 3);

v_a 列が Row-Overflow ストレージに格納される方法に注意してください。

Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4
v_a = [BLOB Inline Root] Slot 0 Column 2 Offset 0x19 Length 24 Length (physical) 24
Level = 0                            Unused = 99                          UpdateSeq = 1
TimeStamp = 1098383360               
Link 0
Size = 8000                          RowId = (1:176:0) 

したがって、他の人がすでにコメントしているように、MAX タイプは、適合する場合、デフォルトでインラインに格納されます。多くの DW プロジェクトでは、典型的な DW ロードはスキャンまたは少なくとも範囲スキャンを行う必要があるため、これは受け入れられないため、sp_tableoption ..., 'large value types out of row', '1'を使用する必要があります。これは既存の行には影響しないことに注意してください。私のテストでは、インデックスの再構築でも影響しないため、オプションを早期にオンにする必要があります。

ほとんどの OLTP タイプのロードでは、可能であれば MAX タイプがインラインで格納されるという事実は実際には利点です。OLTP アクセス パターンはシークであり、行幅はそれにほとんど影響を与えないためです。

それでも、元の質問に関しては、別の表は必要ありません。オプションをオンにlarge value types out of rowすると、開発/テストの費用は無料で同じ結果が得られます。

于 2009-11-09T16:32:43.610 に答える
11

逆説的ですが、データが通常8000文字未満の場合は別のテーブルに保存し、データが8000文字を超える場合は同じテーブルに保存します。

これは、SQL Serverが行を単一のページに配置できる場合はデータをページに保持しますが、データが大きくなると、TEXTデータ型と同じようにデータを移動し、ポインターだけを残すためです。行。したがって、3000文字の行の束の場合、ページあたりの行数が少なくなり、実際には非効率的ですが、12000文字の行の束の場合、データは行から外れるため、実際にはより効率的です。

そうは言っても、通常、長さの組み合わせは多岐にわたるため、私はそれを独自のテーブルに移動します。これにより、このテーブルを別のファイルグループなどに移動するための柔軟性が得られます。

sp_tableoptionを使用して、データを行から強制的に削除するように指定することもできることに注意してください。varchar(max)は、基本的にTEXTデータ型に似ていますが、デフォルトで行外のデータ(TEXTの場合)ではなく、デフォルトで行内のデータ(varchar(max)の場合)になります。

于 2009-11-09T16:09:27.270 に答える
2

データを最も論理的な構造に構造化し、SQLServerがデータを物理的に格納する方法に関して最適化を実行できるようにする必要があります。

パフォーマンス分析を通じて、構造がパフォーマンスの問題であることがわかった場合は、構造またはストレージ設定に変更を加えることを検討してください。

于 2009-11-09T16:14:23.263 に答える
0

詳細テーブルを作成して正規化します。ログの一部のエントリに同じ詳細があると思いますか?したがって、正規化すると、テキストを詳細テーブルに保存した場合、すべての出現箇所のテキストではなく、FKidINTEGERのみが保存されます。非正規化する理由がある場合はそれを行いますが、あなたの質問からはそうではないと思います。

于 2009-11-09T15:45:53.580 に答える
0

インラインにしてください。要点はvarchar、空の場合は 0 バイト、「Hello」の場合は 4 バイトなどを使用することです。

于 2009-11-09T15:41:09.337 に答える