2009-04-03 34 views
9

Tôi muốn để có thể chèn dữ liệu từ một bảng với một cột sắc vào một bảng tạm thời trong SQL Server 2005.Làm thế nào để giảm tài sản SẮC của cột trong SQL Server 2005

Các TSQL trông giống như sau:

-- Create empty temp table 
SELECT * 
INTO #Tmp_MyTable 
FROM MyTable 
WHERE 1=0 
... 
WHILE ... 
BEGIN 
    ... 
    INSERT INTO #Tmp_MyTable 
    SELECT TOP (@n) * 
    FROM MyTable 
    ... 

END 

Đoạn mã trên đã tạo #Tmp_Table với cột nhận dạng và chèn sau thất bại với lỗi "Giá trị rõ ràng cho cột nhận dạng trong bảng '#Tmp_MyTable' chỉ có thể được chỉ định khi danh sách cột được sử dụng và IDENTITY_INSERT BẬT. "

Có cách nào trong TSQL thả thuộc tính nhận dạng của cột trong bảng tạm thời mà không liệt kê tất cả các cột rõ ràng không? Tôi đặc biệt muốn sử dụng "SELECT *" để mã sẽ tiếp tục hoạt động nếu các cột mới được thêm vào MyTable.

Tôi tin rằng việc thả và tạo lại cột sẽ thay đổi vị trí của nó, làm cho nó không thể sử dụng SELECT *.

Cập nhật:

Tôi đã cố gắng sử dụng IDENTITY_INSERT như đề xuất trong một phản ứng. Nó không hoạt động - xem repro bên dưới. Tôi đang làm gì sai?

-- Create test table 
CREATE TABLE [dbo].[TestTable](
    [ID] [numeric](18, 0) IDENTITY(1,1) NOT NULL, 
    [Name] [varchar](50) NULL, 
CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED 
(
    [ID] ASC 
) 
) 
GO 
-- Insert some data 
INSERT INTO TestTable 
(Name) 
SELECT 'One' 
UNION ALL 
SELECT 'Two' 
UNION ALL 
SELECT 'Three' 
GO 
-- Create empty temp table 
SELECT * 
INTO #Tmp 
FROM TestTable 
WHERE 1=0 

SET IDENTITY_INSERT #Tmp ON -- I also tried OFF/ON 
INSERT INTO #Tmp 
SELECT TOP 1 * FROM TestTable 

SET IDENTITY_INSERT #Tmp OFF 
GO 
-- Drop test table 
DROP TABLE [dbo].[TestTable] 
GO 

Lưu ý rằng các thông báo lỗi "Một giá trị rõ ràng cho cột sắc trong bảng '#TmpMyTable' chỉ có thể được xác định khi một danh sách cột được sử dụng và IDENTITY_INSERT ON." - Tôi đặc biệt không muốn sử dụng danh sách cột như được giải thích ở trên.

Cập nhật 2 Cố gắng the suggestion from Mike nhưng điều này đã đưa ra những lỗi tương tự:

-- Create empty temp table 
SELECT * 
INTO #Tmp 
FROM (SELECT 
     m1.* 
     FROM TestTable     m1 
      LEFT OUTER JOIN TestTable m2 ON m1.ID=m2.ID 
     WHERE 1=0 
) dt 

INSERT INTO #Tmp 
SELECT TOP 1 * FROM TestTable 

Đối với lý do tại sao tôi muốn làm điều này: MyTable là một bảng dàn mà có thể chứa một số lượng lớn hàng được sáp nhập vào một bảng khác. Tôi muốn xử lý các hàng từ bảng dàn dựng, chèn/cập nhật bảng chính của tôi và xóa chúng khỏi bảng dàn trong một vòng lặp xử lý N hàng trên mỗi giao dịch. Tôi nhận ra có nhiều cách khác để đạt được điều này.

Cập nhật 3

tôi không thể có được Mike's solution để làm việc, tuy nhiên nó gợi ý các giải pháp sau đây mà không làm việc: tiền tố với một cột không bản sắc và thả các cột sắc:

