使用列名的 UnPivoted 数据的 SP 或函数
SP or Function on UnPivoted data using column names
我很难加入 "yesterday's" 行来提取历史数据。为了提高可读性,我删除了很多代码,只是使用 "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 ...
所有这些列都包含 "mark" 值,我需要抓取昨天的 "marks" 进行比较。我试图避免在函数的 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,所以我不能调用它内联。
除了在函数中列出所有可能的列之外,我还有其他选择吗?如果以后再增加栏目,维护就成问题了..
我正在寻找一种内置的、非 hacky 的方法来完成此任务。如果我 必须 在函数(或游标)中使用硬编码来完成,我会的。我只是希望有人可能知道一种更快的方法来使动态 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
此方法使用 TVF 从几乎任何 table、记录或数据集创建 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
Returns
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))
我很难加入 "yesterday's" 行来提取历史数据。为了提高可读性,我删除了很多代码,只是使用 "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 ...
所有这些列都包含 "mark" 值,我需要抓取昨天的 "marks" 进行比较。我试图避免在函数的 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,所以我不能调用它内联。
除了在函数中列出所有可能的列之外,我还有其他选择吗?如果以后再增加栏目,维护就成问题了..
我正在寻找一种内置的、非 hacky 的方法来完成此任务。如果我 必须 在函数(或游标)中使用硬编码来完成,我会的。我只是希望有人可能知道一种更快的方法来使动态 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
此方法使用 TVF 从几乎任何 table、记录或数据集创建 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
Returns
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))