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
This website stores cookies on your computer.
These cookies are used to improve your website experience and provide more personalized services to you, both on this website and through other media.
To find out more about the cookies we use, see our Privacy Policy