带演员表的选择中的动态“列”

Posted

技术标签:

【中文标题】带演员表的选择中的动态“列”【英文标题】:Dynamic "column" in select with cast 【发布时间】:2017-08-09 17:04:26 【问题描述】:

我正在寻找一种方法来根据另一个 SQL 查询的条件在表中转换数据。

我的表格如下;

数据表: 身份证 |实体 ID |价值1 |价值2 |值 3 ... 值 200

实体: 身份证 |名称

字段: 身份证 |实体 ID |数据类型 |字段编号

查找: 身份证 |实体 ID |字段编号

我的理想输出如下:

数据.Id [Id] Field.Id [FieldId] Entity.Id [EntityId] [StringVal] [间隔]

“StringVal”和“IntVal”都将根据“Fields”表中的“DataType”列进行设置,如果设置为“lookup”(在“Data”表中存储为字符串),则应在此选择查询中解析为整数并填充到“IntVal”列中,否则应直接解析为 [StringVal] 列。

我有以下 SQL 查询,它可以按预期工作,但是,如果我有 200 个数据字段,我必须为每个字符串和查找复制 CASE 和 WHEN 子句 200 次,然后就是复杂性添加新类型,例如“DateTime”,这将导致更多问题。

SELECT
  dt.Id as [Id],
  f.Id as [FieldId],
  f.EntityId as [EntityId],
  CASE
    WHEN f.DataType = 'string' and f.FieldNum = 0 THEN dt.Value0
    WHEN f.DataType = 'string' and f.FieldNum = 1 THEN dt.Value1
    WHEN f.DataType = 'string' and f.FieldNum = 2 THEN dt.Value2
    WHEN f.DataType = 'string' and f.FieldNum = 3 THEN dt.Value3
    WHEN f.DataType = 'string' and f.FieldNum = 4 THEN dt.Value4
    WHEN f.DataType = 'string' and f.FieldNum = 5 THEN dt.Value5
    WHEN f.DataType = 'string' and f.FieldNum = 6 THEN dt.Value6
    WHEN f.DataType = 'string' and f.FieldNum = 7 THEN dt.Value7
    WHEN f.DataType = 'string' and f.FieldNum = 8 THEN dt.Value8
    WHEN f.DataType = 'string' and f.FieldNum = 9 THEN dt.Value9
    ELSE NULL
  END as [StringVal],
  CAST(CASE
    WHEN f.DataType = 'lookup' and f.FieldNum = 0 THEN dt.Value0
    WHEN f.DataType = 'lookup' and f.FieldNum = 1 THEN dt.Value1
    WHEN f.DataType = 'lookup' and f.FieldNum = 2 THEN dt.Value2
    WHEN f.DataType = 'lookup' and f.FieldNum = 3 THEN dt.Value3
    WHEN f.DataType = 'lookup' and f.FieldNum = 4 THEN dt.Value4
    WHEN f.DataType = 'lookup' and f.FieldNum = 5 THEN dt.Value5
    WHEN f.DataType = 'lookup' and f.FieldNum = 6 THEN dt.Value6
    WHEN f.DataType = 'lookup' and f.FieldNum = 7 THEN dt.Value7
    WHEN f.DataType = 'lookup' and f.FieldNum = 8 THEN dt.Value8
    WHEN f.DataType = 'lookup' and f.FieldNum = 9 THEN dt.Value9
    ELSE NULL
  END as INT) as [IntVal]
FROM Lookups as i
  left JOIN Fields f on i.FieldId = f.id and i.EntityId = f.EntityId
  INNER join DataTable dt on i.EntityId = dt.EntityId

有没有更简单的方法来实现这一点?我需要将其保存在索引视图中,因此不能使用存储过程、函数等。

编辑:

尝试通过以下 cmets 中所述的函数来实现此目的,但发现了同样的问题。以下是我尝试的函数解决方案。

SELECT
  dt.Id as [Id],
  f.Id as [FieldId],
  f.EntityId as [EntityId],
  CASE
    WHEN f.DataType = 'string' THEN dbo.u_function_return_string
      (
        f.FieldNum, dt.Value0, dt.Value1,
        dt.Value2, dt.Value3, dt.Value4,
        dt.Value5, dt.Value6, dt.Value7,
        dt.Value8, dt.Value9
      )
    ELSE NULL
  END as [StringVal],
  CAST(CASE
    WHEN f.DataType = 'lookup' THEN dbo.u_function_returns_string
      (
         f.FieldNum, dt.Value0, dt.Value1,
         dt.Value2, dt.Value3, dt.Value4,
         dt.Value5, dt.Value6, dt.Value7,
         dt.Value8, dt.Value9
      )
    ELSE NULL
  END as INT) as [IntVal]
