テーブルのID列の明示的な値は、列リストが使用され、IDENTITY_INSERTがONの場合にのみ指定できますSQL Server


187

このクエリを実行しようとしています

INSERT INTO dbo.tbl_A_archive
  SELECT *
  FROM SERVER0031.DB.dbo.tbl_A

でも走った後でも

set identity_insert dbo.tbl_A_archive on

このエラーメッセージが表示されます

テーブル 'dbo.tbl_A_archive'のID列の明示的な値は、列リストが使用され、IDENTITY_INSERTがONの場合にのみ指定できます。

tbl_A行と幅の巨大なテーブルです。つまり、列がたくさんあります。すべての列を手動で入力する必要はありません。これを機能させるにはどうすればよいですか?


ちなみにリンクサーバーはもうセットアップ済み!
jhowe

4
「X select * Yに挿入」がテーブルスキーマを変更するまで問題にならなかったことを除いて、私もこの問題を抱えています
FistOfFury

回答:


81

概要

SQL Serverでは、列リストを使用しない限り、ID列に明示的な値を挿入できません。したがって、次のオプションがあります。

  1. 列リストを作成します(手動またはツールを使用。以下を参照)

または

  1. ID列をtbl_A_archive通常の非ID列に作成する:テーブルがアーカイブテーブルであり、ID列に常に明示的な値を指定している場合、ID列が必要なのはなぜですか?代わりに通常のintを使用してください。

ソリューション1の詳細

の代わりに

SET IDENTITY_INSERT archive_table ON;

INSERT INTO archive_table
  SELECT *
  FROM source_table;

SET IDENTITY_INSERT archive_table OFF;

あなたは書く必要があります

SET IDENTITY_INSERT archive_table ON;

INSERT INTO archive_table (field1, field2, ...)
  SELECT field1, field2, ...
  FROM source_table;

SET IDENTITY_INSERT archive_table OFF;

field1, field2, ...あなたのテーブルのすべての列の名前を含みます。その列のリストを自動生成する場合は、Daveの回答またはAndomarの回答をご覧ください


ソリューション2の詳細

残念ながら、identity intカラムの「タイプを変更」するだけでは、identity以外のintカラムにすることはできません。基本的に、次のオプションがあります。

  • アーカイブテーブルにまだデータが含まれていない場合は、列を削除し、IDのない新しい列を追加します。

または

  • SQL Server Management Studioを使用して、アーカイブテーブルのID列のIdentity Specification/ (Is Identity)プロパティをに設定しますNo。これにより、テーブルを再作成して既存のデータをコピーするスクリプトが作成されます。そのためには、Tools/ Options/ Designers/ Table and Database Designers/の設定を解除する必要もありますPrevent saving changes that require table re-creation

または


1
あなたの一番下の解決策:列に「IDENTITY(1、1)」またはこのようなものがある場合、一時的に削除する必要があります。
Aleksandr Khomenko

1
@AleksandrKhomenko:はい、それは私が「それを通常の(非IDの)int列にする」という意味でした。
ハインツィ

1
混乱させて申し訳ありません。実際、彼は自動インクリメントプロパティも削除する必要があることを意味しました。SQL-Serverの場合は "IDENTITY(1、1)"と呼ばれます-あなたの答えは完全に正しいです。しかし、MySQLとOracleには別のコマンドがあります(そして、自明ではありません。w3schools.com/ sql / sql_autoincrement.aspを参照してください
Aleksandr Khomenko

2
これは詳細に欠けています。一部の人にとっては悪徳かもしれませんが、他の人にとってはナンセンス
です

1
@ DJ:私はいくつかの詳細を追加しました。
ハインツィ

332
SET IDENTITY_INSERT tableA ON

INSERTステートメントの列リストを作成する必要があります。

INSERT Into tableA ([id], [c2], [c3], [c4], [c5] ) 
SELECT [id], [c2], [c3], [c4], [c5] FROM tableB

「INSERT Into tableA SELECT ........」のようではありません

SET IDENTITY_INSERT tableA OFF

17
+1 ...クエリのSELECT句で明示的な列のみが必要です。クエリのINSERT句にアスタリスクを付けることができます
MacGyver

8
...ターゲットにID列があり、ソーステーブルにID列がない場合を除いて
MacGyver

1
この答えは、ハインツィよりも少し明確です。SETIDENTITY_INSERT
Marty

4
+1魅力のように機能します!ただし、「クエリのINSERT句にアスタリスクを付けることができます」はここでは機能しません。
Shai Alon 2016年

1
select句の列が宛先テーブルと一致しない場合、このエラーが発生しました。これを確認して、挿入は私のために働きました
ホセ

39

ただでテーブルを右クリック-あなたは、SQL Server Management Studioを使用している場合は、列リストを自分で入力する必要はありません、オブジェクトエクスプローラと選択してスクリプトテーブルを - > にSELECT - > 新しいクエリエディタウィンドウ

そうでない場合は、次のようなクエリが出発点として役立ちます。

SELECT SUBSTRING(
    (SELECT ', ' + QUOTENAME(COLUMN_NAME)
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = 'tbl_A'
        ORDER BY ORDINAL_POSITION
        FOR XML path('')),
    3,
    200000);

2
これを変数に選択して、動的SQLクエリで使用できます。あなたは私の日を救った。どうもありがとう!
Pawel Cioch、2015

1
ありがとう!私はそれでこれを作りましたgist.github.com/timabell/0ddd6a69565593f907c7
Tim Abell

ワオ。クエリを動的に構築するために、これを必死に探していました。ここでこれを見て驚いた。どうも。
Yasin Bilir 16

24

ハインジの答えに同意します。最初の2番目のオプションの場合、テーブル内の列のコンマ区切りリストを生成するクエリを次に示します。

select name + ', ' as [text()] 
from sys.columns 
where object_id = object_id('YourTable') 
for xml path('')

大きなテーブルの場合、これは入力作業の多くを節約できます:)


