繁体   English   中英

透视表值函数

[英]Pivoting a table-valued function

我有一个返回两列的TVF:'measure'(名称)和'score',该度量的数字分数:

dbo.ScoringFunction(param1, param2, ..., paramN)
Measure   Score
-------   -----
measure1  10
measure2  5
...       ...
measureN  15

我正在针对包含其参数的大量行运行此函数:

Name   Param1   Param2   ...   ParamN
----   ------   ------         ------
Daniel 12       5              6
etc.

我正在尝试找到一种方法来显示确定这些分数的参数旁边的度量和分数:

Name   Param1   Param2   ...   ParamN   measure1   measure2   ...   measureN
----   ------   ------         ------   --------   --------         --------
Daniel 12       5              6        10         5                15
etc.

到目前为止,我已经尝试使用数据透视表,但它很棘手,因为被转动的数据包含在TVF而不是静态表中。 我也尝试过使用CROSS APPLY,但是一旦我掌握了数据(测量和分数),我仍然无法将其转换为格式良好的行。

如果有人有任何想法,他们将不胜感激!

如果你创建一个如下所示的函数:

CREATE FUNCTION [dbo].[fGetSpecificMeasures]
(
    @HeightScore INT
    , @WeightScore INT
    , @TvScore INT
)
RETURNS TABLE
RETURN
(
    SELECT
        Final.Height AS tv_height_score
        , Final.[Weight] AS tv_weight_score
        , Final.TV AS tv_score
    FROM
    (
        SELECT measure, score FROM ScoringRubric WHERE measure = 'Height' AND @HeightScore BETWEEN bottom_of_range AND top_of_range
            UNION ALL
        SELECT measure, score FROM ScoringRubric WHERE measure = 'Weight' AND @WeightScore BETWEEN bottom_of_range AND top_of_range
            UNION ALL
        SELECT measure, score FROM ScoringRubric WHERE measure = 'TV' AND @TvScore BETWEEN bottom_of_range AND top_of_range
    ) Base
    PIVOT
    (
        MAX(score) 
        FOR measure
        IN
        (
            [Height]
            , [Weight]
            , [TV]
        )
    ) Final
);
GO

一个看起来像这样:

CREATE FUNCTION [dbo].[fGetMeasureScore]
(
    @Measure VARCHAR(50)
    , @Value INT
)
RETURNS TABLE
RETURN
(
    SELECT
        score
    FROM ScoringRubric
    WHERE measure = @Measure
        AND @Value BETWEEN bottom_of_range AND top_of_range
);
GO

然后,您可以使用以下任一方式获取数据:

DECLARE @User VARCHAR(50) = 'Daniel'

SELECT
    UserProfile.*
    , HeightScore.score AS tv_height_score
    , WeightScore.score AS tv_weight_score
    , TvScore.score AS tv_score
FROM UserProfile
INNER JOIN ScoringRubric HeightScore
    ON HeightScore.measure = 'Height'
    AND UserProfile.height BETWEEN HeightScore.bottom_of_range AND HeightScore.top_of_range
INNER JOIN ScoringRubric WeightScore
    ON WeightScore.measure = 'Weight'
    AND UserProfile.[weight] BETWEEN WeightScore.bottom_of_range AND WeightScore.top_of_range
INNER JOIN ScoringRubric TvScore
    ON TvScore.measure = 'TV'
    AND UserProfile.TV BETWEEN TvScore.bottom_of_range AND TvScore.top_of_range
WHERE UserProfile.name = @User

SELECT
    *
FROM UserProfile
CROSS APPLY dbo.fGetSpecificMeasures(height, [weight], TV)
WHERE name = @User

SELECT
    UP.*
    , HeightScore.score AS tv_height_score
    , WeightScore.score AS tv_weight_score
    , TvScore.score AS tv_score
FROM UserProfile UP
CROSS APPLY fGetMeasureScore('Height', UP.height) HeightScore
CROSS APPLY fGetMeasureScore('Weight', UP.[weight]) WeightScore
CROSS APPLY fGetMeasureScore('TV', UP.TV) TvScore
WHERE UP.name = @User

我真的不知道你认为哪一种最适合你的用途。 如果您有疑问,请告诉我。

至于你原来的问题,如果这是功能:

CREATE FUNCTION [dbo].[fGetMeasureScoresOriginal]
(
    @HeightScore INT
    , @WeightScore INT
    , @TvScore INT
)
RETURNS TABLE
RETURN
(
    SELECT measure, score FROM ScoringRubric WHERE measure = 'Height' AND @HeightScore BETWEEN bottom_of_range AND top_of_range
        UNION ALL
    SELECT measure, score FROM ScoringRubric WHERE measure = 'Weight' AND @WeightScore BETWEEN bottom_of_range AND top_of_range
        UNION ALL
    SELECT measure, score FROM ScoringRubric WHERE measure = 'TV' AND @TvScore BETWEEN bottom_of_range AND top_of_range
)
GO

然后你可以像这样编写查询和数据透视:

SELECT
    Final.name
    , Final.OriginalHeight AS height
    , Final.OriginalWeight AS [weight]
    , Final.OriginalTv AS TV
    , Final.Height AS tv_height_score
    , Final.[Weight] AS tv_weight_score
    , Final.TV AS tv_score
FROM
(
    SELECT
        UP.name
        , UP.height AS OriginalHeight
        , UP.[weight] AS OriginalWeight
        , UP.TV AS OriginalTv
        , Measures.measure
        , Measures.score
    FROM UserProfile UP
    CROSS APPLY dbo.fGetMeasureScoresOriginal(UP.height, UP.[weight], UP.TV) Measures
    WHERE UP.name = @User
) Base
PIVOT
(
    MAX(score)
    FOR measure
    IN
    (
        [Height]
        , [Weight]
        , [TV]
    )
) Final

编辑 :刚刚意识到我没有回答原来的问题。 现在添加。

如果要转动静态行集,可以使用我在此处描述的技术。

重新发布示例,因此它将是不同的表,但我认为该技术也适用于您的情况。

SELECT
  Customers.CustID,
  MAX(CASE WHEN CF.FieldID = 1 THEN CF.FieldValue ELSE NULL END) AS Field1,
  MAX(CASE WHEN CF.FieldID = 2 THEN CF.FieldValue ELSE NULL END) AS Field2,
  MAX(CASE WHEN CF.FieldID = 3 THEN CF.FieldValue ELSE NULL END) AS Field3,
  MAX(CASE WHEN CF.FieldID = 4 THEN CF.FieldValue ELSE NULL END) AS Field4
  -- Add more...

  FROM Customers 

  LEFT OUTER JOIN CustomFields CF
  ON CF.ID = Customers.CustID

  WHERE Customers.CustName like 'C%'

  GROUP BY Customers.CustID

在没有改变太多(希望)的情况下,我会在函数中进行旋转,然后使用CROSS APPLY中的函数并拉出列。 所以如果你的函数是这样的:

CREATE FUNCTION dbo.ScoringFunction ()
RETURNS TABLE
RETURN (

SELECT Measure, Score
FROM …

)

然后我会像这样重写它:

CREATE FUNCTION dbo.ScoringFunction ()
RETURNS TABLE
RETURN (

WITH originalSelect AS (
  SELECT Measure, Score
  FROM …
) SELECT , , … FROM originalSelect PIVOT ( MAX(Score) FOR Measure IN (, , …) ) p

)

并在最终查询中使用它,如下所示:

SELECT
  t.Name,
  t.,
  t.,
  …
  x.,
  x.,
  …
FROM atable t
CROSS APPLY dbo.ScoringFunction (t., t., …) x

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM