SELECT INTOを使用してテーブルをコピーし、IDENTITYプロパティを無視するにはどうすればよいですか?


41

私は次のようなID列を持つテーブルを持っています:

create table with_id (
 id int identity(1,1),
 val varchar(30)
);

これはよく知られています

select * into copy_from_with_id_1 from with_id;

idにIDを持つcopy_from_with_id_1になります。

次のスタックオーバーフローの質問では、すべての列を明示的にリストすることに言及しています。

やってみよう

select id, val into copy_from_with_id_2 from with_id;

この場合でもidはID列です。

私が欲しいのはテーブルのようなものです

create table without_id (
 id int,
 val varchar(30)
);

回答:


52

Books Onlineから

new_tableの形式は、選択リスト内の式を評価することにより決定されます。new_tableの列は、選択リストで指定された順序で作成されます。new_tableの各列には、選択リストの対応する式と同じ名前、データ型、NULL値可能性、および値があります。備考セクションの「ID列の操作」で定義されている条件を除き、列のIDENTITYプロパティが転送されます。

ページを下に:

既存のID列が新しいテーブルに選択されると、次の条件のいずれかに該当しない限り、新しい列はIDENTITYプロパティを継承します。

  • SELECTステートメントには、結合、GROUP BY句、または集計関数が含まれています。
  • 複数のSELECTステートメントは、UNIONを使用して結合されます。
  • ID列は、選択リストに複数回リストされています。
  • ID列は式の一部です。
  • ID列は、リモートデータソースからのものです。

これらの条件のいずれかが真の場合、列はIDENTITYプロパティを継承する代わりにNOT NULLで作成されます。新しいテーブルにID列が必要だが、そのような列が使用できない場合、またはソースID列とは異なるシード値または増分値が必要な場合は、IDENTITY関数を使用して選択リストに列を定義します。以下の「例」セクションの「IDENTITY関数を使用したID列の作成」を参照してください。

だから...あなたは理論的には逃げることができます:

select id, val 
into copy_from_with_id_2 
from with_id

union all

select 0, 'test_row' 
where 1 = 0;

次回誰かがそれを見たときに削除されないように、このコードをコメントして説明することが重要です。


29

Ericsの回答に触発され、テーブル名のみに依存し、特定の列名を使用しない次のソリューションを見つけました。

select * into without_id from with_id where 1 = 0
union all
select * from with_id where 1 = 0
;
insert into without_id select * from with_id;

編集

これを改善することさえ可能です

select * into without_id from with_id
union all
select * from with_id where 1 = 0
;

13

結合を使用して、新しいテーブルを一度に作成および設定できます。

SELECT
  t.*
INTO
  dbo.NewTable
FROM
  dbo.TableWithIdentity AS t
  LEFT JOIN dbo.TableWithIdentity ON 1 = 0
;

1 = 0条件のため、右側には一致がないため、左側の行の重複を防ぎます。これは外部結合であるため、左側の行も削除されません。最後に、これは結合であるため、IDENTITYプロパティは削除されます。

したがって、左側の列だけを選択すると、dbo.TableWithIdentityの正確なコピーがデータごとに、つまりIDENTITYプロパティが削除された状態でのみ生成されます。

そうは言っても、マックスヴァーノンは心に留めておく価値のあるコメントで有効なポイントを挙げました。上記のクエリの実行プランを見ると:

実行計画

ソーステーブルが実行プランで一度だけ言及されていることに気付くでしょう。他のインスタンスはオプティマイザーによって削除されました。

そのため、オプティマイザーが、計画で結合の右側が不要であることを正しく確立できる場合、SQL Serverの将来のバージョンではIDENTITYプロパティが不要であると判断できる可能性があると予測するのが合理的です。クエリプランに従ってソース行セットに別のIDENTITY列がなくなったため、どちらも削除されました。つまり、上記のクエリは、ある時点で期待どおりに機能しなくなる可能性があります。

ただし、ypercubeᵀᴹで正しく指摘されているように、これまでマニュアルでは、結合がある場合にIDENTITYプロパティが保持されないことが明示的に記述されています。

