SQL Server 2014の圧縮と最大行サイズ


8

多くのdecimal(26,8)列を含む広い非正規化テーブルを作成する必要があります(1024列の制限より少なく、ほとんどの列はnullまたはゼロになります)。行あたりの制限は8060バイトなので、ページ圧縮を使用してテーブルを作成しようとしました。以下のコードはテーブルを作成し、1つの行を挿入し、行サイズをクエリします。行のサイズが制限をはるかに下回っていますが、テーブルにdecimal(26,8)列を1つ追加しようとすると、エラーが発生し、操作が失敗します。内部オーバーヘッドのバイト。」それだけ多くの列を持つ単一のテーブルを作成する方法はありますか?

drop table t1
GO
create table t1(c1 decimal(26, 8) null)
with (data_compression = page)
GO

declare @i int = 2;
declare @sql varchar(100);
while @i <= 486
begin
    set @sql = 'alter table t1 add c' + convert(varchar, @i) + ' decimal(26, 8) null';
    execute (@sql);
    set @i += 1;
end;
GO


insert into t1(c1) select 0
GO
declare @i int = 2;
declare @sql varchar(100);
while @i <= 486
begin
    set @sql = 'update t1 set c' + convert(varchar, @i) + ' = 0';
    execute (@sql);
    set @i += 1;
end;
GO

select max_record_size_in_bytes from sys.dm_db_index_physical_stats (db_id(), object_id('t1'), NULL, NULL, 'DETAILED')
GO

1
FWIW、私はページ圧縮や10進圧縮なしで 613 DECIMAL(26, 8) NULLフィールドをテーブルに入れることができます。ページ圧縮ではなくvardecimalを有効にすると、オーバーヘッドが1 Kを超えます。値によっては、vardecimalなしでページごとにより多くのフィールドを格納できる可能性があります。
Jon of All Trades

回答:


4

実行中の制限は、ページに保存されているデータとは関係ありません。計算は、列のデータ型に基づいて行われます。そのため、テーブルにデータがない状態でエラーが発生します。圧縮により、この制限はさらに悪化します。オーバーヘッドの背後にある技術的な詳細については、こちらをご覧ください

SPARSE列を使用することにより、この問題を回避できます。つまり、挿入内容によっては挿入が失敗する可能性がありますが、8060バイトの制限を回避できます。次のコードは、1023列を問題なく作成できることを示しています。

drop table t1
GO
create table t1(c1 decimal(26, 8) null)
GO

declare @i int = 2;
declare @sql varchar(100);
while @i <= 1023
begin
    set @sql = 'alter table t1 add c' + convert(varchar, @i) + ' decimal(26, 8) SPARSE null';
    execute (@sql);
    set @i += 1;
end;
GO

ただし、それに関するすべての制限(リンクされた記事を参照)により、これがユースケースに適さなくなる場合があります。具体的には、スペースをほとんどとらNULLない0ように最適化されるのは(ではなく)値のみです。01つの行に挿入するが多すぎると、エラーが発生します。1023個の0値を挿入しようとすると、次のようになります。

メッセージ511、レベル16、状態1、行1許容最大行サイズ8060より大きいサイズ17402の行を作成できません。

本当に必死になったら、VARCHAR(27)代わりに列を作成できると思います。可変長の列をページ外に移動できるため、テーブル定義の8060バイトの制限を超えることができますが、値の特定の組み合わせを挿入すると失敗します。SQL Serverは、テーブルを作成するときにこれを警告します。

警告:テーブル "t1"が作成されましたが、その最大行サイズは許可されている最大の8060バイトを超えています。結果の行がサイズ制限を超えると、このテーブルへのINSERTまたはUPDATEは失敗します。

VARCHAR(27)アプローチを続ける場合、ページまたは行の圧縮が役立つ場合があります。これにより、0との両方で使用されるスペースが最小限になりNULLます。VARCHAR(27)私は1023のに挿入することができるよ0うまく値を。


2

