Generate Data Insert Scripts Using Hex Strings to Preserve Data

Applicability:

SQL Server 2000: Not Supported**

SQL Server 2005: Tested

SQL Server 2008: Tested

SQL Server 2008R2: Tested

SQL Server 2012: Not Tested

Credits:

Author: Unknown

Date: 28 Nov 2011

Description

There are several well publicised methods of generating data insert scripts for SQL Server tables and with good reason - until SQL 2008, this feature was not available through any of the Microsoft SQL Server management toolset. SQL 2008 does provide this function, as does SQL 2008 R2 (although Microsoft decided to move its location giving the initial impression that it had disappeared)

However, these methods all script text using the standard unicode text string i.e. N'This is text'.

There will always be some user (or 3rd party software vendor) who will ruin your day by including control characters or other non-text characters in the text data. Fun, fun, fun.

This script adopts a new approach by converting all characters in a text column into one Hex string.

These will be processed by a standard insert statement - you just have to look as some of the SQL Server Replication stored procedures to find examples of this (one of the common SQL Injection attacks is even delivered using strings in this format in attempt to mask the malicious intent of the string)

The code is not particularly performant for larger data volumes, but then this kind of task is normally required when setting up or transferring lookup table data between servers/databases. For larger data volumes, I would recommend using BCP in native format mode.

**NOTE: This code could be adapted for SQL 2000 by replacing the VARCHAR(MAX) variables with VARCHAR(8000), but the verbose nature of a hex string (just over 4 * the size) will limit the size of the text strings that can be processed

Code

DROP PROCEDURE dbo.usp_generate_inserts

GO

CREATE PROCEDURE dbo.usp_generate_inserts

(

@Table NVARCHAR(255)

)

AS

BEGIN

SET NOCOUNT ON

DECLARE

@Is_Identity BIT;

@Columns NVARCHAR(MAX);

@Values NVARCHAR(MAX);

@Script NVARCHAR(MAX);

IF ISNULL(CHARINDEX('.', @Table), 0) = 0

BEGIN

PRINT 'Procedure dbo.usp_generate_inserts expects a table_name parameter in the form of schema_name.table_name';

END

ELSE

BEGIN

-- initialize variables as otherwise the padding will fail (return nulls for nvarchar(max) types)

SET @Is_Identity = 0;

SET @Columns = '';

SET @Values = '';

SET @Script = '';

/*

The following select makes an assumption that the identity column should be included in

the insert statements. Such inserts still work when coupled with identity_insert toggle,

which is typically used when there is a need to "plug the holes" in the identity values.

Please note the special handling of the text data type. The type should never be present

in SQL Server 2005 tables because it will not be supported in future versions, but there

are unfortunately plenty of tables with text columns out there, patiently waiting for

someone to upgrade them to varchar(max).

*/

SELECT

@Is_Identity = @Is_Identity | COLUMNPROPERTY(OBJECT_ID(@Table), column_name, 'IsIdentity'),

@Columns = @Columns + ', ' + '['+ column_name + ']',

@Values =

@Values + ' + '', '' + isnull(master.dbo.fn_varbintohexstr(cast(' +

CASE data_type

WHEN 'text' THEN 'cast([' + column_name + '] as varchar(max))'

WHEN 'ntext' THEN 'cast([' + column_name + '] as nvarchar(max))'

ELSE '[' + column_name + ']'

END + ' as varbinary(max))), ''null'')'

FROM

information_schema.columns

WHERE table_name = SUBSTRING(@Table, CHARINDEX('.', @Table) + 1, LEN(@Table))

AND data_type != 'timestamp'

ORDER BY ordinal_position;

SET @Script =

'select ''insert into ' + @Table + ' (' + SUBSTRING(@Columns, 3, LEN(@Columns)) +

') values ('' + ' + SUBSTRING(@Values, 11, LEN(@Values)) + ' + '');'' from ' + @Table + ';';

IF @Is_Identity = 1

PRINT ('set identity_insert ' + @Table + ' on');

/*

generate insert statements. If the results to text option is set and the query results are

completely fit then the prints are a part of the batch, but if the results to grid is set

then the prints (identity insert related) can be gathered from the messages window.

*/

EXEC sp_executesql @Script;

IF @Is_Identity = 1

PRINT ('set identity_insert ' + @Table + ' off');

END

SET NOCOUNT OFF

END

GO