Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

Replace comma with semicolon procedure problems with table update trigger

Posted on 2014-04-20
8
Medium Priority
?
693 Views
Last Modified: 2014-04-22
I am creating a procedure (replaceCommaTest.sql) that will change instances of  ',' (comma) with ';'  (semicolon).
I have a trigger on the table (see createAuditTrigger.sql).

When I run the procedure I get this message:
Msg 9420, Level 16, State 1, Procedure EnvironmentChangeAudit, Line 46
XML parsing: line 1, character 5340932, illegal xml character

If I delete the trigger and run the procedure, every works fine.

Also, if I do a single insert statement with the trigger in place, the trigger works fine.

Any idea what is going on and how to fix the problem
replaceCommaTest.sql
createAuditTrigger.sql
0
Comment
Question by:dblankman
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 2
  • 2
  • +1
8 Comments
 
LVL 75

Expert Comment

by:Anthony Perkins
ID: 40011407
INSERTED will be empty when you do a DELETE, so you may have to qualify that.  Also,
you may need to add a root node to that Xml document in order to make it well-formed.

But in order to troubleshoot it, I would add print statements to get the following value before you attempt to assign it to @ActionXml:
SELECT  COALESCE((SELECT    *
                  FROM      deleted
                 FOR
                  XML AUTO
                 ), N'<deleted/>') + 
		COALESCE((SELECT   *
                FROM     inserted
                FOR
                XML AUTO
                ), N'<inserted/>');

Open in new window


Also, the following code is pointless as it will always have at least one:
  -- Get number of affected rows
  DECLARE @Count int = @@ROWCOUNT;


-- Make sure at least one row was actually affected
  IF (@Count > 0)

Open in new window

0
 
LVL 35

Expert Comment

by:ste5an
ID: 40011476
Take a look at Tracking Data Changes.
0
 
LVL 70

Assisted Solution

by:Scott Pletcher
Scott Pletcher earned 1000 total points
ID: 40012586
Most likely the data has one or more of the characters "&", "<" and/or ">".

The only really effective way around that might be to gen the full column list and REPLACE those characters where appropriate.  

You could also dynamically generate the list at run time, but that will slow down the trigger which you don't want to do of course.  Triggers should be as efficient as possible because of their critical path.

Along those lines, we can touch up other aspects of the trigger, like so:


CREATE TRIGGER dbo.EnvironmentChangeAudit
ON dbo.environment
AFTER  UPDATE, DELETE
NOT FOR REPLICATION
AS
SET NOCOUNT ON;
DECLARE @deleted_found bit
DECLARE @inserted_found bit

IF EXISTS(SELECT 1 FROM deleted)
    SET @deleted_found = 1;
ELSE
    SET @deleted_found = 0;

IF EXISTS(SELECT 1 FROM inserted)
    SET @inserted_found = 1;
ELSE
    SET @inserted_found = 0;
   
-- Make sure at least one row was actually affected
IF @deleted_found = 1
OR @inserted_found = 1
BEGIN

    DECLARE @ActionType char(6)
    DECLARE @ActionXml xml;

    IF @deleted_found > 0
       IF @inserted_found > 0
           SET @ActionType = 'update'
       ELSE
           SET @ActionType = 'delete'
    ELSE
        SET @ActionType = 'insert'

    -- Use FOR XML AUTO to retrieve before and after snapshots of the changed
    -- data in XML format
    SELECT @ActionXml = COALESCE
    (
      (
        SELECT *
        FROM deleted
        FOR XML AUTO
      ), N'<deleted/>'
    ) + COALESCE
    (
      (
        SELECT *
        FROM inserted

                              
FOR XML AUTO
      ), N'<inserted/>'
    );

    -- Insert a row for the logged action in the audit logging table
    INSERT INTO dbo.DmlActionLog
    (
      SchemaName,
      TableName,
      ActionType,
      ActionXml,
      UserName,
      Spid,
      ActionDateTime
    )
    SELECT
      OBJECT_SCHEMA_NAME(@@PROCID, DB_ID()),
      OBJECT_NAME(t.parent_id, DB_ID()),
      @ActionType,
      @ActionXml,
      USER_NAME(),
      @@SPID,
      GETDATE()
    FROM sys.triggers t
    WHERE t.object_id = @@PROCID;