SELECT CAST(1 AS NUMERIC(18,0)) AS ID2, * 
INTO #Tmp 
FROM TestTable 
WHERE 1=0 
ALTER TABLE #Tmp DROP COLUMN ID 

INSERT INTO #Tmp 
SELECT TOP 1 * FROM TestTable 

Đề xuất của Mike chỉ lưu trữ các khóa trong bảng tạm thời cũng là một gợi ý tốt, mặc dù trong trường hợp cụ thể này, có nhiều lý do tôi thích có tất cả các cột trong bảng tạm thời.

+0

nó làm việc cho tôi, thấy tôi trả lời ... –

+0

Bạn giải pháp trong Cập nhật 3 công trình tuyệt vời cho tôi. Tôi đã cố gắng để có được OUTPUT của một UPDATE vào một bảng tạm thời mà không xác định colums (khoảng 100!) – apc

Trả lời

3

NẾU bạn chỉ là chế biến hàng như bạn mô tả, nó sẽ không thể tốt hơn để chỉ cần chọn N giá trị quan trọng hàng đầu chính vào một bảng tạm thời như:

CREATE TABLE #KeysToProcess 
(
    TempID int not null primary key identity(1,1) 
    ,YourKey1 int not null 
    ,YourKey2 int not null 
) 

INSERT INTO #KeysToProcess (YourKey1,YourKey2) 
SELECT TOP n YourKey1,YourKey2 FROM MyTable 

Các phím không nên thay đổi thường xuyên (Tôi hy vọng) nhưng các cột khác có thể không có hại để làm theo cách này.

được @@ ROWCOUNT của chèn và bạn có thể làm một vòng lặp dễ dàng trên TempID nơi nó sẽ được từ 1 đến @@ ROWCOUNT

và/hoặc

chỉ cần tham gia #KeysToProcess để bàn MyKeys của bạn và đang trên con đường của bạn, không cần sao chép tất cả dữ liệu.

này chạy tốt trên của tôi SQL Server 2005, nơi MyTable.MyKey là một cột sắc.

-- Create empty temp table 
SELECT * 
INTO #TmpMikeMike 
FROM (SELECT 
     m1.* 
     FROM MyTable     m1 
      LEFT OUTER JOIN MyTable m2 ON m1.MyKey=m2.MyKey 
     WHERE 1=0 
) dt 

INSERT INTO #TmpMike 
SELECT TOP 1 * FROM MyTable 

SELECT * from #TmpMike 



EDIT
NÀY CÔNG TRÌNH, không có lỗi ...

-- Create empty temp table 
SELECT * 
INTO #Tmp_MyTable 
FROM (SELECT 
      m1.* 
      FROM MyTable     m1 
       LEFT OUTER JOIN MyTable m2 ON m1.KeyValue=m2.KeyValue 
      WHERE 1=0 
    ) dt 
... 
WHILE ... 
BEGIN 
    ... 
    INSERT INTO #Tmp_MyTable 
    SELECT TOP (@n) * 
    FROM MyTable 
    ... 

END 

tuy nhiên, vấn đề thực sự của bạn là gì? Tại sao bạn cần lặp trong khi chèn "*" vào bảng tạm thời này? Bạn có thể thay đổi chiến lược và đưa ra một thuật toán tốt hơn nhiều.

+0

Không hiệu quả đối với tôi. Tôi đã thử sửa đổi repro của tôi như bạn đã đề xuất, xem ở trên. – Joe

+0

Một tùy chọn khác cho truy vấn lồng nhau sẽ 'CHỌN m1. * TỪ MyTable m1 LEFT OUTER JOIN m2 MyTable ON 1 = 1 WHERE 1 = 0' Sau đó, bạn không cần phải biết gì về các cột trong bảng kết cấu. –

7

Bạn có thể thử

SET IDENTITY_INSERT #Tmp_MyTable ON 
-- ... do stuff 
SET IDENTITY_INSERT #Tmp_MyTable OFF 

Điều này sẽ cho phép bạn chọn vào #Tmp_MyTable mặc dù nó có một cột sắc.

Nhưng này sẽ không công việc:

