使用列名称在UnPivoted数据上使用SP或函数

时间:2017-03-02 15:58:45

标签: sql sql-server dynamic-sql unpivot

我很难得到"昨天"行加入以提取历史数据。为了便于阅读,我删除了很多代码,并且只是使用" SELECT *"代替 - 它是一个很大的。

以下是UNPIVOT的剥离版本:

SELECT *
FROM EODMarkHistory
    UNPIVOT
    (
    Value
    FOR Name IN (C0,C1,C2,C3,C4,C5,C6,C7,C8,C9,C10,C11,C12
                ,P0,PLow,PHigh,P1,P2,P3,P4,P5,P6,P7,P8,P9,P10,P11,P12
                ,F0,F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,F11,F12,F13,F14,F15,F16,F17,F18,F19,F20,F21,F22,F23,F24
                ,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8)
    ) piv
    INNER JOIN ...

所有这些列都包含"标记"我需要抓住昨天的标记"比较。我试图避免在函数中的IF或CASE语句中列出每一个。我编写了一个与动态SQL一起使用的存储过程:

SET @MarkDate = DATEADD(DAY,-1,@MarkDate)
DECLARE @DynamicSQL nvarchar(800) = N'SELECT ' + @Column + ' FROM EODMarkHistory WHERE SubGroupCode=''' + @ProductCode + ''' AND MarkDate = ''' + CONVERT(varchar(10),@MarkDate,121) + ''''

exec sp_executesql @DynamicSQL

但是当然动态SQL不能进入函数,这是一个SP,所以我无法调用此内联。

除了列出函数中所有可能的列之外,我还有其他选项吗?如果我们将来添加列,维护成为一个问题..

我正在寻找一种内置的,非黑客的方式来实现这一目标。如果我必须在函数(或光标)中进行硬编码,我会。我只是希望有人知道在这种情况下获得动态SQL(或其他具有相同结果的选项)的更快方法吗?优选在线。如果不能实现内联动态,我可以在没有帮助的情况下对其进行硬编码。

编辑 - 小工作示例

仅限示例代码 - 这远远不够正确......

DECLARE @Test TABLE(MarkDate varchar(10),C1 int, C2 int, C3 int, C4 int)
DECLARE @MarkDate datetime = '3/2/2017'
INSERT INTO @Test
VALUES
('3/2/2017',50,-50,100,-100)
,('3/1/2017',75,-75,125,-125)

SELECT * FROM @Test

--Current result
SELECT *
FROM @Test
    UNPIVOT
    (
        VALUE
        FOR Name IN (C1,C2,C3,C4)
    ) piv

--Wanted Result
SELECT '3/2/2017' AS MarkDate, 50 AS Value, 'C1' AS Name, 75 AS YestValue
UNION
SELECT '3/2/2017' AS MarkDate, -50 AS Value, 'C2' AS Name, -75 AS YestValue
UNION
SELECT '3/2/2017' AS MarkDate, 100 AS Value, 'C3' AS Name, 125 AS YestValue
UNION
SELECT '3/2/2017' AS MarkDate, -100 AS Value, 'C4' AS Name, -125 AS YestValue

1 个答案:

答案 0 :(得分:2)

此方法使用TVF从几乎任何表,记录或数据集创建EAV结构(实体属性值)。也适用于CROSS APPLY。请参阅UDF底部的注释。

现在,如果您不想使用UDF,可以在CROSS APPLY中轻松使用此方法

示例

-- Create Some Sample Data
Declare @Test Table (MarkDate varchar(10),C1 int, C2 int, C3 int, C4 int)
Insert Into @Test Values ('3/2/2017',50,-50,100,-100),('3/1/2017',75,-75,125,-125)

-- Set Key Dates
Declare @Date1 date = '2017-03-01'
Declare @Date2 date = '2017-03-02'

-- Execute Code
Select MarkDate  = @Date2
      ,Value     = max(case when Entity=@Date2 then Value end)
      ,Name      = Attribute
      ,YestValue = max(case when Entity=@Date1 then Value end)
 From [dbo].[udf-EAV]((Select * from @Test Where MarkDate in (@Date1,@Date2) for XML RAW))
 Group By Attribute

<强>返回

MarkDate    Value   Name        YestValue
2017-03-02  50      C1          75
2017-03-02  -50     C2          -75
2017-03-02  100     C3          125
2017-03-02  -100    C4          -125

感兴趣的UDF

CREATE FUNCTION [dbo].[udf-EAV](@XML xml)
Returns Table 
As
Return (
    with cteKey(k) as (Select Top 1 xAtt.value('local-name(.)','varchar(100)') From @XML.nodes('/row') As A(xRow) Cross Apply A.xRow.nodes('./@*') As B(xAtt))    

    Select Entity    = xRow.value('@*[1]','varchar(50)')
          ,Attribute = xAtt.value('local-name(.)','varchar(100)')
          ,Value     = xAtt.value('.','varchar(max)') 
    From  @XML.nodes('/row') As A(xRow)
    Cross Apply A.xRow.nodes('./@*') As B(xAtt)
    Where xAtt.value('local-name(.)','varchar(100)') Not In (Select k From cteKey)
)
-- Notes:  First Field in Query will be the Entity
-- Select * From [dbo].[udf-EAV]((Select UTCDate=GetUTCDate(),* From sys.dm_os_sys_info for XML RAW))