FROM dbo.Lookups as i
  INNER JOIN dbo.Fields f on i.FieldId = f.id and i.EntityId = f.EntityId
  INNER join dbo.DataTable dt on i.EntityId = dt.EntityId

功能:

CREATE FUNCTION u_function_return_string(
  @fieldNum INT,
  @val0 NVARCHAR(255),
  @val1 NVARCHAR(255),
  @val2 NVARCHAR(255),
  @val3 NVARCHAR(255),
  @val4 NVARCHAR(255),
  @val5 NVARCHAR(255),
  @val6 NVARCHAR(255),
  @val7 NVARCHAR(255),
  @val8 NVARCHAR(255),
  @val9 NVARCHAR(255)
)
RETURNS NVARCHAR(255) WITH SCHEMABINDING
AS BEGIN
RETURN
  CASE
    WHEN @fieldNum = 0 THEN @val0
    WHEN @fieldNum = 1 THEN @val1
    WHEN @fieldNum = 2 THEN @val2
    WHEN @fieldNum = 3 THEN @val3
    WHEN @fieldNum = 4 THEN @val4
    WHEN @fieldNum = 5 THEN @val5
    WHEN @fieldNum = 6 THEN @val6
    WHEN @fieldNum = 7 THEN @val7
    WHEN @fieldNum = 8 THEN @val8
    WHEN @fieldNum = 9 THEN @val9
  END
END
GO

有没有办法将“行”嵌入到函数中?理想情况下,我想提交 2 个参数,字段编号和“dt”结果集。

编辑 2:

找到了一种使函数采用类型的方法...可行...但无法弄清楚如何将查询中的“dt.*”解析为该类型并传递给函数。

CREATE OR ALTER FUNCTION u_function_return_string(
  @fieldNum INT,
  @data dbo.DataTableType READONLY
)
RETURNS NVARCHAR(255) WITH SCHEMABINDING
AS BEGIN
  DECLARE @tmp NVARCHAR(255)
  DECLARE @value NVARCHAR(255)
  set @tmp = 'Value' + CAST(@fieldNum as NVARCHAR(50))
  SET @value = (SELECT @tmp FROM @data)
  RETURN @value
END
GO

【问题讨论】:

欢迎来到 EAV 的世界。 对于那些一起玩的人:[实体属性值]sqlblog.com/blogs/aaron_bertrand/archive/2009/11/19/… 看起来类似于最终目标的方法,但对我的问题没有帮助:( “我需要将它保存在视图中,因此不能使用存储过程或任何东西。”。甚至没有功能? 您可以在使用模式绑定功能的模式绑定视图上创建索引。 【参考方案1】:

您可以利用标量值模式绑定函数和动态 sql,并在选择查询中用一个 case 函数调用替换多个 case 语句。

我已修改您的查询以解释该方法。请根据您的要求进行更改。

  SELECT
  dt.Id as [Id],
  f.Id as [FieldId],
  f.EntityId as [EntityId],
  CASE
    WHEN f.DataType = 'string' THEN 
         dbo.fn_getvalue(f.FieldNum,f.EntityId)
    ELSE NULL
  END as [StringVal],
  CAST(CASE
    WHEN f.DataType = 'lookup' THEN 
         dbo.fn_getvalue(f.FieldNum,f.EntityId)
    ELSE NULL
  END as INT) as [IntVal]
FROM Lookups as i
  left JOIN Fields f on i.FieldId = f.id and i.EntityId = f.EntityId
  INNER join DataTable dt on i.EntityId = dt.EntityId 




   CREATE FUNCTION [dbo].[fn_getvalue]
    (@param1 int, @param2 int)
    RETURNS nvarchar(255) WITH SCHEMABINDING
    AS
    BEGIN
        declare @sql nvarchar(max); 
        declare @output nvarchar(50);

        set @sql = 'SELECT @field=Value' + @param1  + ' FROM DataTable WHERE EntityId = ' + cast(@param2 as nvarchar);

        execute sp_executesql @sql,N'@field nvarchar(50) OUTPUT', @field = @output OUTPUT;
        return @output;
    END

【讨论】:

不幸的是,这种方法的性能在大约 10k 记录后急剧下降 - 对于案例语句,它在执行查询方面相对一致。

以上是关于带演员表的选择中的动态“列”的主要内容,如果未能解决你的问题,请参考以下文章

基于仪表板提示选择的 BI 答案/视图中的动态列选择

使用 Google 表格中的查询进行动态列选择

根据第三列动态选择两列之一[重复]

指定的演员对日期选择器无效

动态选择 django orm 列

Laravel 中的动态相关下拉选择