@ありがとう、本当に役に立ちました。:)
Chirag Thakar

場合によっては、実用的なソリューションが必要なだけです。ありがとうございました!
トビアスフェイル

15

「アーカイブ」テーブルがメインテーブルの正確なコピーである場合は、IDがID列であることを削除することをお勧めします。そうすれば、それらを挿入できます。

または、次のステートメントを使用して、テーブルのID挿入を許可および禁止できます。

SET IDENTITY_INSERT tbl_A_archive ON
--Your inserts here
SET IDENTITY_INSERT tbl_A_archive OFF

最後に、ID列をそのまま機能させる必要がある場合は、常にストアドプロシージャを実行するだけで済みます。

sp_columns tbl_A_archive 

これにより、テーブルからすべての列が返され、クエリに切り取って貼り付けることができます。(これはほとんど常に*を使用するよりも優れています)


11

SQLステートメントの場合は、列リストも指定する必要があります。たとえば

INSERT INTO tbl (idcol1,col2) VALUES ( value1,value2)

の代わりに

INSERT INTO tbl VALUES ( value1,value2)

2
これはOPのステートメントには当てはまりません。INSERT INTOの後にSELECTステートメントが続く場合、VALUESは必要ありません。回答を修正または削除してください。
ゲイリー

4

どちらも機能しますが、#1を使用してもエラーが発生する場合は、#2に進んでください。

1)

SET IDENTITY_INSERT customers ON
GO
insert into dbo.tbl_A_archive(id, ...)
SELECT Id, ...
FROM SERVER0031.DB.dbo.tbl_A

2)

SET IDENTITY_INSERT customers ON
GO
insert into dbo.tbl_A_archive(id, ...)
VALUES(@Id,....)

4
OPが複数のレコードを挿入している場合、これは実際の方法ではありません。あなたは「tbl_Aは行と幅が巨大なテーブルです。つまり、列がたくさんあります。すべての列を手動で入力する必要はありません」。
ゲイリー

4

この質問で言及されているソリューションのSelectステートメントのすべての列名をカンマ区切りのリストに入力するために、以下のオプションを使用します。これらのオプションは、ここでのほとんどの応答よりも冗長ではないためです。ただし、ここでのほとんどの応答はまだ完全に受け入れ可能です。

1)

SELECT column_name + ',' 
FROM   information_schema.columns 
WHERE  table_name = 'YourTable'

2) SQL Server SSMSがある場合、これはおそらく列を作成する最も簡単な方法です。

1)オブジェクトエクスプローラーでテーブルに移動し、テーブル名の左側にある+をクリックするか、テーブル名をダブルクリックしてサブリストを開きます。

2)列サブフォルダーをメインのクエリ領域にドラッグすると、列リスト全体が自動貼り付けされます。


3

Identity列がある場合は、挿入する列名を指定する必要があります。したがって、コマンドは次のようになります。

SET IDENTITY_INSERT DuplicateTable ON

INSERT Into DuplicateTable ([IdentityColumn], [Column2], [Column3], [Column4] ) 
SELECT [IdentityColumn], [Column2], [Column3], [Column4] FROM MainTable

SET IDENTITY_INSERT DuplicateTable OFF

テーブルに多くの列がある場合、このコマンドを使用してそれらの列名を取得します。

SELECT column_name + ','
FROM   information_schema.columns 
WHERE  table_name = 'TableName'
for xml path('')

(最後のコンマ( '、')を削除した後)過去の列名をコピーするだけです。


3

これはうまくいくはずです。私はちょうどあなたの問題に遭遇しました:

SET IDENTITY_INSERT dbo.tbl_A_archive ON;
INSERT INTO     dbo.tbl_A_archive (IdColumn,OtherColumn1,OtherColumn2,...)
SELECT  *
FROM        SERVER0031.DB.dbo.tbl_A;
SET IDENTITY_INSERT dbo.tbl_A_archive OFF;

残念ながら、IDを指定するレコードを挿入するには、ID列を含む列のリストが必要なようです。 ただし、SELECTで列をリストする必要はありません。以下のよう@Dave Cluderayは、あなたがコピーしてペーストするために、これは(未満200000文字場合)フォーマットされたリストになります示唆しました。

