透视表值函数

时间:2012-07-02 20:52:44

标签: sql sql-server

我有一个返回两列的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,但是一旦我掌握了数据(测量和分数),我仍然无法将其转换为格式良好的行。

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

3 个答案:

答案 0 :(得分:0)

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

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

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

答案 1 :(得分:0)

如果要转动静态行集,可以使用我所描述的技术here

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

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

答案 2 :(得分:0)

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

CREATE FUNCTION dbo.ScoringFunction (parameters)
RETURNS TABLE
RETURN (

SELECT Measure, Score
FROM …

)

然后我会像这样重写它:

CREATE FUNCTION dbo.ScoringFunction (parameters)
RETURNS TABLE
RETURN (

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

)

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

SELECT
  t.Name,
  t.param1,
  t.param2,
  …
  x.measure1,
  x.measure2,
  …
FROM atable t
CROSS APPLY dbo.ScoringFunction (t.param1, t.param2, …) x