TSQL: non-boolean type specified error using updat

2019-07-22 09:06发布

问题:

I'm trying to use the update function in T-sql in a trigger to check if a column has been changed or not. This is what I'm doing:

declare column_name cursor for select name from sys.columns where object_id = object_id(N'tblKit')
open column_name

fetch column_name into @colname
while (@@Fetch_status=0)
begin
if(Update(@colname))

I get an error saying non-boolean type specified where a condition is expected. This is the syntax used in the msdn forum. So is there anything wrong with this?

I'm using Microsoft SQL server 2008 with Management Studio

回答1:

If you simply want to log changes of individual columns in your trigger, you could try unpivoting, possibly in conjunction with full join. The idea is, you unpivot both inserted and deleted then join them on the table's key and the column containing the unpivoted names, filtering out the rows where the values are same.

Here's an example illustration of the method.

First, the table definitions:

CREATE TABLE TestTable (
  ID int IDENTITY PRIMARY KEY,
  Attr1 int,
  Attr2 int,
  Attr3 int
);

CREATE TABLE TestTableLog (
  ID int IDENTITY PRIMARY KEY,
  TableID int,
  AttrName sysname,
  OldValue int,
  NewValue int,
  Timestamp datetime DEFAULT GETDATE()
);

Next, a trigger for logging changes. This one will catch all the operations: insert, update, and delete:

CREATE TRIGGER trTestTable ON TestTable
AFTER INSERT, UPDATE, DELETE
AS BEGIN
  WITH inserted_unpivot AS (
    SELECT
      ID,
      AttrName,
      Value
    FROM inserted i
    UNPIVOT (Value FOR AttrName IN (Attr1, Attr2, Attr3)) u
  ),
  deleted_unpivot AS (
    SELECT
      ID,
      AttrName,
      Value
    FROM deleted d
    UNPIVOT (Value FOR AttrName IN (Attr1, Attr2, Attr3)) u
  )
  INSERT INTO TestTableLog (TableID, AttrName, OldValue, NewValue)
  SELECT
    ISNULL(i.ID, d.ID),
    ISNULL(i.AttrName, d.AttrName),
    d.Value,
    i.Value
  FROM inserted_unpivot i
    FULL JOIN deleted_unpivot d
      ON i.ID = d.ID AND i.AttrName = d.AttrName
  WHERE CASE i.Value WHEN d.Value THEN 0 ELSE 1 END = 1
END

Now let's fill the TestTable with some data:

WHILE (SELECT COUNT(*) FROM TestTable) < 15
  INSERT INTO TestTable
  SELECT RAND() * 1000, RAND() * 1000, RAND() * 1000
;

Here's its contents before the subsequent changes:

ID          Attr1       Attr2       Attr3
----------- ----------- ----------- -----------
1           820         338         831
2           795         881         453
3           228         430         719
4           36          236         105
5           246         115         649
6           488         657         438
7           990         360         15
8           668         978         724
9           872         385         562
10          460         396         462
11          62          599         630
12          145         815         439
13          595         7           54
14          587         85          655
15          80          606         407

And now let's perform some modifications to the contents:

UPDATE TestTable SET Attr2 = 35 WHERE ID = 3;
UPDATE TestTable SET Attr3 = 0 WHERE ID BETWEEN 6 AND 10;
INSERT INTO TestTable VALUES (1, 1, 1);
DELETE FROM TestTable WHERE ID = 14;

Here's what we've got in TestTable afterwards:

ID          Attr1       Attr2       Attr3
----------- ----------- ----------- -----------
1           820         338         831
2           795         881         453
3           228         35          719
4           36          236         105
5           246         115         649
6           488         657         0
7           990         360         0
8           668         978         0
9           872         385         0
10          460         396         0
11          62          599         630
12          145         815         439
13          595         7           54
15          80          606         407
16          1           1           1

And this is what has been logged:

ID          TableID     AttrName   OldValue    NewValue    Timestamp
----------- ----------- ----------- ----------- ----------- -----------------------
1           3           Attr2       430         35          2011-08-22 20:12:19.217
2           10          Attr3       462         0           2011-08-22 20:12:19.227
3           9           Attr3       562         0           2011-08-22 20:12:19.227
4           8           Attr3       724         0           2011-08-22 20:12:19.227
5           7           Attr3       15          0           2011-08-22 20:12:19.227
6           6           Attr3       438         0           2011-08-22 20:12:19.227
7           16          Attr1       NULL        1           2011-08-22 20:12:19.227
8           16          Attr3       NULL        1           2011-08-22 20:12:19.227
9           16          Attr2       NULL        1           2011-08-22 20:12:19.227
10          14          Attr1       587         NULL        2011-08-22 20:12:19.230
11          14          Attr2       85          NULL        2011-08-22 20:12:19.230
12          14          Attr3       655         NULL        2011-08-22 20:12:19.230

The setup, of course, has been somewhat simplified. In particular, all the main table's columns that are to be logged are of the same type, and so there's no need to convert data to some generic type to embrace various kinds of data. But maybe that's just what you need. And if not, I believe this can provide a good start to implementing the ultimate solution.