インスタンスを切り替えるため、USEを追加しました。

USE PES
SELECT SUBSTRING(
    (SELECT ', ' + QUOTENAME(COLUMN_NAME)
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = 'Provider'
        ORDER BY ORDINAL_POSITION
        FOR XML path('')),
    3,
    200000);

3
SET IDENTITY_INSERT tableA ON

INSERT Into tableA ([id], [c2], [c3], [c4], [c5] ) 
SELECT [id], [c2], [c3], [c4], [c5] FROM tableB

好きじゃない

INSERT INTO tableA
SELECT * FROM tableB

SET IDENTITY_INSERT tableA OFF

2

このコードスニペットは、ID主キー列がオンのときにテーブルに挿入する方法を示しています。

SET IDENTITY_INSERT [dbo].[Roles] ON
GO
insert into Roles (Id,Name) values(1,'Admin')
GO
insert into Roles (Id,Name) values(2,'User')
GO
SET IDENTITY_INSERT [dbo].[Roles] OFF
GO

1

ストアドプロシージャを使用して、あるテーブルから別のテーブルに値を挿入する場合。私はこれこれを使用しましたが、後者はほとんどAndomarの答えに似ています。

CREATE procedure [dbo].[RealTableMergeFromTemp]
    with execute as owner
AS
BEGIN
BEGIN TRANSACTION RealTableDataMerge
SET XACT_ABORT ON

    DECLARE @columnNameList nvarchar(MAX) =
     STUFF((select ',' + a.name
      from sys.all_columns a
      join sys.tables t on a.object_id = t.object_id 
       where t.object_id = object_id('[dbo].[RealTable]') 
    order by a.column_id
    for xml path ('')
    ),1,1,'')

    DECLARE @SQLCMD nvarchar(MAX) =N'INSERT INTO [dbo].[RealTable] (' + @columnNameList + N') SELECT * FROM [#Temp]'

    SET IDENTITY_INSERT [dbo].[RealTable] ON;
    exec(@sqlcmd)
    SET IDENTITY_INSERT [dbo].[RealTable] OFF

COMMIT TRANSACTION RealTableDataMerge
END

GO

0

自動インクリメントプロパティを持つ1つ以上の列があるか、その属性の値が制約として計算されます。その列を変更しようとしています。

それを解決するには2つの方法があります。1)他の列に明示的に言及し、それらの値のみを設定すると、PrimaryKeyまたは自動インクリメント列の値が自動的に設定されます。

2)INDENTITY_INSERTをオンにしてから、挿入クエリを実行して、最終的にIDENTITY_INSERTをオフにすることができます。

提案:より適切で効率的なアプローチであるため、最初のステップに従ってください。

詳細については、SQLヘルパーに関するこの記事を参照してください


0

レコードを選択するテーブルの列名、データ型、および順序が宛先テーブルと完全に同じであることを確認してください。唯一の違いは、宛先テーブルには最初の列としてID列があり、ソーステーブルにはないことです。

「INSERT INTO table_Dest SELECT * FROM table_source_linked_server_excel」を実行していたときに、同様の問題に直面していました。テーブルには115列ありました。

Excelから(リンクサーバーとして)データをデータベースのテーブルに読み込むようなテーブルが2つありました。データベーステーブルに、ソースExcelにはない 'id'というID列を追加しました。1つのテーブルでクエリが正常に実行され、別のテーブルで、「テーブルのID列の明示的な値は、列リストが使用され、IDENTITY_INSERTがSQL Serverにある場合にのみ指定できます」というエラーが発生しました。シナリオは両方のクエリでまったく同じだったので、これは不可解でした。だから私はこれを調査し、私が見つけたのはINSERT INTO .. SELECT *でエラーが発生していたクエリでした:

  1. 値は正しかったが、ソーステーブルの一部の列名が変更された
  2. SELECT *によって選択されている実際のデータ列を超えるいくつかの追加の列がありました。ソースExcelテーブル(リンクサーバーの下)で、[スクリプトテーブル]> [選択先]> [新しいクエリウィンドウ]のオプションを使用してこれを発見しました。Excelの最後の列の直後に非表示の列が1つありましたが、データがありませんでした。ソースのExcelテーブルでその列を削除して保存しました。

上記の2つの変更を行った後、INSERT INTO ... SELECT *のクエリは正常に実行されました。宛先テーブルのID列は、挿入された各行のID値を期待どおりに生成しました。

したがって、宛先テーブルにソーステーブルにはないID列がある場合でも、名前とデータ型、およびソースと宛先の列の順序がまったく同じであれば、INSERT INTO .. SELECT *は正常に実行されます。

それが誰かを助けることを願っています。


-1

このエラーは、テーブル定義の列数と挿入クエリの列数の不一致が原因で発生すると思います。また、入力した値では列の長さが省略されます。この問題を解決するには、テーブルの定義を確認してください


私のために働いた:私はデザインからテーブルに列を追加するのを忘れました、他の答えを試す前にこれを最初にチェックするのは良いことです。
Exel Gamboa
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.