@Joe の回答VARCHAR(27)説明されている技術的側面と提案された回避策(列を使用)の外では、OPで表現されている「[a]広い非正規化テーブルを作成する必要がある」と質問します。しなければならない単一のテーブルでも、私はお勧めします/必要なだけの「兄弟」のテーブルとして渡ってそれらを広げるお勧めします。兄弟テーブルは次のようなテーブルです。

  • 互いに1対1の関係にあり、
  • すべてがまったく同じ主キーを持ち、
  • 1つだけがIDENTITY列を持っている(他のFKはない)
  • 残りは、(PK列に)外部キーがあり、 IDENTITY

ここでは、論理行を2つ以上の物理テーブルに分割しています。しかし、それが本質的に正規化の本質であり、リレーショナルデータベースが処理するように設計されています。

このシナリオでは、PKを複製することによって使用される余分なスペースが発生しINNER JOIN、テーブルを一緒にする必要があるためにクエリがさらに複雑になります(すべてのSELECTクエリがすべての列を使用しない限り、頻繁ではありませんが、通常は発生しません)。または明示的なトランザクションを作成するINSERTか、UPDATEそれらを一緒に作成します(FKのセットをDELETE介して処理できますON DELETE CASCADE)。

ただし、適切なネイティブデータ型を備えた適切なデータモデルがあり、後で予期しない結果が生じる可能性のある不正操作を行わないという利点があります。VARCHAR(27)これを使用して技術的なレベルで機能する場合でも、実用的には、小数点以下を文字列として格納することはあなたの/プロジェクトの最大の利益になるとは思いません。

したがって、単一の論理エンティティを単一のコンテナで物理的に表現する必要がないことを認識していないために単一のテーブルのみが「必要」である場合、それが機能するときにこれらすべてを単一のテーブルに強制することを試みないでください。複数のテーブルにわたって優雅に。

次の例は、基本的な概念を示しています。

セットアップ

CREATE TABLE tempdb.dbo.T1
(
  [ID] INT NOT NULL IDENTITY(11, 2) PRIMARY KEY,
  [Col1] VARCHAR(25),
  [Col2] DATETIME NOT NULL DEFAULT (GETDATE())
);

CREATE TABLE tempdb.dbo.T2
(
  [ID] INT NOT NULL PRIMARY KEY
                    FOREIGN KEY REFERENCES tempdb.dbo.T1([ID]) ON DELETE CASCADE,
  [Col3] UNIQUEIDENTIFIER,
  [Col4] BIGINT
);

GO
CREATE PROCEDURE #TestInsert
(
  @Val1 VARCHAR(25),
  @Val4 BIGINT
)
AS
SET NOCOUNT ON;

BEGIN TRY
  BEGIN TRAN;

  DECLARE @InsertedID INT;

  INSERT INTO tempdb.dbo.T1 ([Col1])
  VALUES (@Val1);

  SET @InsertedID = SCOPE_IDENTITY();

  INSERT INTO tempdb.dbo.T2 ([ID], [Col3], [Col4])
  VALUES (@InsertedID, NEWID(), @Val4);

  COMMIT TRAN;
END TRY
BEGIN CATCH
  IF (@@TRANCOUNT > 0)
  BEGIN
    ROLLBACK TRAN;
  END;

  THROW;
END CATCH;

SELECT @InsertedID AS [ID];
GO

テスト

EXEC #TestInsert 'aa', 454567678989;

EXEC #TestInsert 'bb', 12312312312234;

SELECT *
FROM   tempdb.dbo.T1
INNER JOIN tempdb.dbo.T2
        ON T2.[ID] = T1.[ID];

戻り値:

ID  Col1  Col2                     ID  Col3                                  Col4
11  aa    2017-07-04 10:39:32.660  11  44465676-E8A1-4F38-B5B8-F50C63A947A4  454567678989
13  bb    2017-07-04 10:41:38.180  13  BFE43379-559F-4DAD-880B-B09D7ECA4914  12312312312234
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.