I have a TVF that returns two columns: 'measure' (a name) and 'score', a numeric score for that measure:
dbo.ScoringFunction(param1, param2, ..., paramN)
Measure Score
------- -----
measure1 10
measure2 5
... ...
measureN 15
I'm running this function against a large number of rows that contain its parameters:
Name Param1 Param2 ... ParamN
---- ------ ------ ------
Daniel 12 5 6
etc.
I am trying to find a way to display the measures and their scores next to the parameters that determine those scores:
Name Param1 Param2 ... ParamN measure1 measure2 ... measureN
---- ------ ------ ------ -------- -------- --------
Daniel 12 5 6 10 5 15
etc.
So far I have tried using a pivot table, but it's tricky since the data being pivoted is contained in a TVF rather than a static table. I've also tried using CROSS APPLY, but once I have the data (measures & scores), I'm still unable to pivot it into a nicely formatted row.
If anybody has any ideas, they would be much appreciated!
If you make a function that looks like this:
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
And one that looks like this:
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
Then you can get your data with either of the following:
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
I don't really know which one you'll find most appropriate for your uses. Let me know if you have questions.
As for your original question, if this were the function:
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
Then you could write the query and pivot like so:
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
EDIT : Just realized I didn't answer the original question. Adding that now.
If you want to pivot a static set of rows you can use the technique as I described here .
Reposting the example, so it will be different tables, but the technique can be applied in your case as well I think.
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
Without changing too much (hopefully), I would do the pivoting in the function , then use the function in CROSS APPLY and pull the columns. So if your function is something like this:
CREATE FUNCTION dbo.ScoringFunction (parameters)
RETURNS TABLE
RETURN (
SELECT Measure, Score
FROM …
)
then I would rewrite it like this:
CREATE FUNCTION dbo.ScoringFunction (parameters)
RETURNS TABLE
RETURN (
SELECT Measure, Score
FROM …
)
and use it in the final query like this:
SELECT
t.Name,
t.param1,
t.param2,
…
x.measure1,
x.measure2,
…
FROM atable t
CROSS APPLY dbo.ScoringFunction (t.param1, t.param2, …) x
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.