-- Create empty temp table 
SELECT * 
INTO #Tmp_MyTable 
FROM MyTable 
WHERE 1=0 
... 
WHILE ... 
BEGIN 
    ... 
    SET IDENTITY_INSERT #Tmp_MyTable ON 

    INSERT INTO #Tmp_MyTable 
    SELECT TOP (@n) * 
    FROM MyTable 

    SET IDENTITY_INSERT #Tmp_MyTable OFF 
    ...  
END 

(kết quả trong các lỗi "Một giá trị rõ ràng cho cột sắc trong bảng '#Tmp' chỉ có thể được xác định khi một danh sách cột được sử dụng và IDENTITY_INSERT là ON. ")

Dường như không có cách nào mà không thực sự thả cột - nhưng điều đó sẽ thay đổi thứ tự của các cột như OP đã đề cập. Ugly hack: Tạo một bảng mới dựa trên #Tmp_MyTable ...

Tôi đề nghị bạn viết một thủ tục lưu trữ tạo bảng tạm thời dựa trên tên bảng (MyTable) với cùng các cột (theo thứ tự), nhưng với tài sản nhận dạng bị thiếu.

Bạn có thể sử dụng đoạn mã sau:

select t.name as tablename, typ.name as typename, c.* 
from sys.columns c inner join 
    sys.tables t on c.object_id = t.[object_id] inner join 
    sys.types typ on c.system_type_id = typ.system_type_id 
order by t.name, c.column_id 

để có được một cái nhìn thoáng qua trên phản ánh một cách trình bày TSQL. Tôi tin rằng bạn sẽ phải lặp qua các cột cho bảng được đề cập và thực thi động (được tạo thủ công, được lưu trữ trong chuỗi và sau đó được đánh giá) thay đổi các câu lệnh thành bảng được tạo ra.

Bạn có nhớ gửi một quy trình được lưu trữ như thế cho phần còn lại của thế giới không? Câu hỏi này dường như cũng xuất hiện khá nhiều trong các diễn đàn khác ...

+0

không có nghĩa là sự đảo ngược? lần lượt BẬT đầu tiên ... –

+0

@tvanfosson - michaelpryor là đúng. Bạn cần bật identity_insert để cho phép chèn. Sau đó, tắt nó đi khi hoàn thành. – NYSystemsAnalyst

+0

@NY - đúng, không biết tôi đang nghĩ gì. Đã xóa nhận xét trước đó. – tvanfosson

1

EDIT Toggling IDENTITY_INSERT như được đề xuất bởi Daren chắc chắn là cách tiếp cận trang nhã hơn, trong trường hợp của tôi, tôi cần loại bỏ cột nhận dạng sao cho tôi có thể chèn lại dữ liệu đã chọn vào bảng nguồn

Cách mà tôi đã giải quyết vấn đề này là tạo bảng tạm thời giống như bạn làm, bỏ rõ ràng cột nhận dạng và sau đó tự động tạo sql để tôi có danh sách cột loại trừ cột nhận dạng (như trong trường hợp của bạn, do đó, proc sẽ vẫn hoạt động nếu có thay đổi đối với giản đồ) và sau đó thực thi sql dưới đây là một mẫu

declare @ret int 
Select * into #sometemp from sometable 
Where 
id = @SomeVariable 

Alter Table #sometemp Drop column SomeIdentity 

Select @SelectList = '' 
Select @SelectList = @SelectList 
+ Coalesce('[' + Column_name + ']' + ', ' ,'') 
from information_schema.columns 
where table_name = 'sometable' 
and Column_Name <> 'SomeIdentity' 

Set @SelectList = 'Insert into sometable (' 
+ Left(@SelectList, Len(@SelectList) -1) + ')' 
Set @SelectList = @SelectList 
+ ' Select * from #sometemp ' 
exec @ret = sp_executesql @selectlist 
0

Tôi đã viết thủ tục này như biên soạn nhiều câu trả lời tự động và bản sắc thả cột nhanh:

