sql update with dynamic column names

2019-08-13 19:03发布

问题:

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 use EXEC 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, '');