如何使用动态表和字段名称连接数据

时间:2014-02-20 23:03:02

标签: sql sql-server dynamic-sql

所以我有一些表包含一个外键,它根据上下文引用不同的表和字段。我有一个包含相关表和字段名称的密钥源表。然后我需要能够在这些表/字段中查找值。

我将其简化为以下表格/数据,以展示我所拥有的内容:

CREATE TABLE [dbo].[WorkOrderStatus](
    [WorkOrderStatusId] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](60) NOT NULL
) ON [PRIMARY]

CREATE TABLE [dbo].[Note](
    [NoteId] [int] IDENTITY(1,1) NOT NULL,
    [NoteKeySourceId] [int] NULL,
    [KeyValue] [int] NULL,
    [Note] [nvarchar](max) NOT NULL,
) ON [PRIMARY]

CREATE TABLE [dbo].[NoteKeySource](
    [NoteKeySourceId] [int] IDENTITY(1,1) NOT NULL,
    [SourceTable] [nvarchar](60) NOT NULL,
    [SourceField] [nvarchar](60) NOT NULL,
    [SourceTextField] [nvarchar](60) NOT NULL,
) ON [PRIMARY]

GO

INSERT INTO WorkOrderStatus VALUES('Open')
DECLARE @OpenStatusId int = SCOPE_IDENTITY();

INSERT INTO WorkOrderStatus VALUES('Closed')
DECLARE @ClosedStatusId int = SCOPE_IDENTITY();

INSERT INTO NoteKeySource VALUES('WorkOrderStatus', 'WorkOrderStatusId', 'Name')
DECLARE @WorkOrderSource int = SCOPE_IDENTITY();

INSERT INTO Note VALUES(@WorkOrderSource, @OpenStatusId, 'Opened the work order')
INSERT INTO Note VALUES(@WorkOrderSource, @ClosedStatusId, 'Closed the work order')

所以我需要能够查询Notes列表,包括KeyKalue标识的NoteKeySource中引用的SourceTextField。

这是我现在拥有的SP,它有效:

CREATE PROCEDURE [dbo].[spGetNotes]
AS
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

CREATE TABLE #NoteTable (
    NoteId int NOT NULL,
    SourceTable nvarchar(60),
    SourceField nvarchar(60),
    SourceTextField nvarchar(60),
    KeyValue int,
    Context nvarchar(MAX),
    Note nvarchar(MAX),
)

INSERT INTO #NoteTable (NoteId, SourceTable, SourceField, SourceTextField, KeyValue, Note) 
    SELECT N.NoteId, NKS.SourceTable, NKS.SourceField, NKS.SourceTextField, N.KeyValue, N.Note
        FROM Note AS N
            LEFT OUTER JOIN NoteKeySource AS NKS
                ON NKS.NoteKeySourceId = N.NoteKeySourceId

DECLARE @Context nvarchar(max)

DECLARE @Sql nvarchar(255);
DECLARE @SqlDecl nvarchar(255) = N'@Key int, @Ctx nvarchar(MAX) OUTPUT';


DECLARE @NoteId int, @KeyValue int, @SourceTable nvarchar(60), @SourceField nvarchar(60), @SourceTextField nvarchar(60)

DECLARE db_cursor CURSOR FOR SELECT SourceTable, SourceField, SourceTextField, KeyValue
        FROM #NoteTable WHERE SourceTable IS NOT NULL FOR UPDATE OF Context


OPEN db_cursor  
FETCH NEXT FROM db_cursor INTO @SourceTable, @SourceField, @SourceTextField, @KeyValue

WHILE @@FETCH_STATUS = 0  
BEGIN
    SET @Sql =  N'SELECT @Ctx = ' + @SourceTextField + ' FROM ' + @SourceTable + ' WHERE ' + @SourceField + '= @Key';

    EXECUTE sp_executesql @Sql, @SqlDecl, @Key = @KeyValue, @Ctx = @Context OUTPUT

    UPDATE #NoteTable SET Context=@Context WHERE CURRENT OF db_cursor

    FETCH NEXT FROM db_cursor INTO @SourceTable, @SourceField, @SourceTextField, @KeyValue
END

CLOSE db_cursor  
DEALLOCATE db_cursor 


SELECT NoteId, Context, Note from #NoteTable

DROP TABLE #NoteTable

RETURN 0

调用spGetNotes返回:

1:打开:打开工单

2:关闭:关闭工单

它有效,但它非常难看,我想知道是否有更好/更清洁/更合适的方法来实现这一点而不使用游标或while循环。我的理解是动态SQL不能在普通SQL的子查询中使用,所以这是我能想到的最好的。

在SQL方面我比较绿,所以如果我错了,请赐教!此外,如果对一般领域问题有更好的整体方法,那也将受到欢迎和赞赏:)

1 个答案:

答案 0 :(得分:1)

创建一个视图,在源表中提供单个EAV覆盖。然后将NoteKeySource加入其中。

CREATE VIEW AllSources AS (
  SELECT 
   'SourceTable1' AS [SourceTable], [SourceField], [SourceTextField]
  FROM SourceTable1
  UNPIVOT([SourceTextField] FOR [SourceField] IN
      ('Table1Field1','Table1Field2','Table1Field3','Table1Field4')
  ) p
  UNION ALL
  SELECT 
   'SourceTable2' AS [SourceTable], [SourceField], [SourceTextField]
  FROM SourceTable2
  UNPIVOT([SourceTextField] FOR [SourceField] IN
    ('Table2Field1','Table2Field2')
  ) p
  UNION ALL
  SELECT 
   'SourceTable3' AS [SourceTable], [SourceField], [SourceTextField]
  FROM SourceTable3
  UNPIVOT([SourceTextField] FOR [SourceField] IN
    ('Table3Field1','Table3Field2','Table3Field3')
  ) p
  --etc
)