本文將介紹當您嘗試在 Windows Azure SQL Database 想要使用 SELECT INTO 時,發生這個版本不支援 SELECT INTO 的錯誤時,可能的變通作法。
【情境描述】
當您在 Windows Azure SQL Database(以下簡稱 SQL Database)中使用下列的 T-SQL 指令碼,嘗試複製 ClientBasic 資料表的內容到 ClientBasic_1011130:
select * into ClientBasic_1011130from ClientBasic
您會遇到下列的錯誤訊息:
在 SQL Database 並不完全支援 SQL Server 所有的 T-SQL 功能,而發生這個問題的原因正是 SQL Database 不支援 SELECT INTO 敘述所致。
【實作步驟】
使用 SELECT INTO 最常見的用法就是用來快速複製來源資料表的結構(不包含索引與 Primary Key),偏偏 SQL Database 不支援這樣的寫法,因此筆者使用下列的指令碼來嘗試複製來源資料表的結構:
1: --建立儲存來源資料表 MetaData 的資料表變數
2: declare @MetaData table3: (
4: is_hidden bit not null,5: column_ordinal int not null,6: name sysname NULL,7: is_nullable bit NOT NULL,8: system_type_id int NOT NULL,9: system_type_name nvarchar(256) NULL,10: max_length smallint NOT NULL,11: precision tinyint NOT NULL,12: scale tinyint NOT NULL,13: collation_name sysname NULL,14: user_type_id int NULL,15: user_type_database sysname NULL,16: user_type_schema sysname NULL,17: user_type_name sysname NULL,18: assembly_qualified_type_name nvarchar(4000),
19: xml_collection_id int NULL,20: xml_collection_database sysname NULL,21: xml_collection_schema sysname NULL,22: xml_collection_name sysname NULL,23: is_xml_document bit NOT NULL,24: is_case_sensitive bit NOT NULL,25: is_fixed_length_clr_type bit NOT NULL,26: source_server sysname NULL,27: source_database sysname NULL,28: source_schema sysname NULL,29: source_table sysname NULL,30: source_column sysname NULL,31: is_identity_column bit NULL,32: is_part_of_unique_key bit NULL,33: is_updateable bit NULL,34: is_computed_column bit NULL,35: is_sparse_column_set bit NULL,36: ordinal_in_order_by_list smallint NULL,37: order_by_list_length smallint NULL,38: order_by_is_descending smallint NULL,39: tds_type_id int NOT NULL,40: tds_length int NOT NULL,41: tds_collation_id int NULL,42: tds_collation_sort_id tinyint NULL43: )
44:
45: --準備原始資料表的欄位及資料型態
46: declare @name sysname --欄位名稱47: ,@system_type_name nvarchar(256) --欄位資料型態
48: ,@sql nvarchar(max) = '' --動態建立暫存資料表的SQL字串49:
50: --利用 sp_describe_first_result_set 系統預存程序取得來源資料表的 MetaData
51: insert into @MetaData52: exec sp_describe_first_result_set N'select * from ClientBasic'53:
54: --利用cursor取得組合建立暫存資料表的欄位
55: declare cur_meta_data cursor for56: select name,system_type_name57: from @MetaData58:
59: open cur_meta_data60: fetch next from cur_meta_data into @name,@system_type_name61:
62: while (@@fetch_status = 0)63: begin64: set @sql += iif(@sql = '',@name + ' ' + @system_type_name,' , ' + @name + ' ' + @system_type_name)65: fetch next from cur_meta_data into @name,@system_type_name66: end67:
68: close cur_meta_data69: deallocate cur_meta_data70:
71: --建立暫存資料表
72: set @sql = 'if object_id(''TmpTab'') is not null ' +73: ' drop table TmpTab ' +74: 'create table TmpTab (' + @sql + ')'75: exec (@sql)
在上述指令碼中利用 sp_describe_first_result_set 系統預存程序取得查詢結果的 MetaData,接著將 MetaData 的內容 INSERT 到與 sp_describe_first_result_set 回傳結果集相同的資料表變數,最後利用 Cursor 來組合來源資料表的資料行定義,並搭配 EXEC 敘述來執行動態 T-SQL,進而做到類似 SELECT INTO 複製資料表結構的目的。
光是這樣做還不夠,因為 SQL Database 中的資料表必須都有叢集索引(Clustered Index),因此再利用下列指令碼來取得來源資料表的叢集索引欄位,同樣的使用動態 T-SQL 搭配 EXEC 敘述來建立叢集索引。
1: declare @clustered_index nvarchar(50)2: set @clustered_index = (3: select b.name4: from sys.index_columns a5: join sys.columns b6: on b.object_id = a.object_id7: and a.column_id = b.column_id8: join sys.indexes c9: on a.object_id = c.object_id10: and c.type = 111: where a.object_id = object_id(N'ClientBasic','U')12: )
13:
14: set @sql = N'create clustered index clusteredidx on TmpTab(' + @clustered_index + N')'15:
16: exec (@sql)
最後您就可以使用 INSERT INTO .. FROM 敘述來複製資料表的內容,如下列的指令碼:
insert into TmpTab
select * from ClientBasicselect *
from TmpTab
經過上述步驟,您就可以利用變通的方式來達到與 SELECT INTO 相同目的,快速複製來源資料表的內容到暫存資料表。
【參考資料】