END;
GO
0
Visualize your virtual and backup environments

Create well-organized and polished visualizations of your virtual and backup environments when planning VMware vSphere, Microsoft Hyper-V or Veeam deployments. It helps you to gain better visibility and valuable business insights.

 

Author Comment

by:dblankman
ID: 40014401
I have discovered something about the trigger. Instead of creating a new record in the log for each up, it creates a single record for all of the records changed. For example if 4 records are changed in one update query, then there is only one record created instead of 4. Is there a way to modify the trigger so that it fires for each record changed rather than all of the records at once.
0
 
LVL 35

Expert Comment

by:ste5an
ID: 40014420
Create a XML for each row instead of the entire table. Use a sub-query in the INSERT INTO with an SELECT on the virtual tables.

But this will imho be a conceptual break with your DmlActionLog table, cause you're splitting one action into 4 actions.
0
 

Author Comment

by:dblankman
ID: 40014425
I am not sure what you mean? How would the trigger be modified to create XML for each row. I want the DmlActionLog table to have a row for each separate row that is being changed. If 20 records are changed, I would want 20 rows created in the DmlActionLog table.
0
 
LVL 35

Accepted Solution

by:
ste5an earned 1000 total points
ID: 40014481
Caveat: You have here a semantic problem. One DML action is one UPDATE. One UPDATE can affect more than one rows.

E.g. based on the previous solutions, something like

INSERT  INTO dbo.DmlActionLog
        ( SchemaName ,
          TableName ,
          ActionType ,
          ActionXml ,
          UserName ,
          Spid ,
          ActionDateTime
        )
        SELECT  OBJECT_SCHEMA_NAME(@@PROCID, DB_ID()) ,
		OBJECT_NAME(t.parent_id, DB_ID()) ,
		@ActionType ,
		COALESCE((	SELECT   DI.*
					FROM     DELETED DI
					WHERE    DI.primaryKeyColumns = D.primaryKeyColumns
					FOR XML AUTO
		), N'<deleted/>') + 
		COALESCE((	SELECT  II.*
					FROM    INSERTED II
					WHERE   II.primaryKeyColumns = D.primaryKeyColumns
					FOR XML AUTO
					), N'<inserted/>') ,
		USER_NAME() ,
		@@SPID ,
		GETDATE()
        FROM    sys.triggers t
                CROSS JOIN DELETED D
                LEFT JOIN INSERTED I ON D.primaryKeyColumns = I.primaryKeyColumns
        WHERE   t.object_id = @@PROCID;

Open in new window

0
 
LVL 75

Expert Comment

by:Anthony Perkins
ID: 40014724
Is there a way to modify the trigger so that it fires for each record changed rather than all of the records at once.
No.  For performance reasons a TRIGGER in SQL Server fires once per statement and not once per row.
0

Featured Post

NFR key for Veeam Agent for Linux

Veeam is happy to provide a free NFR license for one year.  It allows for the non‑production use and valid for five workstations and two servers. Veeam Agent for Linux is a simple backup tool for your Linux installations, both on‑premises and in the public cloud.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

After restoring a Microsoft SQL Server database (.bak) from backup or attaching .mdf file, you may run into "Error '15023' User or role already exists in the current database" when you use the "User Mapping" SQL Management Studio functionality to al…
SQL Server engine let you use a Windows account or a SQL Server account to connect to a SQL Server instance. This can be configured immediatly during the SQL Server installation or after in the Server Authentication section in the Server properties …
Do you want to know how to make a graph with Microsoft Access? First, create a query with the data for the chart. Then make a blank form and add a chart control. This video also shows how to change what data is displayed on the graph as well as form…
In this video, Percona Director of Solution Engineering Jon Tobin discusses the function and features of Percona Server for MongoDB. How Percona can help Percona can help you determine if Percona Server for MongoDB is the right solution for …

715 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question