既存のID列が新しいテーブルに選択されると、[...] [t] SELECTステートメントに結合が含まれていない限り、新しい列はIDENTITYプロパティを継承します。

そのため、マニュアルに記載されている限り、おそらく動作は変わらないので安心できます。

チャットで関連トピックを取り上げてくれたShaneisypercubeᵀᴹ称賛送ります。


うまくいくでしょうJOIN (SELECT 1) AS dummy ON 1 = 1か?
ypercubeᵀᴹ


5

このコードを試してください。

SELECT isnull(Tablename_old.IDENTITYCOL + 0, -1) AS 'New Identity Column'
INTO   dbo.TableName_new
FROM   dbo.TableName_old 

このISNULL呼び出しにより、新しい列がNOT NULLnull可能に作成されます。


1
それがあるISNULL()か、+0それをしないこと?または両方が必要ですか?
ypercubeᵀᴹ

3

別の方法を示すためだけに:

リンクサーバーを使用できます

SELECT * 
INTO without_id 
FROM [linked_server].[source_db].dbo.[with_id];

これを使用して、ローカルサーバーへのリンクサーバーを一時的に作成できます。

DECLARE @LocalServer SYSNAME 
SET @LocalServer = @@SERVERNAME;
EXEC master.dbo.sp_addlinkedserver @server = N'localserver'
    , @srvproduct = ''
    , @provider = 'SQLNCLI'
    , @datasrc = @LocalServer;
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'localserver'
    , @useself = N'True'
    , @locallogin = NULL
    , @rmtuser = NULL
    , @rmtpassword = NULL;

その時点でselect * intolocalserverリンクサーバーの4部構成の名前を参照してコードを実行します。

SELECT * 
INTO without_id 
FROM [localserver].[source_db].dbo.[with_id];

それが完了したら、localserverこれでリンクサーバーをクリーンアップします。

EXEC sp_dropserver @server = 'localserver'
    , @droplogins = 'droplogins';

または、OPENQUERY構文を使用できます

SELECT * 
INTO without_id 
FROM OPENQUERY([linked_server], 'SELECT * FROM [source_db].dbo.[with_id]');

1

selectステートメントに結合が含まれている場合、identityプロパティは転送されません。

select a.* into without_id from with_id a inner join with_id b on 1 = 0;

また、(プロパティをid保持しないようにコピーされた列の望ましい動作を与えIDENTITYます。ただし、行をまったくコピーしないという副作用があります!

insert into without_id select * from with_id;

(AakashMに感謝!)


1

簡単な方法は、列を式の一部にすることです。

例:
テーブルdbo.EmployeeのID列にIDがある場合、以下の例では、一時テーブル#tのID列にもIDENTITYがあります。

--temp table has IDENTITY
select ID, Name 
into #t
from dbo.Employee

これを変更してIDに式を適用すると、#tのID列にIDENTITYがなくなります。この場合、ID列に単純な追加を適用します。

--no IDENTITY
select ID = ID + 0, Name 
into #t
from dbo.Employee

他のデータ型の式の他の例には、convert()、string concatenation、またはIsnull()が含まれます。


1
docs.microsoft.com/en-us/sql/t-sql/queries/…から:「既存のID列が新しいテーブルに選択されると、次の条件のいずれかが当てはまらない限り、新しい列はIDENTITYプロパティを継承します。 ... ID列は、列がIDENTITYプロパティを継承するのではなくNULLをしないで作成された...表現の一部である「。
Manngo

1

列がIDENTITYを使用して作成されたかどうかがわからない(または気にしない)テーブルから挿入したい場合があります。作業している整数列ではない場合もあります。この場合、以下が機能します。

SELECT TOP(0) ISNULL([col],NULL) AS [col], ... INTO [table2] FROM [table1]
ALTER TABLE [table2] REBUILD WITH (DATA_COMPRESSION=page)
INSERT INTO [table2] ...

ISNULLはIDENTITY属性を列から削除しますが、元の列と同じ名前とタイプで挿入し、nullを許可しません。TOP(0)は、選択した行を挿入するために使用できる空のテーブルを作成します。必要に応じて、データを挿入する前にテーブルを圧縮することもできます。



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