8

This is probably a bit of a limited, but valuable scenario. I have a SQL Server 2008 database with a table that has millions of records. There appears to be an intermittent problem with several of the records. I'm trying to repro the problem. In an effort to do this, I finally got the ID of an offending record. I would like to generate an INSERT statement associated with this single record in my PROD database. Then I can easily migrate it into my TESTING database in an effort to repro and resolve the problem.

Basically, I need to generate a single INSERT statement for a single record from a single table where I know the primary key value of the record.

Does anyone have any ideas of how I can accomplish this? Essentially, I want to generate insert statements on a conditional basis.

Thank you!

user70192
  • 13,786
  • 51
  • 160
  • 240
  • 1
    table schema? conditions? there are pretty smart people over here but none of them are prophets :P – aF. May 17 '12 at 13:00

3 Answers3

5

First try to recreate what you want to insert with a SELECT statement.

After that you can insert into the table with a INSERT INTO like this:

INSERT INTO tablename
SELECT ....

If they are on different servers, you can use INSERT like this:

INSERT INTO tablename VALUES (...)

using the values given by the SELECT in the other server fill the values in the insert.

aF.
  • 64,980
  • 43
  • 135
  • 198
  • If these are on different servers this will require a linked server. I like this idea better but I think the OP is trying to generate a static string with values to copy, paste and execute elsewhere. – Aaron Bertrand May 17 '12 at 13:19
  • Excellent Sir! very valuable answer. –  Oct 10 '17 at 12:47
5

In your specific case I think you can do this:

