【问题标题】:How to find the table and column name affected by a stored procedure at runtime?如何在运行时查找受存储过程影响的表名和列名?
【发布时间】:2015-03-04 08:29:46
【问题描述】:

假设我有一个存储过程,其逻辑中有 if else 条件。这表示如果条件为真,则更新 tableA,否则更新 tableB。我想获取受其所有列影响的表名,这些列的值根据运行存储过程时发生的条件执行而更新。

这是为了记录目的,哪个表、列受到哪个存储过程和谁的影响。 我正在使用 SQL Server 2008。如果您有任何建议来实现这一点,请告诉我。

【问题讨论】:

  • 分析查询执行计划中的数据(正如 SSMS 可以做的那样)可能会为您提供此信息。 但是,如果您无法在客户端执行此操作,则最好使用触发器来记录更改(这会更好,避免远距离操作)。
  • 这是一次性的任务,还是永远都会做的事情?
  • @jarlh 每当存储过程修改该特定数据库中任何表中的任何数据时,这将记录详细信息。
  • 我无法添加触发器,因为客户端及其客户端数据库不允许这样做。我想到了一种解决方案,可以在存储过程的临时表中记录插入/更新,然后将其与结果集一起返回。然而,这是一个非常糟糕的主意,需要更改存储过程的逻辑。这可能会变得混乱。所以我想要一种简单的方法,虽然我可以识别存储过程完成的更新。

标签: sql sql-server sql-server-2008 stored-procedures


【解决方案1】:

进行日志记录的方法有很多种,但其中一种方法是使用触发器。请参阅 SQL Server 在线文档中的 DML Triggers。创建一个日志表,然后在 TableA 和 TableB 上创建一个 AFTER TRIGGER 以将适当的信息写入日志表。

【讨论】:

  • 我无法添加触发器,因为客户端及其客户端数据库不允许这样做。我想到了一种解决方案,可以在存储过程的临时表中记录插入/更新,然后将其与结果集一起返回。但是,这是一个非常糟糕的主意,需要更改存储过程的逻辑。这可能会变得混乱。所以我想要一种简单的方法,虽然我可以识别存储过程完成的更新。
【解决方案2】:

行级和列级跟踪

更改跟踪使应用程序能够跟踪用户所做的所有更改,这是一种内置方法

enter link description here

SQL Server 更改跟踪确定列是否更改,因此我决定在 2008 R2 中使用以下脚本进行尝试。基本上在最后一个更新语句中,我将列更新为列中已经存在的相同值。 SQL 似乎只是记录该列涉及更新语句。不幸的是,这意味着任何想要在列级别执行同步操作的人都必须编写更复杂的行更新逻辑。

CREATE TABLE [dbo].[Test]( 
    [Id] [int] IDENTITY(1,1) NOT NULL, 
    [A] [varchar](50) NULL, 
    [B] [int] NULL 
CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED 
( 
    [Id] ASC 
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY] 
) ON [PRIMARY]

GO

ALTER DATABASE TrackingTest 
SET CHANGE_TRACKING = ON 
(CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON)

ALTER TABLE TrackingTest.dbo.Test 
ENABLE CHANGE_TRACKING 
WITH (TRACK_COLUMNS_UPDATED = ON);

GO

INSERT INTO dbo.Test (A,B) VALUES ('C', 50) 
INSERT INTO dbo.Test (A,B) VALUES ('D', 50)

SELECT 'SYS_CHANGE_COLUMS = NULL means that all columns changed.' 
SELECT 
    CT.Id, CT.SYS_CHANGE_OPERATION, 
    CT.SYS_CHANGE_COLUMNS, CT.SYS_CHANGE_CONTEXT, 
    CHANGE_TRACKING_IS_COLUMN_IN_MASK (COLUMNPROPERTY(OBJECT_ID('dbo.Test'), 'A', 'ColumnId'),CT.SYS_CHANGE_COLUMNS) AS A_CHANGED, 
    CHANGE_TRACKING_IS_COLUMN_IN_MASK (COLUMNPROPERTY(OBJECT_ID('dbo.Test'), 'B', 'ColumnId'),CT.SYS_CHANGE_COLUMNS) AS B_CHANGED 
FROM 
    CHANGETABLE(CHANGES dbo.Test, NULL) AS CT