CREATE PROCEDURE dbo.sp_drop_table_identity @tableName VARCHAR(256) AS 
BEGIN 
    DECLARE @sql VARCHAR (4096); 
    DECLARE @sqlTableConstraints VARCHAR (4096); 
    DECLARE @tmpTableName VARCHAR(256) = @tableName + '_noident_temp'; 

    BEGIN TRANSACTION 

    -- 1) Create temporary table with edentical structure except identity 
    -- Idea borrowed from https://stackoverflow.com/questions/21547/in-sql-server-how-do-i-generate-a-create-table-statement-for-a-given-table 
    -- modified to ommit Identity and honor all constraints, not primary key only! 
    SELECT 
     @sql = 'CREATE TABLE [' + so.name + '_noident_temp] (' + o.list + ')' 
     + ' ' + j.list 
    FROM sysobjects so 
    CROSS APPLY (
     SELECT 
      ' [' + column_name + '] ' 
      + data_type 
      + CASE data_type 
       WHEN 'sql_variant' THEN '' 
       WHEN 'text' THEN '' 
       WHEN 'ntext' THEN '' 
       WHEN 'xml' THEN '' 
       WHEN 'decimal' THEN '(' + CAST(numeric_precision as VARCHAR) + ', ' + CAST(numeric_scale as VARCHAR) + ')' 
       ELSE COALESCE('(' + CASE WHEN character_maximum_length = -1 THEN 'MAX' ELSE CAST(character_maximum_length as VARCHAR) END + ')', '') 
      END 
      + ' ' 
      /* + case when exists (-- Identity skip 
      select id from syscolumns 
      where object_name(id)=so.name 
      and name=column_name 
      and columnproperty(id,name,'IsIdentity') = 1 
      ) then 
      'IDENTITY(' + 
      cast(ident_seed(so.name) as varchar) + ',' + 
      cast(ident_incr(so.name) as varchar) + ')' 
      else '' 
      end + ' ' */ 
      + CASE WHEN IS_NULLABLE = 'No' THEN 'NOT ' ELSE '' END 
      + 'NULL' 
      + CASE WHEN information_schema.columns.column_default IS NOT NULL THEN ' DEFAULT ' + information_schema.columns.column_default ELSE '' END 
      + ',' 
     FROM 
      INFORMATION_SCHEMA.COLUMNS 
     WHERE table_name = so.name 
     ORDER BY ordinal_position 
     FOR XML PATH('') 
    ) o (list) 
    CROSS APPLY(
     SELECT 
      CHAR(10) + 'ALTER TABLE ' + @tableName + '_noident_temp ADD ' + LEFT(alt, LEN(alt)-1) 
     FROM(
      SELECT 
       CHAR(10) 
       + ' CONSTRAINT ' + tc.constraint_name + '_ni ' + tc.constraint_type + ' (' + LEFT(c.list, LEN(c.list)-1) + ')' 
       + COALESCE(CHAR(10) + r.list, ', ') 
      FROM 
       information_schema.table_constraints tc 
       CROSS APPLY(
        SELECT 
         '[' + kcu.column_name + '], ' 
        FROM 
         information_schema.key_column_usage kcu 
        WHERE 
         kcu.constraint_name = tc.constraint_name 
        ORDER BY 
         kcu.ordinal_position 
        FOR XML PATH('') 
       ) c (list) 
       OUTER APPLY(
        -- https://stackoverflow.com/questions/3907879/sql-server-howto-get-foreign-key-reference-from-information-schema 
        SELECT 
         ' REFERENCES [' + kcu1.constraint_schema + '].' + '[' + kcu2.table_name + ']' + '([' + kcu2.column_name + ']) ' 
         + CHAR(10) 
         + ' ON DELETE ' + rc.delete_rule 
         + CHAR(10) 
         + ' ON UPDATE ' + rc.update_rule + ', ' 
        FROM information_schema.referential_constraints as rc 
         JOIN information_schema.key_column_usage as kcu1 ON (kcu1.constraint_catalog = rc.constraint_catalog AND kcu1.constraint_schema = rc.constraint_schema AND kcu1.constraint_name = rc.constraint_name) 
         JOIN information_schema.key_column_usage as kcu2 ON (kcu2.constraint_catalog = rc.unique_constraint_catalog AND kcu2.constraint_schema = rc.unique_constraint_schema AND kcu2.constraint_name = rc.unique_constraint_name AND kcu2.ordinal_position = KCU1.ordinal_position) 
        WHERE 
         kcu1.constraint_catalog = tc.constraint_catalog AND kcu1.constraint_schema = tc.constraint_schema AND kcu1.constraint_name = tc.constraint_name 
       ) r (list) 
      WHERE tc.table_name = @tableName 
      FOR XML PATH('') 
     ) a (alt) 
    ) j (list) 
    WHERE 
     xtype = 'U' 
    AND name NOT IN ('dtproperties') 
    AND so.name = @tableName 

    SELECT @sql as '1) @sql'; 
    EXECUTE(@sql); 

    -- 2) Obtain current back references on our table from others to reenable it later 
    -- https://stackoverflow.com/questions/3907879/sql-server-howto-get-foreign-key-reference-from-information-schema 
    SELECT 
     @sqlTableConstraints = (
      SELECT 
       'ALTER TABLE [' + kcu1.constraint_schema + '].' + '[' + kcu1.table_name + ']' 
       + ' ADD CONSTRAINT ' + kcu1.constraint_name + '_ni FOREIGN KEY ([' + kcu1.column_name + '])' 
       + CHAR(10) 
       + ' REFERENCES [' + kcu2.table_schema + '].[' + kcu2.table_name + ']([' + kcu2.column_name + '])' 
       + CHAR(10) 
       + ' ON DELETE ' + rc.delete_rule 
       + CHAR(10) 
       + ' ON UPDATE ' + rc.update_rule + ' ' 
      FROM information_schema.referential_constraints as rc 
       JOIN information_schema.key_column_usage as kcu1 ON (kcu1.constraint_catalog = rc.constraint_catalog AND kcu1.constraint_schema = rc.constraint_schema AND kcu1.constraint_name = rc.constraint_name) 
       JOIN information_schema.key_column_usage as kcu2 ON (kcu2.constraint_catalog = rc.unique_constraint_catalog AND kcu2.constraint_schema = rc.unique_constraint_schema AND kcu2.constraint_name = rc.unique_constraint_name AND kcu2.ordinal_position = KCU1.ordinal_position) 
      WHERE 
       kcu2.table_name = 'department' 
      FOR XML PATH('') 
     ); 
    SELECT @sqlTableConstraints as '8) @sqlTableConstraints'; 
    -- Execute at end 

    -- 3) Drop outer references for switch (structure must be identical: http://msdn.microsoft.com/en-gb/library/ms191160.aspx) and rename table 
    SELECT 
     @sql = (
      SELECT 
       ' ALTER TABLE [' + kcu1.constraint_schema + '].' + '[' + kcu1.table_name + '] DROP CONSTRAINT ' + kcu1.constraint_name 
      FROM information_schema.referential_constraints as rc 
       JOIN information_schema.key_column_usage as kcu1 ON (kcu1.constraint_catalog = rc.constraint_catalog AND kcu1.constraint_schema = rc.constraint_schema AND kcu1.constraint_name = rc.constraint_name) 
       JOIN information_schema.key_column_usage as kcu2 ON (kcu2.constraint_catalog = rc.unique_constraint_catalog AND kcu2.constraint_schema = rc.unique_constraint_schema AND kcu2.constraint_name = rc.unique_constraint_name AND kcu2.ordinal_position = KCU1.ordinal_position) 
      WHERE 
       kcu2.table_name = @tableName 
      FOR XML PATH('') 
     ); 
    SELECT @sql as '3) @sql' 
    EXECUTE (@sql); 

    -- 4) Switch partition 
    -- http://www.calsql.com/2012/05/removing-identity-property-taking-more.html 
    SET @sql = 'ALTER TABLE ' + @tableName + ' switch partition 1 to ' + @tmpTableName; 
    SELECT @sql as '4) @sql'; 
    EXECUTE(@sql); 

    -- 5) Rename real old table to bak 
    SET @sql = 'EXEC sp_rename ' + @tableName + ', ' + @tableName + '_bak'; 
    SELECT @sql as '5) @sql'; 
    EXECUTE(@sql); 

    -- 6) Rename temp table to real 
    SET @sql = 'EXEC sp_rename ' + @tmpTableName + ', ' + @tableName; 
    SELECT @sql as '6) @sql'; 
    EXECUTE(@sql); 

    -- 7) Drop bak table 
    SET @sql = 'DROP TABLE ' + @tableName + '_bak'; 
    SELECT @sql as '7) @sql'; 
    EXECUTE(@sql); 

    -- 8) Create again doped early constraints 
    SELECT @sqlTableConstraints as '8) @sqlTableConstraints'; 
    EXECUTE(@sqlTableConstraints); 


    -- It still may fail if there references from objects with WITH CHECKOPTION 
    -- it may be recreated - https://stackoverflow.com/questions/1540988/sql-2005-force-table-rename-that-has-dependencies 
    COMMIT 
