29

1000列のテーブルを作成しています。ほとんどの列はnvarcharタイプです。テーブルは作成されますが、警告があります

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

テーブルのほとんどの列には、すでにデータがあります (つまり、列の 99% にデータがあります)。310 番目以降の列を更新しようとすると (309 列から始まるすべての列に何らかの値がある場合)、エラーが発生します。

8060 の許容最大行サイズより大きいサイズ 8061 の行を作成できません。

このデータを最初の 308 列すべてに挿入しています

「Lorem ipsum dolor sit amet, consectetur adipisicing elit.」

データ型を使用してntextいる場合、約 450 列を更新できますが、それを超えると更新ntextできません。少なくとも 700 列を更新する必要があります。どの SQL Server がそれを許可していないか。テーブルの一部の列を別のテーブルに移動できないというシナリオがあります。

実際、私は既存のウィンドウアプリケーションに取り組んでいます。これは非常に大きな Windows アプリケーションです。

実際、最大 700 個の nvarchar 列のデータを挿入しようとしているテーブルは、実行時に動的に作成されます。場合によっては、400 ~ 600 列を挿入する必要があります。しかし、一般的には、簡単に処理できる100〜200列が必要です。

問題は、このテーブルを複数のテーブルに分割できないことです。この構造で作成された多くのテーブルとテーブルの名前が別のテーブルに保持されているため、つまり、この構造で 100 以上のテーブルがあり、それらは動的に作成されています。テーブルの作成とそのデータの操作には、4 ~ 5 の言語 (C#、Java など) が使用されており、WCF、Windows サービス、および Web サービスも含まれます。

したがって、テーブルを分割した後にテーブルとそのデータを操作するのは簡単ではないと思います。テーブルを分割すると、多くの構造変更が必要になります。

ですから、この問題を解決する最善の方法を教えてください。

また、次のようなスパース列を使用しようとしました:

Create table ABCD(Id int, Name varchar(100) Sparse, Age int);

ColumnStoreIndexについても考えましたが、私の目的は解決されていません。

スパース列を使用すると、テーブルに 3000 列を作成できますが、ページ サイズも制限されます。

一時テーブルを使用するか、他のタイプのSQLサーバーオブジェクトを使用してそれを達成する方法はありますか?

4

7 に答える 7

20

SQL Server の最大列制限

短い文字列列あたりのバイト数 8,000

GROUP BY、ORDER BY 8,060あたりのバイト数

行あたりのバイト数 8,060

インデックス キーあたりの列数 16

外部キーあたりの列数16

主キーあたりの列数16

非ワイド テーブルあたりの列数 1,024

幅の広いテーブルあたりの列数 30,000

SELECT ステートメントあたりの列数 4,096

INSERT ステートメントごとの列数 4096

UPDATE ステートメントあたりの列数 (ワイド テーブル) 4096

行あたり8,060バイトを超えるvarchar、nvarchar、varbinary、sql_variant、または CLR ユーザー定義型の列を組み合わせる場合は、次の点を考慮してください。

行サイズの制限である 8,060 バイトを超えると、パフォーマンスに影響する可能性があります。これは、SQL Server が 1 ページあたり 8 KB の制限を維持しているためです。varchar、nvarchar、varbinary、sql_variant、または CLR のユーザー定義型の列の組み合わせがこの制限を超えると、SQL Server データベース エンジンSQL Server Database Engine は、最大幅のレコード列を ROW_OVERFLOW_DATA アロケーション ユニット内の別のページに移動します。元のページのバイト ポインター。大きなレコードの別のページへの移動は、更新操作に基づいてレコードが長くなるため、動的に行われます。レコードを短くする更新操作により、レコードが IN_ROW_DATA アロケーション ユニットの元のページに戻される場合があります。また、行オーバーフロー データを含む大きなレコードの並べ替えや結合など、他の選択操作をクエリして実行すると、処理時間が遅くなります。

したがって、複数の varchar、nvarchar、varbinary、sql_variant、または CLR ユーザー定義型の列を含むテーブルを設計する場合は、オーバーフローする可能性がある行の割合と、このオーバーフロー データがクエリされる可能性がある頻度を考慮してください。行オーバーフロー データの多くの行に対して頻繁にクエリが実行される可能性がある場合は、一部の列が別のテーブルに移動されるように、テーブルを正規化することを検討してください。これは、非同期 JOIN 操作で照会できます。

  • 個々の列の長さは、varchar、nvarchar、varbinary、sql_variant、および CLR ユーザー定義型列の 8,000 バイトの制限内に収める必要があります。テーブルの 8,060 バイトの行制限を超えることができるのは、それらを組み合わせた長さだけです。
  • char および nchar データを含む他のデータ型列の合計は、8,060 バイトの行制限内に収まる必要があります。ラージ オブジェクト データも、8,060 バイトの行制限から除外されます。
  • クラスター化インデックスのインデックス キーには、ROW_OVERFLOW_DATA アロケーション ユニットに既存のデータがある varchar 列を含めることはできません。クラスター化インデックスが varchar 列に作成され、既存のデータが IN_ROW_DATA アロケーション ユニットにある場合、データを行外にプッシュする列に対する後続の挿入または更新アクションは失敗します。アロケーション ユニットの詳細については、「テーブルとインデックスの編成」を参照してください。
  • 行オーバーフロー データを含む列を、非クラスター化インデックスのキー列または非キー列として含めることができます。
  • スパース列を使用するテーブルのレコード サイズの制限は 8,018 バイトです。変換されたデータと既存のレコード データが 8,018 バイトを超えると、MSSQLSERVER ERROR 576 が返されます。列がスパース型と非スパース型の間で変換されると、データベース エンジンは現在のレコード データのコピーを保持します。これにより、レコードに必要なストレージが一時的に 2 倍になります。.
  • 行オーバーフロー データを含む可能性のあるテーブルまたはインデックスに関する情報を取得するには、sys.dm_db_index_physical_stats 動的管理関数を使用します。

n 個の列とデータ型 Nvarchar を持つテーブルの作成

CREATE Proc [dbo].[CreateMaxColTable_Nvarchar500]
(@TableName nvarchar(100),@NumofCols int)
AS
BEGIN

DECLARE @i INT
DECLARE @MAX INT
DECLARE @SQL VARCHAR(MAX)
DECLARE @j VARCHAR(10)
DECLARE @len int
SELECT @i=1
SELECT @MAX=@NumofCols
SET @SQL='CREATE TABLE ' + @TableName + '('

WHILE @i<=@MAX

BEGIN
select @j= cast(@i as varchar)
SELECT @SQL= @SQL+'X'+@j  +' NVARCHAR(500) , '
SET @i = @i + 1
END
select @len=len(@SQL)

select  @SQL = substring(@SQL,0,@len-1)


SELECT @SQL= @SQL+ ' )'

exec (@SQL)

END

詳細については、次のリンクにアクセスしてください。

http://msdn.microsoft.com/en-us/library/ms186981%28SQL.105%29.aspx?PHPSESSID=tn8k5p1s508cop8gr43e1f34d2

http://technet.microsoft.com/en-us/library/ms143432.aspx

しかし、なぜそんなに多くの列を持つテーブルが必要なのかシナリオを教えてください。データベースの再設計について検討すべきだと思います。

于 2013-08-07T19:38:20.157 に答える
12

これは単に不可能です。ストレージ エンジンの内部: レコードの構造を参照してください。

あなたのテーブルがこのようなものであると仮定します。

CREATE TABLE T1(
    col_1 varchar(8000) NULL,
    col_2 varchar(8000) NULL,
    /*....*/
    col_999 varchar(8000) NULL,
    col_1000 varchar(8000) NULL
) 

次に、すべてのNULL値を含む行でさえ、次のストレージを使用します。

  • 1バイトのステータスビットA
  • 1バイトのステータスビットB
  • 2 バイトの列数オフセット
  • 125 バイトNULL_BITMAP( bit1,000 列の場合は列ごとに 1 つ)

つまり、すでに 129 バイトが使用されていることが保証されています (残り 7,931)。

いずれかの列の値がNULL空文字列でも空の文字列でもない場合は、次のスペースも必要です。

  • 2 バイトの可変長列数 (残り 7,929)。
  • 列オフセット配列の 2 から 2000 バイトの間の任意の場所。
  • データそのもの。

列オフセット配列は、可変長列ごとに 2 バイトを消費しますが、その列とそれ以降のすべての列も長さが 0 の場合を除きます。したがってcol_1000、更新では 2000 バイト全体が使用されるのに対し、更新 col_1では 2 バイトしか使用されません。

したがって、各列に 5 バイトのデータを入力できます。列オフセット配列の各 2 バイトを考慮すると、残りの 7,929 バイトに最大 7,000 バイトが追加されます。

ただし、保存しているデータは 102 バイト (51nvarchar文字) であるため、行に残っている実際のデータへの 24 バイト ポインターを使用して行外に保存できます。

FLOOR(7929/(24 + 2)) = 304

col_1したがって、この長さのデータの 304 列を格納でき、それは, col_2,から更新する場合に最適です...。データが含まれている場合col_1000、計算は

FLOOR(5929/24) = 247

NTEXT計算は似ていますが、16バイトのポインターを使用できることを除いて、データをいくつかの余分な列に絞り込むことができます

FLOOR(7929/(16 + 2)) = 440

これらすべての行外ポインタをSELECTテーブルに対して追跡する必要があると、パフォーマンスが大幅に低下する可能性があります。

これをテストするスクリプト

DROP TABLE T1

/* Create table with 1000 columns*/
DECLARE @CreateTableScript nvarchar(max) = 'CREATE TABLE T1('

SELECT @CreateTableScript += 'col_' + LTRIM(number) + ' VARCHAR(8000),'
FROM master..spt_values
WHERE type='P' AND number BETWEEN 1 AND 1000
ORDER BY number

SELECT @CreateTableScript += ')'

EXEC(@CreateTableScript)

/* Insert single row with all NULL*/
INSERT INTO T1 DEFAULT VALUES


/*Updating first 304 cols succeed. Change to 305 and it fails*/
DECLARE @UpdateTableScript nvarchar(max) = 'UPDATE T1 SET  '

SELECT @UpdateTableScript += 'col_' + LTRIM(number) + ' = REPLICATE(1,1000),'
FROM master..spt_values
WHERE type='P' AND number BETWEEN 1 AND 304
ORDER BY number

SET @UpdateTableScript = LEFT(@UpdateTableScript,LEN(@UpdateTableScript)-1)
EXEC(@UpdateTableScript)
于 2012-12-28T16:16:56.427 に答える
9

1.000 列のテーブルを持つことは、データベースの設計に非常に問題があることを示しています。

テーブルの 1 つに 500 を超える列があるプロジェクトを継承しましたが、アプリケーションの 90% を作り直す必要があるため、1 年以上経ってもまだ大幅に減らすことができません。

手遅れになる前に DB を再設計してください。

于 2012-12-28T14:36:04.917 に答える
2

n個の列とデータ型Nvarcharのテーブルを作成する

CREATE Proc [dbo].[CreateMaxColTable_Nvarchar500]
(@TableName nvarchar(100),@NumofCols int)
AS
BEGIN

DECLARE @i INT
DECLARE @MAX INT
DECLARE @SQL VARCHAR(MAX)
DECLARE @j VARCHAR(10)
DECLARE @len int
SELECT @i=1
SELECT @MAX=@NumofCols
SET @SQL='CREATE TABLE ' + @TableName + '('

WHILE @i<=@MAX

BEGIN
select @j= cast(@i as varchar)
SELECT @SQL= @SQL+'A'+@j  +' NVARCHAR(500) , '
SET @i = @i + 1
END
select @len=len(@SQL)

select  @SQL = substring(@SQL,0,@len-1)


SELECT @SQL= @SQL+ ' )'

exec (@SQL)

END
于 2013-02-06T06:00:11.363 に答える
2

「非ワイド」テーブルあたりの最大列数: 1,024 「ワイド」テーブルあたりの最大列数: 30,000

単一のテーブルごとにこの数が必要な場合は正確には何ですか? パフォーマンスを向上させ、開発を容易にするために、テーブルを垂直方向に数回分割することを強くお勧めします。

于 2012-12-28T14:35:01.833 に答える