【问题标题】:Dynamic casting within dynamic SQL动态 SQL 中的动态转换
【发布时间】:2017-09-07 03:11:33
【问题描述】:

我正在尝试创建一个存储过程,该过程将返回具有可变数量的列(在 tblHeaderDefinition 中定义)的变量类型(也在 tblHeaderDefinition 中)的结果集。这些列的值作为 VARCHAR 存储在单独的表中,该表与定义表进行 INNER JOINED。

一些不同的应用程序(在 C#、VBA 等中)引入了这些数据并以不同的方式使用它,但是当我引入这个表时,我的列都是 VARCHAR 类型,与 value 列相同。我想在 SQL 中将它们转换为适当的类型,并让应用程序查看列的 type 属性以确定如何处理该列,而不是将定义表引入每个单独的应用程序并将每个应用程序转换为正确的类型。

下面的查询返回我正在寻找的平面表,但我想不出在这种情况下使用动态转换的方法。

DECLARE @collist NVARCHAR(MAX)
SET @collist = stuff((SELECT DISTINCT ',' + QUOTENAME(ColumnName) 
            FROM tblHeaderDefinition
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

DECLARE @q NVARCHAR(MAX)
SET @q = '
    SELECT * 
    FROM (
        SELECT ColumnName, Value
            FROM (
                SELECT  tblHeaderDefinition.pkHeaderDefinitionID
                    , tblHeaderDefinition.ColumnName
                    , tblHeaderDefinition.ColumnType
                    , tblHeaderValue.Value
                FROM    tblHeaderValue 
                INNER JOIN  tblHeaderDefinition ON tblHeaderValue.fkHeaderDefinitionID = tblHeaderDefinition.pkHeaderDefinitionID
        ) AS x
    ) AS source
    pivot (
        max(Value)
        FOR ColumnName IN (' + @collist + ')
    ) AS pvt
'

EXEC (@q)

测试

我正在使用的表:

CREATE TABLE [dbo].[tblHeaderDefinition](
    [pkHeaderDefinitionID] [int] IDENTITY(1,1) NOT NULL,
    [ColumnName] [varchar](256) NULL,
    [ColumnType] [varchar](256) NULL
) ON [PRIMARY]

GO

CREATE TABLE [dbo].[tblHeaderValue](
    [pkHeaderValueID] [int] IDENTITY(1,1) NOT NULL,
    [fkHeaderDefinitionID] [int] NULL,
    [fkHeaderID] [int] NULL,
    [Value] [varchar](256) NULL
) ON [PRIMARY]

GO

SET IDENTITY_INSERT [dbo].[tblHeaderDefinition] ON 
GO
INSERT [dbo].[tblHeaderDefinition] ([pkHeaderDefinitionID], [ColumnName], [ColumnType]) VALUES (1, N'ColIntTest', N'INT')
GO
INSERT [dbo].[tblHeaderDefinition] ([pkHeaderDefinitionID], [ColumnName], [ColumnType]) VALUES (2, N'ColVarCharTest', N'VARCHAR(50)')
GO
INSERT [dbo].[tblHeaderDefinition] ([pkHeaderDefinitionID], [ColumnName], [ColumnType]) VALUES (3, N'ColRealTest', N'REAL')
GO
INSERT [dbo].[tblHeaderDefinition] ([pkHeaderDefinitionID], [ColumnName], [ColumnType]) VALUES (4, N'ColBitTest', N'BIT')
GO
SET IDENTITY_INSERT [dbo].[tblHeaderDefinition] OFF
GO

SET IDENTITY_INSERT [dbo].[tblHeaderValue] ON 
GO
INSERT [dbo].[tblHeaderValue] ([pkHeaderValueID], [fkHeaderDefinitionID], [fkHeaderID], [Value]) VALUES (1, 1, 5, N'54')
GO
INSERT [dbo].[tblHeaderValue] ([pkHeaderValueID], [fkHeaderDefinitionID], [fkHeaderID], [Value]) VALUES (2, 2, 5, N'NA-0490')
GO
INSERT [dbo].[tblHeaderValue] ([pkHeaderValueID], [fkHeaderDefinitionID], [fkHeaderID], [Value]) VALUES (3, 3, 5, N'1000.094')
GO
INSERT [dbo].[tblHeaderValue] ([pkHeaderValueID], [fkHeaderDefinitionID], [fkHeaderID], [Value]) VALUES (4, 4, 5, N'1')
GO
SET IDENTITY_INSERT [dbo].[tblHeaderValue] OFF
GO

【问题讨论】:

  • 使用客户端代码更容易做到这一点,甚至都不好笑。
  • 我同意这会更容易,但在我的情况下,该代码需要编写 4 次,在 4 种不同的应用程序中,用 4 种不同的语言,我只能看到这是一场噩梦。
  • 除非你作弊,让客户端代码成为数据库中的 CLR 存储过程。一段 C# 代码。 (当然,托管 CLR 程序集有其自身的挑战。)
  • 这种方法只剩下一个问题,即 WonderWare Archestra 的快速脚本语言,可以准确地描述为 POS,它无法访问这些类型的程序集。但还是无法摆脱那个。
  • CLR 存储过程的功能就像 T-SQL 存储过程一样,就 RPC 协议和EXEC 语句而言,所以我看不出这会有什么问题。实际创建过程可能需要一些手动 SQL。如果您真的有一种无法使用存储过程的客户端语言,我会很好奇这怎么可能。

标签: sql-server casting dynamic-sql


【解决方案1】:

您可以使用另一个变量来保存列的转换:

DECLARE @collist NVARCHAR(MAX), @collist2 NVARCHAR(MAX);

SET @collist = stuff((SELECT DISTINCT ',' + QUOTENAME(ColumnName) 
            FROM tblHeaderDefinition
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

SET @collist2 = stuff((SELECT DISTINCT ',CAST(' + QUOTENAME(ColumnName) + ' AS ' + 
                                        ColumnType + ') AS ' + QUOTENAME(ColumnName)
            FROM tblHeaderDefinition
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

DECLARE @q NVARCHAR(MAX)
SET @q = '
    SELECT ' + @collist2 + ' 
    FROM (
        SELECT ColumnName, Value
            FROM (
                SELECT  tblHeaderDefinition.pkHeaderDefinitionID
                    , tblHeaderDefinition.ColumnName
                    , tblHeaderDefinition.ColumnType
                    , tblHeaderValue.Value
                FROM    tblHeaderValue 
                INNER JOIN  tblHeaderDefinition ON tblHeaderValue.fkHeaderDefinitionID = tblHeaderDefinition.pkHeaderDefinitionID
        ) AS x
    ) AS source
    pivot (
        max(Value)
        FOR ColumnName IN (' + @collist + ')
    ) AS pvt
'

EXEC (@q)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多