END 

Sử dụng khá đơn giản:

EXEC sp_drop_table_identity @tableName = 'some_very_big_table' 

Lợi ích và hạn chế:

  1. Sử dụng câu lệnh switch partition (áp dụng cho các bảng không được phân đoạn) cho di chuyển nhanh mà không sao chép toàn bộ dữ liệu. Nó cũng áp dụng một số điều kiện để áp dụng.
  2. Nó thực hiện trên bản sao bảng bay mà không có nhận dạng. Giải pháp như vậy I also post separately và nó cũng có thể cần điều chỉnh trên các cấu trúc không tầm thường như các trường hợp (nó bao gồm các nhu cầu của tôi).
  3. Nếu bảng có trong các đối tượng có lược đồ bị ràng buộc bởi CHECKOUPTION (sp, số lượt xem), nó ngăn không cho chuyển đổi (xem chú thích cuối cùng trong mã). Nó có thể được thêm vào kịch bản để tạm thời thả như vậy ràng buộc. Tôi chưa làm điều đó.

Tất cả phản hồi đều được chào đón.

0

cách hiệu quả nhất để thả các cột sắc (đặc biệt đối với cơ sở dữ liệu lớn) trên SQL Server là để sửa đổi siêu dữ liệu DDL trực tiếp, trên SQL Server cũ hơn 2005 này có thể được thực hiện với:

