问题
EDIT: Database names have been modified for simplicity
I'm trying to get some dynamic sql in place to update static copies of some key production tables into another database (sql2008r2). The aim here is to allow consistent dissemination of data (from the 'static' database) for a certain period of time as our production databases are updated almost daily.
I am using a CURSOR
to loop through a table that contains the objects that are to be copied into the 'static' database.
The prod
tables don't change that frequently, but I'd like to make this somewhat "future proof" (if possible!) and extract the columns names from INFORMATION_SCHEMA.COLUMNS
for each object (instead of using SELECT * FROM ...
)
1) From what I have read in other posts,
EXEC()
seems limiting, so I believe that I'll need to useEXEC sp_executesql
but I'm having a little trouble getting my head around it all.2) As an added extra, if at all possible, i'd also like to exclude some columns for particular tables (structures vary slightly in the 'static' database)
here's what i have so far.
when executed, @colnames
returns NULL and therefore @sql
returns NULL...
could someone guide me to where i might find a solution?
any advice or help with this code is much appreciated.
CREATE PROCEDURE sp_UpdateRefTables
@debug bit = 0
AS
declare @proddbname varchar(50),
@schemaname varchar(50),
@objname varchar(150),
@wherecond varchar(150),
@colnames varchar(max),
@sql varchar(max),
@CRLF varchar(2)
set @wherecond = NULL;
set @CRLF = CHAR(10) + CHAR(13);
declare ObjectCursor cursor for
select databasename,schemaname,objectname
from Prod.dbo.ObjectsToUpdate
OPEN ObjectCursor ;
FETCH NEXT FROM ObjectCursor
INTO @proddbname,@schemaname,@objname ;
while @@FETCH_STATUS=0
begin
if @objname = 'TableXx'
set @wherecond = ' AND COLUMN_NAME != ''ExcludeCol1'''
if @objname = 'TableYy'
set @wherecond = ' AND COLUMN_NAME != ''ExcludeCol2'''
--extract column names for current object
select @colnames = coalesce(@colnames + ',', '') + QUOTENAME(column_name)
from Prod.INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = + QUOTENAME(@objname,'') + isnull(@wherecond,'')
if @debug=1 PRINT '@colnames= ' + isnull(@colnames,'null')
--replace all data for @objname
--@proddbname is used as schema name in Static database
SELECT @sql = 'TRUNCATE TABLE ' + @proddbname + '.' + @objname + '; ' + @CRLF
SELECT @sql = @sql + 'INSERT INTO ' + @proddbname + '.' + @objname + ' ' + @CRLF
SELECT @sql = @sql + 'SELECT ' + @colnames + ' FROM ' + @proddbname + '.' + @schemaname + '.' + @objname + '; '
if @debug=1 PRINT '@sql= ' + isnull(@sql,'null')
EXEC sp_executesql @sql
FETCH NEXT FROM ObjectCursor
INTO @proddbname,@schemaname,@objname ;
end
CLOSE ObjectCursor ;
DEALLOCATE ObjectCursor ;
P.S. i have read about sql injection, but as this is an internal admin task, i'm guessing i'm safe here!? any advice on this is also appreciated.
many thanks in advance.
回答1:
You have a mix of SQL and dynamic SQL in your query against information_schema
. Also QUOTENAME
isn't necessary in the where clause and will actually prevent a match at all, since SQL Server stores column_name
, not [column_name]
, in the metadata. Finally, I'm going to change it to sys.columns
since this is the way we should be deriving metadata in SQL Server. Try:
SELECT @colnames += ',' + name
FROM Prod.sys.columns
WHERE OBJECT_NAME([object_id]) = @objname
AND name <> CASE WHEN @objname = 'TableXx' THEN 'ExcludeCol1' ELSE '' END
AND name <> CASE WHEN @objname = 'TableYy' THEN 'ExcludeCol2' ELSE '' END;
SET @colnames = STUFF(@colnames, 1, 1, '');
来源:https://stackoverflow.com/questions/10190422/sql-update-with-dynamic-column-names