0

I need a script that generate insert statements but with check for if the data doesn't already exist, this because it should be periodically run on parallell systems where different dtata will be added to the systems but we want them tables to be in sync. I have the basic ides and borrowed parts of code but get a syntax error i have trouble solving.

I'm basing my code on the code Param Yadav showed at Converting Select results into Insert script - SQL Server but I need to check for data already in the table. (I need to add more "bells & whistles later, but take this step-by-step)

My own main addition is the @NOT_EXISTS part which should be in the WHERE clause of the NOT EXISTS check. If I replace that with a plain WHERE 0=1 I get no syntax error so it indicates the error is in my @NOT_EXISTS string.

Edit: Yesterday I thought I had an answer to my own question but when running on "real data" I saw that some lines are too long for QUOTENAME, I have to fix those quotation marks "manually" (concats in script) instead...


SET NOCOUNT ON

DECLARE @CSV_COLUMN VARCHAR(MAX),
    @QUOTED_DATA VARCHAR(MAX),
    @NOT_EXISTS VARCHAR(MAX),
    @SQL_KOD VARCHAR(MAX),
    @TABLE_NAME VARCHAR(MAX),
    @FILTER_CONDITION VARCHAR(MAX)='',
    @FIRST_COL INT,
    @LAST_COL INT

/* INPUT DATA */
SELECT @TABLE_NAME = 'WorkflowError'
SELECT @FIRST_COL = 2
SELECT @LAST_COL = 4
/* */

SELECT @CSV_COLUMN=STUFF
(
    (
     SELECT ',['+ NAME +']' FROM sys.all_columns 
     WHERE OBJECT_ID=OBJECT_ID(@TABLE_NAME) AND 
     is_identity!=1 FOR XML PATH('')
    ),1,1,''
)

--SELECT @CSV_COLUMN