CREATE PROCEDURE dbo.GenerateSingleInsert
    @table     NVARCHAR(511), -- expects schema.table notation
    @pk_column SYSNAME,       -- column that is primary key
    @pk_value  INT            -- change data type accordingly
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @cols   NVARCHAR(MAX), @vals   NVARCHAR(MAX),
            @valOut NVARCHAR(MAX), @valSQL NVARCHAR(MAX);

    SELECT @cols = N'', @vals = N'';

    SELECT @cols = @cols + ',' + QUOTENAME(name),
           @vals = @vals + ' + ' + REPLICATE(CHAR(39),3) + ',' 
            + REPLICATE(CHAR(39),3) + ' + ' + REPLICATE(CHAR(39),2) + '+'
            + 'RTRIM(' + CASE WHEN system_type_id IN (40,41,42,43,58,61) THEN
             'CONVERT(CHAR(8), '  + QUOTENAME(name) + ', 112) + '' '' 
            + CONVERT(CHAR(14), ' + QUOTENAME(name) + ', 14)'
            ELSE 'REPLACE(' + QUOTENAME(name) + ','''''''','''''''''''')' END + ') 
            + ' + REPLICATE(CHAR(39),2)
      FROM sys.columns WHERE [object_id] = OBJECT_ID(@table)
      AND system_type_id <> 189 -- can't insert rowversion
      AND is_computed = 0;      -- can't insert computed columns

    SELECT @cols = STUFF(@cols, 1, 1, ''),
           @vals = REPLICATE(CHAR(39), 4) + ' + ' + STUFF(@vals, 1, 13, '') 
            + REPLICATE(CHAR(39), 2);

    SELECT @valSQL = N'SELECT @valOut = ' + @vals + ' FROM ' + @table + ' WHERE '
        + QUOTENAME(@pk_column) + ' = ''' + RTRIM(@pk_value) + ''';';

    EXEC sp_executesql @valSQL, N'@valOut NVARCHAR(MAX) OUTPUT', @valOut OUTPUT;

    SELECT SQL = 'INSERT ' + @table + '(' + @cols + ') SELECT ' + @valOut;
END
GO

So let's try it out:

CREATE TABLE dbo.splunge
(
    ID INT, dt DATETIME, rv ROWVERSION, t NVARCHAR(MAX)
);

INSERT dbo.splunge(ID, dt, t)
          SELECT 1, GETDATE(), 'foo'
    UNION ALL SELECT 2, GETDATE(), 'bar'
    UNION ALL SELECT 3, GETDATE(), 'O''Brien';

EXEC dbo.GenerateSingleInsert N'dbo.splunge', N'ID', 1;

SQL
-------------
INSERT dbo.splunge([ID],[dt],[t]) SELECT '1','20120517 10:07:07:330','foo'

EXEC dbo.GenerateSingleInsert N'dbo.splunge', N'ID', 2;

SQL
-------------
INSERT dbo.splunge([ID],[dt],[t]) SELECT '2','20120517 10:07:07:330','bar'

EXEC dbo.GenerateSingleInsert N'dbo.splunge', N'ID', 3;

SQL
-------------
INSERT dbo.splunge([ID],[dt],[t]) SELECT '3','20120517 10:07:07:330','O''Brien'

If there is an IDENTITY column you may need to set SET IDENTITY_INSERT ON for the TEST table, and verify that there is no collision. Probably about 500 caveats I should mention, I haven't tested all data types, etc.

However in the more general case there is a lot more to it than this. Vyas K has a pretty robust stored procedure that should demonstrate how complicated it can get:

http://vyaskn.tripod.com/code/generate_inserts_2005.txt

You are probably far better off using a tool like Red-Gate's SQL Data Compare to pick a specific row and generate an insert for you. As I've blogged about, paying for a tool is not just about the money, it's about the hours of troubleshooting and bug-fixing that someone else has already done for you.

Aaron Bertrand
  • 272,866
  • 37
  • 466
  • 490
  • I needed to make some changes to the code you wrote above (thanks btw). It was to big for a comment and I wasn't sure the etiquette on editing the code in your post so I added it below. – RobRolls Oct 16 '13 at 23:00
2

Aaron, I liked your code, it solved a problem for me. I ran into a few issues using it (like you said I would) with nulls and the text type so I made some changes to address those issues.

ALTER PROCEDURE dbo.GenerateSingleInsert
@table     NVARCHAR(511), -- expects schema.table notation
@pk_column SYSNAME,       -- column that is primary key
@pk_value  INT            -- change data type accordingly
AS
BEGIN
SET NOCOUNT ON;

DECLARE @cols   NVARCHAR(MAX), @vals   NVARCHAR(MAX),
        @valOut NVARCHAR(MAX), @valSQL NVARCHAR(MAX);

SELECT @cols = N'', @vals = N'';

SELECT @cols = @cols + ',' + QUOTENAME(name),
       @vals = @vals + ' + '','' + ' + 'ISNULL('+REPLICATE(CHAR(39),4)+'+RTRIM(' + 
                                        CASE WHEN system_type_id IN (40,41,42,43,58,61) -- datetime types
                                        THEN
                                            'CONVERT(CHAR(8), '  + QUOTENAME(name) + ', 112) + '' ''+ CONVERT(CHAR(14), ' + QUOTENAME(name) + ', 14)'
                                        WHEN system_type_id IN (35) -- text type NOTE: can overflow
                                        THEN
                                            'REPLACE(CAST(' + QUOTENAME(name) + 'as nvarchar(MAX)),'+REPLICATE(CHAR(39),4)+','+REPLICATE(CHAR(39),6)+')' 
                                        ELSE 
                                            'REPLACE(' + QUOTENAME(name) + ','+REPLICATE(CHAR(39),4)+','+REPLICATE(CHAR(39),6)+')' 
                                        END 
                                + ')+' + REPLICATE(CHAR(39),4) + ',''null'') + '

  FROM sys.columns WHERE [object_id] = OBJECT_ID(@table)
  AND system_type_id <> 189 -- can't insert rowversion
  AND is_computed = 0;      -- can't insert computed columns

SELECT @cols = STUFF(@cols, 1, 1, ''),
       @vals = REPLICATE(CHAR(39),2) + STUFF(@vals, 1, 6, '') + REPLICATE(CHAR(39),2) ;

SELECT @valSQL = N'SELECT @valOut = ' + @vals + ' FROM ' + @table + ' WHERE '
    + QUOTENAME(@pk_column) + ' = ''' + RTRIM(@pk_value) + ''';';

EXEC sp_executesql @valSQL, N'@valOut NVARCHAR(MAX) OUTPUT', @valOut OUTPUT;

SELECT SQL = 'INSERT ' + @table + '(' + @cols + ') SELECT ' + @valOut;
END
RobRolls
  • 498
  • 5
  • 16