如何从通用审计表中检索重构为具有SQL中有意义列名的行集?

时间:2009-05-20 22:11:05

标签: sql

所以我有这个审计表,看起来像这样:

USE [DatabaseName]
GO
/****** Object:  Table [ERAUser].[Audit]    Script Date: 05/20/2009 17:07:11 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [ERAUser].[Audit](
    [AuditID] [int] IDENTITY(1,1) NOT NULL,
    [Type] [char](1) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [TableName] [varchar](128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [PrimaryKeyField] [varchar](1000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [PrimaryKeyValue] [varchar](1000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [FieldName] [varchar](128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [OldValue] [varchar](1000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [NewValue] [varchar](1000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [UpdateDate] [datetime] NULL DEFAULT (getdate()),
    [UserName] [varchar](128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF

问题是,由于我无法控制的原因,我需要将数据作为复制源表的行集返回给用户(作为此系统用户的其他开发人员)。如何将此架构转换为侧面并将FieldName中的值作为行集的列标题?我正在使用SQL 2005.我将知道tablename和UpdateDate。

3 个答案:

答案 0 :(得分:2)

这可以使用PIVOT功能完成。我会试着找出一个例子:

SELECT *
FROM ERAUser.Audit
PIVOT (max(NewValue) FOR FieldName in (Field1, Field2, Field3)) as PivotTable

如果找到多个具有相同FieldName的行,则需要max()告诉Sql Server该怎么做。您可以使用WHERE语句将其限制为正确的行;如果确保它只找到一个,则max(NewValue)等于NewValue。

如果您有很长的列列表,则可以为此生成SQL:

declare @columnlist nvarchar(4000)
select @columnlist = IsNull(@columnlist + ', ', '') + FieldName
from (
    select distinct FieldName from ERAUser.Audit
) sub

declare @query nvarchar(4000)
select @query = 'select *
from ERAUser.Audit
PIVOT (max(newValue) FOR FieldName in (' + @columnlist + ')) as PivotTable'

exec sp_executesql @query

以下是PIVOT的基本示例,以获得一般概念:

create table #normalized (
    colname varchar(12),
    value varchar(12)
)

insert into #normalized values ('value1','A')
insert into #normalized values ('value2','B')
insert into #normalized values ('value3','C')

select *
from #normalized
PIVOT (max(value) FOR ColName in (value1,value2,value3)) as Y

这将导致:

value1    value2    value3
A         B         C

答案 1 :(得分:0)

这将非常困难,尤其是因为表名和列名的一个组合的值的类型将与其他类型不同。要创建对应于表的一行的整个记录​​,您将必须组合多个审核记录。因此,您的行集必须一次定制到一个表。目前尚不清楚您是否记录未更改的列 - 您可能没有 - 因此处理包含整个记录的行集将是最难的。

答案 2 :(得分:0)

如果您知道只需要从一个表中返回行(例如“Foo”)并假设要创建的所有字段都是varchar(50):

1)创建一个具有所需结构的临时表

DECLARE @Sql VARCHAR(8000)
DECLARE @ColName VARCHAR(128)
DECLARE @Comma VARCHAR(2)

SET @CreateSql = "CREATE TABLE #Temp ("
SET @Comma = ''

DECLARE columnCur CURSOR FOR
SELECT DISTINCT FieldName
FROM Audit
WHERE TableName = 'Foo'

OPEN columnCur

FETCH NEXT FROM columnCur INTO @ColName

WHILE @@FETCH_STATUS = 0
BEGIN
  SET @CreateSql = @CreateSql+@Comma+@ColName+' VARCHAR(128) NULL'
  SET @Comma = ', '
  FETCH NEXT FROM columnCur INTO @ColName
END

CLOSE columnCur
DEALLOCATE columnCur

SET @CreateSql = @CreateSql + ")"

EXECUTE sp_executesql @CreateSql

2)填写表主键:

--Assuming a single PK column, otherwise you need another cursor

DECLARE @pk VARCHAR(1000)

SELECT @pk=PrimaryKeyField
FROM Audit
WHERE TableName = 'Foo'

DECLARE @PkSql VARCHAR(2000)
SET @PkSql = 'INSERT INTO #Temp ('+@pk+') VALUES SELECT DISTINCT PrimaryKeyValue FROM Audit WHERE TableName = ''Foo'''

EXECUTE sp_executesql @PkSql

3)创建一个游标,创建动态sql,用正确的值更新临时表中的行(与上面类似的风格)

4)从#Temp

中选择

5)DROP #Temp

注意:这应该可行,但由于我现在没有可用的SQL Server,所以它完全未经测试。