June 5, 2012 at 1:13 pm
Comments posted to this topic are about the item MERGE Statement Generator
June 21, 2012 at 3:59 pm
Mitch, your Merge Generator inspired me to take a deeper look into the new functionality.
I have tweaked the script to include a few more features:
- The schema is no longer assumed to be dbo, instead it is a parameter
- You can add a linked server so if the source data is on a different server, say you are syncing QA from Production
- You can remove the linked server, so if you don't wish to leave production access on the target server
- Execution is now part of the script and can be toggled, the script will still print regardless
- I added a check in the script execution to see if there was an identity column so that the "Set Identity_Insert" will not be added if an Identity column was not present.
- I added the removal of computed columns as these are not able to merge
- I added debug print statements to see where things may be breaking down, this also can be toggled
- I removed the create procedure portion, as I am still thinking through the way I want to automate this. It can be added back very easily though...
I may end up putting it back in a proc, but for now the proc would have to exist in every data base that I want to sync. My next plans are to script all tables to run thru the proc to perform the merge.
I think that is all... It's pretty fast I just ran it against my QA system pulling data from production and inserted 13.5 M rows in a little over 6 minutes.
***Edit***
Put it back into a proc so I can use it when scripting tables, and had to move the linked server portion to address the queries that are pulling the column information.
ALTER PROC up_MergeTable
(
?@pSourceDB VARCHAR(255) = NULL,
@pTargetDB as varchar(255) = NULL,
@pSchema AS VARCHAR(255) = 'dbo',
????@pSourceTableName as varchar(255) = NULL,
????@pTargetTableName as varchar(255) = NULL,
@pLinkedServer AS NVARCHAR(255) = NULL,
@pDomain AS NVARCHAR(255) = NULL,
@pDisableTriggers BIT = 0,
@pRemoveLinkedServer BIT = 0,
@pEXECUTE BIT = 0,
@pDebug BIT = 0
)
AS
DECLARE?@SourceDB VARCHAR(255) = QUOTENAME(@pSourceDB),
@TargetDB as varchar(255) = QUOTENAME(@pTargetDB),
@Schema AS VARCHAR(255) = QUOTENAME(@pSchema),
????????@SourceTableName as varchar(255) = @pSourceTableName,
????????@TargetTableName as varchar(255) = @pTargetTableName,
@LinkedServer AS NVARCHAR(255) = QUOTENAME(@pLinkedServer), /*If a server is supplied it will be added if it doesn't already exist*/
@Domain AS NVARCHAR(255) = @pDomain, /*If you want to create your linked server with a FQDN as a datasource, which I recommend*/
@DisableTriggers BIT = @pDisableTriggers,
@RemoveLinkedServer BIT = @pRemoveLinkedServer,
@EXECUTE BIT = @pEXECUTE,
@Debug BIT = @pDebug
????
????DECLARE @Tables TABLE(TableName VARCHAR(MAX))
????DECLARE @Columns TABLE(ColId int, ColumnName VARCHAR(MAX))
????DECLARE @ColumnList AS VARCHAR(MAX)
????DECLARE @UnequalList AS VARCHAR(MAX)
????DECLARE @EqualList AS VARCHAR(MAX)
????DECLARE @InsertList AS VARCHAR(MAX)
????DECLARE @sql AS VARCHAR(MAX)
????DECLARE @IdCol as TABLE(IdCol VARCHAR(100))
DECLARE @FQTNSource AS VARCHAR(500)
SELECT @FQTNSource = CASE WHEN @LinkedServer IS NOT NULL THEN @LinkedServer + '.' ELSE '' END + @SourceDB + '.' + @Schema + '.' + QUOTENAME(@SourceTableName)
DECLARE @FQTNTarget AS VARCHAR(500)
SELECT @FQTNTarget = @TargetDB + '.' + @Schema + '.' + QUOTENAME(@TargetTableName)
????DECLARE @MatchColumns as TABLE(ColumnName varchar(100))
????DECLARE @MatchOnList as varchar(MAX)
????SET nocount ON
/*Create linked server var without brackets to properly add or remove the linked server*/
DECLARE @LinkedServerNoBracket AS VARCHAR(255) = REPLACE(REPLACE(@LinkedServer, ']', ''), '[', '')
DECLARE @DataSource AS VARCHAR(255) = @LinkedServerNoBracket + @Domain
IF NOT EXISTS ( SELECT
[sysadmin]
FROM
SYSLOGINS
WHERE
sysadmin = 1 AND
hasaccess = 1 AND
[name] = SYSTEM_USER )
BEGIN
PRINT 'User must be sysAdmin to add Linked Servers'
END
ELSE
BEGIN
/*Create Linked server*/
IF @LinkedServer IS NOT NULL AND
NOT EXISTS ( SELECT
srv.name
FROM
sys.servers srv
WHERE
srv.server_id != 0 AND
srv.name = @LinkedServerNoBracket )
BEGIN
EXEC master.dbo.sp_addlinkedserver
@server = @LinkedServerNoBracket
, @srvproduct = N''
, @provider=N'SQLNCLI'
, @datasrc= @DataSource
EXEC master.dbo.sp_addlinkedsrvlogin
@rmtsrvname = @LinkedServerNoBracket
, @locallogin = NULL
, @useself = N'True'
PRINT 'Created Linked Server'
END
END
????
????-- init working table
????DELETE from @Columns
????
????-- get list of columns in the table. Exclude the timestamp column
????SET @sql = 'SELECTORDINAL_POSITION , COLUMN_NAME FROM ' + CASE WHEN @LinkedServer IS NOT NULL THEN @LinkedServer + '.' ELSE '' END + @SourceDB + '.' + 'INFORMATION_SCHEMA.COLUMNS c INNER JOIN ' + CASE WHEN @LinkedServer IS NOT NULL THEN @LinkedServer + '.' ELSE '' END + @SourceDB + '.[sys].[columns] AS C1 ON OBJECT_NAME([C1].[object_id]) = ''' + @SourceTableName + ''' AND c1.[name] = [c].[COLUMN_NAME] WHERE TABLE_NAME = ''' + @SourceTableName + ''' AND DATA_TYPE != ''Timestamp'' AND [C1].[is_computed] = 0 ORDER BY ORDINAL_POSITION' ?
IF @Debug = 1
BEGIN
PRINT @sql
END
INSERT @Columns EXECUTE (@SQL)
????-- get the table identity column to link the source to the target
????DELETE @IdCol
SELECT @sql = 'SELECT [name] FROM ' + CASE WHEN @LinkedServer IS NOT NULL THEN @LinkedServer + '.' ELSE '' END + @SourceDB + '.' + 'SYS.IDENTITY_COLUMNS WHERE OBJECT_NAME(OBJECT_ID) = ''' + @TargetTableName + ''''
IF @Debug = 1
BEGIN
PRINT @sql
END
INSERT @IdCol
EXEC(@SQL)
????SET @MatchOnList = NULL
????SELECT @MatchOnList = 'T.' + [IdCol] + ' = S.' + [IdCol] from @IdCol
IF @Debug = 1
BEGIN
PRINT 'Match List: '+ ISNULL(@MatchOnList, 'Empty')
END
????-- if there is an identity column use it, but if not then look for primary keys
????if (@MatchOnList is null)
????????begin
????????????SET @sql = 'SELECT u.column_name FROM ' + CASE WHEN @LinkedServer IS NOT NULL THEN @LinkedServer + '.' ELSE '' END + @SourceDB + '.' + 'information_schema.key_column_usage u inner join ' + CASE WHEN @LinkedServer IS NOT NULL THEN @LinkedServer + '.' ELSE '' END + @SourceDB + '.' + 'INFORMATION_SCHEMA.TABLE_CONSTRAINTS c on c.CONSTRAINT_NAME = u.CONSTRAINT_NAME WHERE c.TABLE_NAME = ''' + @SourceTableName + '''and c.CONSTRAINT_TYPE=''Primary Key'' order by u.ORDINAL_POSITION'
????????????insert @MatchColumns EXECUTE (@SQL)
????????????SELECT @MatchOnList = coalesce(@MatchOnList + ' AND T.[' + ColumnName +'] = S.[' + ColumnName +']' , 'T.[' + ColumnName +'] = S.[' + ColumnName +']') FROM @MatchColumns
IF @Debug = 1
BEGIN
PRINT 'Match List: '+ ISNULL(@MatchOnList, 'Empty')
END
????????????if (@MatchOnList is null)
????????????begin
????????????????SET @MatchOnList='T.<TargetColumnName> = S.<SourceColumnName>'
PRINT 'User Must Supply columns to match upon'
????????????end
????????end
????
????-- coalesce the columns
????SET @ColumnList = null
????SELECT @ColumnList = coalesce(@ColumnList + ', [' + ColumnName +']', '[' + ColumnName + ']') FROM @Columns order by ColId
IF @Debug = 1
BEGIN
PRINT 'Column List: '+ ISNULL(@ColumnList, 'Empty')
END
????-- coalesce the unequal columns (used to locate changes)
????SET @UnequalList = null
????SELECT @UnequalList = coalesce(@UnequalList + ' or T.[' + c.ColumnName +'] != S.[' + c.ColumnName +']', 'T.[' + c.ColumnName +'] != S.[' + c.ColumnName +']') FROM @Columns c left outer join @IdCol i on c.ColumnName=i.IdCol where i.IdCol is null
IF @Debug = 1
BEGIN
PRINT 'UnEqual List: '+ ISNULL(@UnequalList, 'Empty')
END
????-- coalesce the equal columns (used to update the target)
????SET @EqualList = null
????SELECT @EqualList = coalesce(@EqualList + ', T.[' + c.ColumnName +'] = S.[' + c.ColumnName +']', 'T.[' + c.ColumnName +'] = S.[' + c.ColumnName +']') FROM @Columns c left outer join @IdCol i on c.ColumnName=i.IdCol where i.IdCol is null
IF @Debug = 1
BEGIN
PRINT 'Equal List: '+ ISNULL(@EqualList, 'Empty')
END
????-- coalesce the insert columns (used to insert the target)
????SET @InsertList = null
????SELECT @InsertList = coalesce(@InsertList + ', S.[' + ColumnName +']', 'S.[' + ColumnName +']') FROM @Columns
IF @Debug = 1
BEGIN
PRINT 'Insert List: '+ ISNULL(@InsertList, 'Empty')
END
SET NOCOUNT OFF
-- now output the statement
/*Clear out variable to hold merge statement*/
Select @sql = ''
IF @DisableTriggers = 1
BEGIN
Select @sql = @sql + 'DISABLE TRIGGER ALL ON ' + QUOTENAME(@TargetTableName) + CHAR(13) + CHAR(10)
Select @sql = @sql + ''?+ CHAR(13) + CHAR(10)
END
/*Put SQL in variable*/
IF EXISTS(SELECT [IdCol] FROM @IdCol)
BEGIN
Select @sql = @sql + 'SET IDENTITY_INSERT ' + @TargetDB + '.' + @Schema + '.' + QUOTENAME(@TargetTableName) + ' ON'?+ CHAR(13) + CHAR(10)
????Select @sql = @sql + ''?+ CHAR(13) + CHAR(10)
END
????Select @sql = @sql + ' MERGE INTO ' + @TargetDB + '.[dbo].[' + @TargetTableName + '] as T'?+ CHAR(13) + CHAR(10)
????Select @sql = @sql + ' USING ' + @FQTNSource +' as S'?+ CHAR(13) + CHAR(10)
????Select @sql = @sql + ' ON ' + @MatchOnList?+ CHAR(13) + CHAR(10)
????Select @sql = @sql + ' WHEN MATCHED AND ' + @UnequalList?+ CHAR(13) + CHAR(10)
????Select @sql = @sql + ' THEN UPDATE SET ' + @EqualList?+ CHAR(13) + CHAR(10)
????Select @sql = @sql + ' WHEN NOT MATCHED BY TARGET'?+ CHAR(13) + CHAR(10)
????Select @sql = @sql + ' THEN INSERT (' + @ColumnList + ')'?+ CHAR(13) + CHAR(10)
????Select @sql = @sql + ' VALUES (' + @InsertList + ')'?+ CHAR(13) + CHAR(10)
????Select @sql = @sql + ' WHEN NOT MATCHED BY SOURCE'?+ CHAR(13) + CHAR(10)
????Select @sql = @sql + ' THEN DELETE;'?+ CHAR(13) + CHAR(10)
IF EXISTS(SELECT [IdCol] FROM @IdCol)
BEGIN
Select @sql = @sql + ''?+ CHAR(13) + CHAR(10)
????Select @sql = @sql + ' SET IDENTITY_INSERT ' + @TargetDB + '.' + @Schema + '.' + QUOTENAME(@TargetTableName) + ' OFF'
END?
IF @DisableTriggers = 1
BEGIN
Select @sql = @sql + 'ENABLE TRIGGER ALL ON ' + QUOTENAME(@TargetTableName)?+ CHAR(13) + CHAR(10)
Select @sql = @sql + ''?+ CHAR(13) + CHAR(10)
END
SELECT @sql?
IF @EXECUTE = 1
BEGIN
/*Execute Merge*/
EXEC(@SQL)
/*Remove Linked Server*/
IF @RemoveLinkedServer = 1
BEGIN
IF NOT EXISTS ( SELECT
[sysadmin]
FROM
SYSLOGINS
WHERE
sysadmin = 1 AND
hasaccess = 1 AND
[name] = SYSTEM_USER )
BEGIN
PRINT 'User must be sysAdmin to add Linked Servers'
END
ELSE
BEGIN
IF EXISTS ( SELECT
srv.name
FROM
sys.servers srv
WHERE
srv.server_id != 0 AND
srv.name = @LinkedServerNoBracket )
EXEC master.dbo.sp_dropserver
@server = @LinkedServerNoBracket
, @droplogins = 'droplogins'
PRINT 'Linked Server Removed'
END
END
END???
August 16, 2012 at 8:15 am
Very interesting script. Writing the merge statement for very wide source and target columns can become tedious. I never thought of automating the code generation..
Great idea.
Mark
June 13, 2013 at 8:21 am
Hi there; I had some more specific requirements for this script (handling different collations, nullable columns and possibility that source table is temporary to name a few).
This was a great base for what I required so I thought I'd share with you the outcome of my developments!
Thanks to all contributors and I'd appreciate any feedback,
cheers
CREATE PROC [dbo].[up_Utility_GenerateMergeSyntax]
(
@pSourceDB VARCHAR(255) = NULL,
@pTargetDB as varchar(255) = NULL,
@pSchema AS VARCHAR(255) = 'dbo',
@pSourceTableName as varchar(255) = NULL,
@pTargetTableName as varchar(255) = NULL,
@pDisableTriggers BIT = 0,
@pDebug BIT = 0,
@pTemporaryTableSpecifier varchar(30) = NULL--for some transfer procedures, source tables are global temps (##) so need to specify postfix here, else will ref @pSourceDB/@pSchema etc...
)
AS
DECLARE
@SourceDB VARCHAR(255) = QUOTENAME(@pSourceDB),
@TargetDB as varchar(255) = QUOTENAME(@pTargetDB),
@Schema AS VARCHAR(255) = QUOTENAME(@pSchema),
@SourceTableName as varchar(255) = @pSourceTableName,
@TargetTableName as varchar(255) = @pTargetTableName,
@DisableTriggers BIT = @pDisableTriggers,
@Debug BIT = @pDebug
DECLARE @Tables TABLE(TableName VARCHAR(MAX))
DECLARE @Columns TABLE(ColId int, ColumnName VARCHAR(MAX), ColumnDataType varchar(max), ColumnCollation varchar(max), NullReplaceAsString varchar(max))
DECLARE @ColumnList AS VARCHAR(MAX)
DECLARE @UnequalList AS VARCHAR(MAX)
DECLARE @EqualList AS VARCHAR(MAX)
DECLARE @InsertList AS VARCHAR(MAX)
DECLARE @sql AS VARCHAR(MAX)
DECLARE @IdCol as TABLE(IdCol VARCHAR(100))
DECLARE @FQTNSource AS VARCHAR(500)
SELECT @FQTNSource = CASE WHEN @pTemporaryTableSpecifier is not null then @pTemporaryTableSpecifier + @SourceTableName else @SourceDB + '.' + @Schema + '.' + QUOTENAME(@SourceTableName) end
DECLARE @FQTNTarget AS VARCHAR(500)
SELECT @FQTNTarget = @TargetDB + '.' + @Schema + '.' + QUOTENAME(@TargetTableName)
DECLARE @MatchColumns as TABLE(ColumnName varchar(100))
DECLARE @MatchOnList as varchar(MAX)
SET nocount ON
-- init working table
DELETE from @Columns
-- get list of columns in the table. Exclude the timestamp column + account for collation and isnull checks!
SET @sql = 'SELECTORDINAL_POSITION , COLUMN_NAME, DATA_TYPE, c.COLLATION_NAME, CASE WHEN c.IS_NULLABLE = ''YES'' THEN
CASE WHEN CHARACTER_MAXIMUM_LENGTH is not null THEN ''''''''''''
WHEN NUMERIC_PRECISION is not null THEN ''0''
WHEN DATETIME_PRECISION is not null THEN ''''''01 Jan 1900''''''
ELSE ''''''''''''
END
ELSE NULL
END as NullReplaceAsString FROM ' + @SourceDB + '.' + 'INFORMATION_SCHEMA.COLUMNS c INNER JOIN ' + @SourceDB + '.[sys].[columns] AS C1 ON OBJECT_NAME([C1].[object_id]) = ''' + @SourceTableName + ''' AND c1.[name] = [c].[COLUMN_NAME] WHERE TABLE_NAME = ''' + @SourceTableName + ''' AND DATA_TYPE != ''Timestamp'' AND [C1].[is_computed] = 0 ORDER BY ORDINAL_POSITION'
IF @Debug = 1
BEGIN
PRINT @sql
END
INSERT @Columns EXECUTE (@SQL)
-- get the table identity column to link the source to the target
DELETE @IdCol
SELECT @sql = 'SELECT [name] FROM ' + @SourceDB + '.' + 'SYS.IDENTITY_COLUMNS WHERE OBJECT_NAME(OBJECT_ID) = ''' + @TargetTableName + ''''
IF @Debug = 1
BEGIN
PRINT @sql
END
INSERT @IdCol
EXEC(@SQL)
SET @MatchOnList = NULL
SELECT @MatchOnList = 'T.' + [IdCol] + ' = S.' + [IdCol] from @IdCol
IF @Debug = 1
BEGIN
PRINT 'Match List: '+ ISNULL(@MatchOnList, 'Empty')
END
-- if there is an identity column use it, but if not then look for primary keys
if (@MatchOnList is null)
begin
SET @sql = 'SELECT u.column_name FROM ' + @SourceDB + '.' + 'information_schema.key_column_usage u inner join ' + @SourceDB + '.' + 'INFORMATION_SCHEMA.TABLE_CONSTRAINTS c on c.CONSTRAINT_NAME = u.CONSTRAINT_NAME WHERE c.TABLE_NAME = ''' + @SourceTableName + '''and c.CONSTRAINT_TYPE=''Primary Key'' order by u.ORDINAL_POSITION'
insert @MatchColumns EXECUTE (@SQL)
SELECT @MatchOnList = coalesce(@MatchOnList + ' AND T.[' + ColumnName +'] = S.[' + ColumnName +']' , 'T.[' + ColumnName +'] = S.[' + ColumnName +']') FROM @MatchColumns
IF @Debug = 1
BEGIN
PRINT 'Match List: '+ ISNULL(@MatchOnList, 'Empty')
END
if (@MatchOnList is null)
begin
SET @MatchOnList='T.<TargetColumnName> = S.<SourceColumnName>'
PRINT 'User Must Supply columns to match upon'
end
end
-- coalesce the columns
SET @ColumnList = null
SELECT @ColumnList = coalesce(@ColumnList + ', [' + ColumnName +']', '[' + ColumnName + ']') FROM @Columns order by ColId
IF @Debug = 1
BEGIN
PRINT 'Column List: '+ ISNULL(@ColumnList, 'Empty')
END
-- coalesce the unequal columns (used to locate changes)
SET @UnequalList = null
SELECT @UnequalList = coalesce(@UnequalList + ' or '+case when NullReplaceAsString is not null then 'isnull(' else '' end +'T.[' + c.ColumnName +']'+case when NullReplaceAsString is not null then ', '+NullReplaceAsString+')' else '' end +' '+ CASE WHEN c.ColumnCollation IS NOT NULL then 'collate database_default' else '' end+' != '+case when NullReplaceAsString is not null then 'isnull(' else '' end +'S.[' + c.ColumnName +'] '+case when NullReplaceAsString is not null then ', '+NullReplaceAsString+')' else '' end + ' ' + CASE WHEN c.ColumnCollation IS NOT NULL then 'collate database_default' else '' end, ' '+case when NullReplaceAsString is not null then 'isnull(' else '' end +'T.[' + c.ColumnName +'] '+case when NullReplaceAsString is not null then ', '+NullReplaceAsString+')' else '' end +' '+ CASE WHEN c.ColumnCollation IS NOT NULL then 'collate database_default' else '' end+' != '+case when NullReplaceAsString is not null then 'isnull(' else '' end +'S.[' + c.ColumnName +'] '+case when NullReplaceAsString is not null then ', '+NullReplaceAsString+')' else '' end +' ' + CASE WHEN c.ColumnCollation IS NOT NULL then 'collate database_default' else '' end) FROM @Columns c left outer join @IdCol i on c.ColumnName=i.IdCol where i.IdCol is null
IF @Debug = 1
BEGIN
PRINT 'UnEqual List: '+ ISNULL(@UnequalList, 'Empty')
END
-- coalesce the equal columns (used to update the target)
SET @EqualList = null
SELECT @EqualList = coalesce(@EqualList + ', T.[' + c.ColumnName +'] = S.[' + c.ColumnName +']', 'T.[' + c.ColumnName +'] = S.[' + c.ColumnName +']') FROM @Columns c left outer join @IdCol i on c.ColumnName=i.IdCol where i.IdCol is null
IF @Debug = 1
BEGIN
PRINT 'Equal List: '+ ISNULL(@EqualList, 'Empty')
END
-- coalesce the insert columns (used to insert the target)
SET @InsertList = null
SELECT @InsertList = coalesce(@InsertList + ', S.[' + ColumnName +']', 'S.[' + ColumnName +']') FROM @Columns
IF @Debug = 1
BEGIN
PRINT 'Insert List: '+ ISNULL(@InsertList, 'Empty')
END
SET NOCOUNT OFF
-- now output the statement
/*Clear out variable to hold merge statement*/
Select @sql = ''
IF @DisableTriggers = 1
BEGIN
Select @sql = @sql + 'DISABLE TRIGGER ALL ON ' + QUOTENAME(@TargetTableName) + CHAR(13) + CHAR(10)
Select @sql = @sql + '' + CHAR(13) + CHAR(10)
END
/*Put SQL in variable*/
IF EXISTS(SELECT [IdCol] FROM @IdCol)
BEGIN
Select @sql = @sql + 'SET IDENTITY_INSERT ' + @TargetDB + '.' + @Schema + '.' + QUOTENAME(@TargetTableName) + ' ON' + CHAR(13) + CHAR(10)
Select @sql = @sql + '' + CHAR(13) + CHAR(10)
END
Select @sql = @sql + ' MERGE INTO ' + @TargetDB + '.[dbo].[' + @TargetTableName + '] as T' + CHAR(13) + CHAR(10)
Select @sql = @sql + ' USING ' + @FQTNSource +' as S' + CHAR(13) + CHAR(10)
Select @sql = @sql + ' ON ' + @MatchOnList + CHAR(13) + CHAR(10)
Select @sql = @sql + ' WHEN MATCHED AND ' + @UnequalList + CHAR(13) + CHAR(10)
Select @sql = @sql + ' THEN UPDATE SET ' + @EqualList + CHAR(13) + CHAR(10)
Select @sql = @sql + ' WHEN NOT MATCHED BY TARGET' + CHAR(13) + CHAR(10)
Select @sql = @sql + ' THEN INSERT (' + @ColumnList + ')' + CHAR(13) + CHAR(10)
Select @sql = @sql + ' VALUES (' + @InsertList + ')' + CHAR(13) + CHAR(10)
Select @sql = @sql + ' WHEN NOT MATCHED BY SOURCE' + CHAR(13) + CHAR(10)
Select @sql = @sql + ' THEN DELETE;' + CHAR(13) + CHAR(10)
IF EXISTS(SELECT [IdCol] FROM @IdCol)
BEGIN
Select @sql = @sql + '' + CHAR(13) + CHAR(10)
Select @sql = @sql + ' SET IDENTITY_INSERT ' + @TargetDB + '.' + @Schema + '.' + QUOTENAME(@TargetTableName) + ' OFF'
END
IF @DisableTriggers = 1
BEGIN
Select @sql = @sql + 'ENABLE TRIGGER ALL ON ' + QUOTENAME(@TargetTableName) + CHAR(13) + CHAR(10)
Select @sql = @sql + '' + CHAR(13) + CHAR(10)
END
SELECT @sql
Viewing 4 posts - 1 through 3 (of 3 total)
You must be logged in to reply to this topic. Login to reply