sp_configure 'allow update', 1 
go 
reconfigure with override 
go 

update syscolumns set colstat = 0 --turn off bit 1 which indicates identity column 
where id = object_id('table_name') and name = 'column_name' 
go 

exec sp_configure 'allow update', 0 
go 
reconfigure with override 
go 

SQL Server 2005 + không hỗ trợ cấu hình lại với ghi đè, nhưng bạn có thể thực hiện truy vấn quảng cáo khi phiên bản SQL Server bắt đầu ở chế độ người dùng đơn lẻ (bắt đầu phiên bản db với cờ -m, tức là "C: \ Program Files \ Microsoft SQL Server \ MSSQL10.MSSQLSERVER \ MSSQL \ Binn \ sqlservr.exe -m ", đảm bảo chạy với tư cách Quản trị viên) bằng Bảng điều khiển dành riêng cho quản trị viên (từ SQL Management Studio kết nối với QUẢN TRỊ: tiền tố, tức là ADMIN: MyDatabase). Cột metdata được lưu trữ trong bảng nội sys.sysschobjs (không hiển thị mà không DAC):

use myDatabase 

update sys.syscolpars set status = 1, idtval = null -- status=1 - primary key, idtval=null - remove identity data 
where id = object_id('table_name') AND name = 'column_name' 

Thông tin thêm về phương pháp này on this blog

+0

Không cần phải khởi động cá thể trong chế độ người dùng đơn lẻ và hack các bảng hệ thống cho việc này. Nó có thể được thực hiện như một siêu dữ liệu chỉ hoạt động với 'thay đổi bảng chuyển đổi' http://stackoverflow.com/questions/6084572/sql-server-how-to-set-auto-increment-after-creating-a-table-without -data-loss/6086661 # 6086661 –

Các vấn đề liên quan