web-dev-qa-db-ja.com

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

私は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)
);
43
bernd_k

から Books Online

New_tableのフォーマットは、選択リストの式を評価することによって決定されます。 new_tableの列は、選択リストで指定された順序で作成されます。 new_tableの各列は、選択リストの対応する式と同じ名前、データ型、null可能性、および値を持っています。 列のIDENTITYプロパティが転送されます。ただし、「解説」セクションの「ID列の操作」で定義されている条件を除きます。

ページの下:

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

  • SELECTステートメントに、結合、GROUP BY句、または集計関数が含まれています。
  • UNIONを使用して、複数のSELECTステートメントを結合します。
  • 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;

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

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
;
31
bernd_k

結合を使用して、新しいテーブルを一度に作成してデータを入力できます。

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

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

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

言われていることすべて、 Max Vernon は、覚えておく価値のあるコメントの有効なポイントを上げました。上記のクエリの実行プランを見ると:

Execution plan

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

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

しかし、 ypercubeᵀᴹ で正しく指摘されているように、これまでのところ the manual は、結合がある場合、IDENTITYプロパティは保持されないことを明示的に述べています。

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

したがって、マニュアルで言及し続けている限り、動作は同じままであることを確信できます。

Shaneis および ypercubeᵀᴹ への称賛:チャットで関連トピックを表示します

13
Andriy M

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

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

ISNULL呼び出しにより、新しい列がNOT NULL nullabilityで作成されます。

6
Saurav Ghosh

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

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

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 * intoコード、localserverリンクサーバーを参照する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]');
3
bernd_k

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

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
anon-99

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

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

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

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

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

他のデータ型の式の他の例には、convert()、文字列連結、またはIsnull()

1
FistOfFury

列が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)は、選択した行を挿入するために使用できる空のテーブルを作成します。必要に応じて、データを挿入する前にテーブルを圧縮することもできます。

1
Tony
select convert(int, id) as id, val 
into copy_from_with_id_without_id 
from with_id;

アイデンティティを削除します。

欠点は、idがnull可能になることですが、その制約を追加できます。

0
john hunter