DECLARE @synchronization_version BIGINT 
SET @synchronization_version = CHANGE_TRACKING_CURRENT_VERSION();

SELECT * FROM dbo.Test 
SELECT 'Changing both columns via "UPDATE dbo.Test SET A=''E'', B=51"' 
UPDATE dbo.Test SET A='E', B=51 
SELECT * FROM dbo.Test

SELECT 
    CT.Id, CT.SYS_CHANGE_OPERATION, 
    CT.SYS_CHANGE_COLUMNS, CT.SYS_CHANGE_CONTEXT, 
    CHANGE_TRACKING_IS_COLUMN_IN_MASK (COLUMNPROPERTY(OBJECT_ID('dbo.Test'), 'A', 'ColumnId'),CT.SYS_CHANGE_COLUMNS) AS A_CHANGED, 
    CHANGE_TRACKING_IS_COLUMN_IN_MASK (COLUMNPROPERTY(OBJECT_ID('dbo.Test'), 'B', 'ColumnId'),CT.SYS_CHANGE_COLUMNS) AS B_CHANGED 
FROM 
    CHANGETABLE(CHANGES dbo.Test, @synchronization_version) AS CT

SET @synchronization_version = CHANGE_TRACKING_CURRENT_VERSION();

SELECT * FROM dbo.Test 
SELECT 'Only setting column A to the value already in the column via "UPDATE dbo.Test SET A=''E''"' 
UPDATE dbo.Test SET A='E' 
SELECT * FROM dbo.Test

SELECT 'Now SYS_CHANGE_COLUMS has a value showing that only A was changed' 
SELECT 'To bad it isn''t smart enough to know that the data did not change!'

SELECT 
    CT.Id, CT.SYS_CHANGE_OPERATION, 
    CT.SYS_CHANGE_COLUMNS, CT.SYS_CHANGE_CONTEXT, 
    CHANGE_TRACKING_IS_COLUMN_IN_MASK (COLUMNPROPERTY(OBJECT_ID('dbo.Test'), 'A', 'ColumnId'),CT.SYS_CHANGE_COLUMNS) AS A_CHANGED, 
    CHANGE_TRACKING_IS_COLUMN_IN_MASK (COLUMNPROPERTY(OBJECT_ID('dbo.Test'), 'B', 'ColumnId'),CT.SYS_CHANGE_COLUMNS) AS B_CHANGED 
FROM 
    CHANGETABLE(CHANGES dbo.Test, @synchronization_version) AS CT 

Results are:

【讨论】:

【解决方案3】:

您需要在TableA 和TableB 上创建UPDATE triggers。以下是以下语法:

CREATE TRIGGER TriggerName
    ON [dbo].[TableName]
    FOR UPDATE
    AS
    BEGIN
    SET NOCOUNT ON
    -- here goes your code
    -- you can select updated values by following
    SELECT *
    FROM
      inserted
    INNER JOIN
      deleted
        ON inserted.PrimaryKey = deleted.PrimaryKey
        -- Update is detected when is in both: deleted and inserted
    END

更新

因此您可以从存储过程中选择更新的数据并将其插入到临时表中。

CREATE PROCEDURE sp_example
@var1 INT,
@var2 NVARCHAR(40),
@var3 NVARCHAR(60)
AS
BEGIN
   INSERT INTO ExampleTbl VALUES (@var1, @var2, @var3)
   -- You could create temp table as shown below
   CREATE TABLE #TempTbl
   (
      Id INT,
      Name NVARCHAR(40),
      Email NVARCHAR(60),
   )
   INSERT INTO #TempTbl VALUES (@var1, @var2, @var3)   
   SELECT * FROM #TempTbl
END

【讨论】:

  • 我无法添加触发器,因为客户端及其客户端数据库不允许这样做。我想到了一种解决方案,可以在存储过程的临时表中记录插入/更新,然后将其与结果集一起返回。然而,这是一个非常糟糕的主意,需要更改存储过程的逻辑。这可能会变得混乱。所以我想要一种简单的方法,虽然我可以识别存储过程完成的更新。
  • 我通过在存储过程中简单创建临时表来更新我的答案以保存更新的值。
猜你喜欢
  • 2010-10-15
  • 2017-09-22
  • 1970-01-01
  • 1970-01-01
  • 2022-01-06
  • 2014-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多