SELECT @QUOTED_DATA=STUFF
(
    (
     SELECT ' ISNULL(QUOTENAME('+NAME+','+QUOTENAME('''','''''')+'),'+'''NULL'''+')+'','''+'+' FROM sys.all_columns 
     WHERE OBJECT_ID=OBJECT_ID(@TABLE_NAME) AND 
     is_identity!=1 FOR XML PATH('')
    ),1,1,''
)

SELECT @QUOTED_DATA=SUBSTRING(@QUOTED_DATA,1,LEN(@QUOTED_DATA)-5)

SELECT @QUOTED_DATA

SELECT @NOT_EXISTS=STUFF
(
    (
     SELECT ' ['+ COLUMN_NAME +']=', 'ISNULL(QUOTENAME('+COLUMN_NAME+','+QUOTENAME('''','''''')+'),'+'''NULL'''+') AND '
     FROM information_schema.columns 
     WHERE table_name = @TABLE_NAME AND 
     ordinal_position BETWEEN @FIRST_COL AND @LAST_COL
     FOR XML PATH('')
    ),1,1,''
)

SELECT @NOT_EXISTS=SUBSTRING(@NOT_EXISTS,1,LEN(@NOT_EXISTS)-4)

SELECT @NOT_EXISTS
--SELECT @NOT_EXISTS=' 0=1 '

SELECT @SQL_KOD='SELECT ''
    IF NOT EXISTS(SELECT 1 
    FROM ' + @TABLE_NAME + ' WHERE ' + @NOT_EXISTS + ')
    BEGIN
        INSERT INTO '+@TABLE_NAME+'('+@CSV_COLUMN+')
        VALUES('''+'+'+@QUOTED_DATA+'+'+''')
    END
    GO '''+' Insert_Scripts 
FROM '+@TABLE_NAME + @FILTER_CONDITION

SELECT @SQL_KOD
EXECUTE (@SQL_KOD)

GO

[stackoverflow won't let me post code unless it's formatted, but then the strings below won't be as they are created in the script...]
When I do SELECT @NOT_EXISTS=' 0=1 ' I get an INSERT line for each row in my table:

IF NOT EXISTS(SELECT 1 FROM WorkflowError WHERE  0=1 )
    BEGIN
        INSERT INTO WorkflowError([TargetSystem],[ErrorCode],[ErrorText],[RetryMaxCount],[RetryStrategyName],[ErrorDescription])
        VALUES('EttLiv','800','Value cannot be null.  Parameter name: source','0',NULL,'Value cannot be null.  Parameter name: source')
    END
GO 

With my @NOT_EXISTS code the @SQL_KOD string becomes this:

SELECT 'IF NOT EXISTS(SELECT 1 FROM WorkflowError
                      WHERE [TargetSystem]=ISNULL(QUOTENAME(TargetSystem,''''),'NULL'))
BEGIN
    INSERT INTO WorkflowError([TargetSystem],[ErrorCode],[ErrorText],[RetryMaxCount],[RetryStrategyName],[ErrorDescription])
    VALUES('+ISNULL(QUOTENAME(TargetSystem,''''),'NULL')+','
    + ISNULL(QUOTENAME(ErrorCode,''''),'NULL')+','
    + ISNULL(QUOTENAME(ErrorText,''''),'NULL')+','
    + ISNULL(QUOTENAME(RetryMaxCount,''''),'NULL')+','
    + ISNULL(QUOTENAME(RetryStrategyName,''''),'NULL')+','
    + ISNULL(QUOTENAME(ErrorDescription,''''),'NULL')+')
END
GO ' Insert_Scripts FROM WorkflowError
However, trying to execute that @SQL_KOD line just gives:

Msg 156, Level 15, State 1, Line 3
Incorrect syntax near the keyword 'NULL'.

...and I can't find out where I have done wrong, if it's in my thinking or if it's just a misplaced quotation mark...
  • You have both table structure same? – mkRabbani May 27 '19 at 06:37
  • I'm only working with one table in this T-SQL script... The main problem for debugging is that it's a script creating a new script (in the string variable @SQL_KOD) which then is executed - hard to keep those quotation marks happy. – Tommy Petersson May 27 '19 at 07:24
  • BTW, when test running my script I had LAST_COL set to 2 to simplify the WHERE clause, I have only one column included in my example SQL_KOD - the [TargetSystem]=ISNULL(QUOTENAME(TargetSystem,''''),'NULL') part of SQL_KOD. – Tommy Petersson May 27 '19 at 07:43
  • The QUOTED_DATA string is what gets into the VALUES list of data and there ISNULL(QUOTENAME(TargetSystem,''''),'NULL') resolves to 'EttLiv' which is the content of the column TargetSystem. The NOT_EXISTS goes into the WHERE clause, but here the very similar [TargetSystem]=ISNULL(QUOTENAME(TargetSystem,''''),'NULL') gives a T-SQL error. Seems like the resolving works different in the WHERE clause compared to in VALUES... – Tommy Petersson May 27 '19 at 08:00
  • If i for test purpose replace WHATEVER for NULL the error message I get changes somewhat, since NULL is T-SQL while WHATEVER isn't. I still have problems understand what's wrong, though... ```t-sql SELECT @NOT_EXISTS=STUFF ( ( SELECT ' ['+ COLUMN_NAME +']=', 'ISNULL(QUOTENAME('+COLUMN_NAME+','+QUOTENAME('''','''''')+'),'+'''WHATEVER'''+') AND ' FROM information_schema.columns WHERE table_name = @TABLE_NAME AND ordinal_position BETWEEN @FIRST_COL AND @LAST_COL FOR XML PATH('') ),1,1,'' ) – Tommy Petersson May 27 '19 at 08:50
  • Error message: Msg 102, Level 15, State 1, Line 3 Incorrect syntax near ')) BEGIN INSERT INTO WorkflowError([TargetSystem],[ErrorCode],[ErrorText],[RetryMaxCount],[RetryStrategyName],[ErrorDescription])'. – Tommy Petersson May 27 '19 at 08:53
  • Hopefully https://stackoverflow.com/users/6165840/param-yadav will see this and educate me... I have written some more comlicated T-SQL scripts than this but at this moment I feel uneducated and can't see what I'm doing wrong. – Tommy Petersson May 27 '19 at 09:36
  • I have of course copied the resulting code (SQL_KOD) to SSMS and seen that it complains on NULL and also seen that if I put two single quotes around NULL instead of one I don't get a syntax error (but not the wanted result either). I'm experimenting with editing that code snippet but may be closer but still no cigar. Indicates that I need a single quotation before the ISNULL in the WHERE clause for the WHERE clause to resolve since it's otherwise just a string, but then it complains on QUOTENAME. – Tommy Petersson May 27 '19 at 10:43

2 Answers2

0

Where do you expect @SQL_KOD to get its values from? Because if you are retrieving your values for TargetSystem / ErrorCode / ... / ErrorDescription from somewhere outside of your insert statement, I would expect a "from" statement. If you want to input variables, you are missing both the definition of the variables and the @-sign in front of the variable name.

As far as keeping quotes happy: try writing your code with QUOTED_IDENTIFIER OFF - you can create the entire @SQL_KOD variable by writing between double quotes ("), and single quotes would behave like normal quotation marks.

A very basic re-write of your code could be something as follows:

SET QUOTED_IDENTIFIER OFF 

DECLARE @SQL_KOD VARCHAR(MAX)

SET @SQL_KOD = 

"DECLARE @WorkFlowError TABLE ([TargetSystem] NVARCHAR(200),[ErrorCode] NVARCHAR(200))

IF NOT EXISTS ( SELECT 1 FROM @WorkFlowError )
BEGIN
    INSERT INTO @WorkFlowError ([TargetSystem],[ErrorCode])
    SELECT ISNULL(QUOTENAME([TargetSystem],''''),'NULL')
        , ISNULL(QUOTENAME([ErrorCode],''''),'NULL')
    FROM (
        SELECT [TargetSystem]='Foo'
            , [ErrorCode]='Bar'
    ) src
END";
  • As seen at the end of SQL_KOD: FROM '+@TABLE_NAME which is the table "workflowerror". All the values for the INSERT statement part correctly gets taken from there, maybe it's different with the WHERE clause. I will look into QUOTED_IDENTIFIER OFF - maybe it's then easier to see where I'm going wrong. – Tommy Petersson May 27 '19 at 08:31
  • ...and my variables are of course declared at the beginning. The otiginal I'm building on was a script that created a stored procedure with parameters, I'm waiting with the sp until things work and have my small /* INPUT DATA */ part instead. – Tommy Petersson May 27 '19 at 08:38
  • What i would like as end result would be NOT EXISTS/INSERT INTO lines like this: ```sql IF NOT EXISTS(SELECT 1 FROM WorkflowError WHERE [TargetSystem]='EttLiv' ) BEGIN INSERT INTO WorkflowError([TargetSystem],[ErrorCode],[ErrorText],[RetryMaxCount],[RetryStrategyName],[ErrorDescription]) VALUES('EttLiv','800','Value cannot be null. Parameter name: source','0',NULL,'Value cannot be null. Parameter name: source') END GO – Tommy Petersson May 27 '19 at 10:03
0

I originally used QUOTENAME as in the Param Yadav script I borrowed from but that function can't handle long strings. It doesn't complain, just returns NULL if the string is too long. Now the script is less readable (long lines of quotation marks) but now works.


SET NOCOUNT ON

DECLARE @CSV_COLUMN VARCHAR(MAX),
        @QUOTED_DATA VARCHAR(MAX),
        @NOT_EXISTS VARCHAR(MAX),
        @SQL_KOD VARCHAR(MAX),
        @TABLE_NAME VARCHAR(MAX),
        @FILTER_CONDITION VARCHAR(MAX),
        @FIRST_COL INT,
        @LAST_COL INT


/* INPUT DATA */
SELECT @TABLE_NAME = 'WorkflowError'
SELECT @FIRST_COL = 2
SELECT @LAST_COL = 4
SELECT @FILTER_CONDITION = ''
/* */

SELECT @CSV_COLUMN=STUFF
(
    (
     SELECT ',['+ NAME +']' FROM sys.all_columns 
     WHERE OBJECT_ID=OBJECT_ID(@TABLE_NAME) AND 
     is_identity!=1 FOR XML PATH('')
    ),1,1,''
)

SELECT @QUOTED_DATA=STUFF
(
    (
     SELECT ' ISNULL('''''''' + REPLACE('+NAME+','''''''','''''''''''') + '''''''','''+'NULL'''+''+')+'',''+'
     FROM sys.all_columns 
     WHERE OBJECT_ID=OBJECT_ID(@TABLE_NAME) AND 
     is_identity!=1 FOR XML PATH('')
    ),1,1,''
)

SELECT @QUOTED_DATA=SUBSTRING(@QUOTED_DATA,1,LEN(@QUOTED_DATA)-5)

SELECT @NOT_EXISTS=STUFF
(
    (
     SELECT ' ['+ COLUMN_NAME +']='' + ', 'ISNULL('''''''' + REPLACE('+COLUMN_NAME+','''''''','''''''''''') + '''''''','''+'NULL'''+''+')+'' AND '
     FROM information_schema.columns 
     WHERE table_name = @TABLE_NAME AND 
     ordinal_position BETWEEN @FIRST_COL AND @LAST_COL
     FOR XML PATH('')
    ),1,1,''
)

SELECT @NOT_EXISTS=SUBSTRING(@NOT_EXISTS,1,LEN(@NOT_EXISTS)-6)

SELECT @SQL_KOD='SELECT ''IF NOT EXISTS(SELECT 1 FROM ' + @TABLE_NAME + ' WHERE ' + @NOT_EXISTS + ' + ' + ''') BEGIN INSERT INTO '+@TABLE_NAME+'('+@CSV_COLUMN+')VALUES('''+'+'+@QUOTED_DATA+'+'+''') END '''+' Insert_Scripts FROM ' + @TABLE_NAME + ' ' + @FILTER_CONDITION

EXECUTE (@SQL_KOD)

SET NOCOUNT OFF

  • I tried to show a line or two that the QUOTED_DATA fails on but they are too long to include in a comment... – Tommy Petersson May 28 '19 at 07:46
  • Added one line that the script doesn't handle OK after the script above, though StackOverflow re-formatted it. – Tommy Petersson May 28 '19 at 07:55
  • I've tried creating some different test data but the only thing in common on failing lines is that they are long, characters like \@([] doesn't bother the script. But all variables are VARCHAR(MAX) and QUOTENAME is only used on column names, like: ISNULL(QUOTENAME(TargetSystem,''''),'NULL')+','+ ISNULL(QUOTENAME(ErrorCode,''''),'NULL')+','+ ISNULL(QUOTENAME(ErrorText,''''),'NULL')+','+ ISNULL(QUOTENAME(RetryMaxCount,''''),'NULL')+','+ ISNULL(QUOTENAME(RetryStrategyName,''''),'NULL')+','+ ISNULL(QUOTENAME(ErrorDescription,''''),'NULL') – Tommy Petersson May 28 '19 at 08:37
  • BTW, the longest text in the Errortext column is 3890 characters long. – Tommy Petersson May 28 '19 at 08:50
  • So, QUOTENAME can't be used, what can I use instead to put quotation marks around the data? It ought to be possible to do without the use of any function, I guess, just even more difficult to keep track of all the quotation marks... – Tommy Petersson May 28 '19 at 09:11
  • To replace QUOTENAME and what it does I'm sure I'll need to use REPLACE :-) I need to get to something like SELECT ISNULL('''' + REPLACE(TargetSystem,'''','''''')+ '''','NULL')... – Tommy Petersson May 28 '19 at 11:16
  • SELECT ISNULL('''' + REPLACE(TargetSystem,'''',''''''),'NULL')+','+ ISNULL('''' + REPLACE(ErrorCode,'''',''''''),'NULL')+','+ ISNULL('''' + REPLACE(ErrorText,'''',''''''),'NULL')+','+ ISNULL('''' + REPLACE(RetryMaxCount,'''',''''''),'NULL')+','+ ISNULL('''' + REPLACE(RetryStrategyName,'''',''''''),'NULL')+','+ ISNULL('''' + REPLACE(ErrorDescription,'''',''''''),'NULL') FROM WorkflowError works. Now I need to create the code that creates this code... – Tommy Petersson May 28 '19 at 11:26
  • I haven't had any need for using the @FILTER_CONDITION so that's not tested with my script but apart from that it now works fine. – Tommy Petersson May 29 '19 at 09:53