views:

308

answers:

3

Hello, I need help with this scalar-valued function.

What I want to do is to return the value I get in MaxValue on the line max(Value) AS MaxValue.

The query works and will only return 1 value if ItemId and ListPropertyId exists, but I am not able to create a function of it.

CREATE FUNCTION GetLpivMax 
(
    -- Add the parameters for the function here
    @ItemId int,
    @ListPropertyId int
)
RETURNS int
AS
BEGIN
  DECLARE @output INT;
  WITH U AS (
    SELECT i.Id AS ItemId,
           lpiv.Value,
           lp.Id AS ListPropertyId
      FROM ListPropertyItemValues lpiv
      JOIN ListPropertyItems lpi ON lpi lpi.Id = lpiv.ListPropertyItemId 
      JOIN ListProperties lp ON lp.Id = lpi.ListPropertyId
      JOIN Items i ON i.Id = lpiv.ItemId)
    SELECT @output = MAX(u.value)
      FROM U u
     WHERE u.listpropertyid = @ListPropertyId 
       AND u.itemid = @ItemId
  GROUP BY u.listpropertyid, u.itemid

  RETURN @output
END
GO
A: 

Add these lines to your code and it should work:

Add this to the beginning (just after BEGIN)

DECLARE @output int

Add this to the end (just before END)

RETURN @output

Put it all together like this:

CREATE FUNCTION GetLpivMax 
(
    -- Add the parameters for the function here
    @ItemId int,
    @ListPropertyId int
)
RETURNS int
AS
BEGIN

  DECLARE @output INT;

  WITH U AS (
    SELECT i.Id AS ItemId,
           lpiv.Value,
           lp.Id AS ListPropertyId
      FROM ListPropertyItemValues lpiv
      JOIN ListPropertyItems lpi ON lpi lpi.Id = lpiv.ListPropertyItemId 
      JOIN ListProperties lp ON lp.Id = lpi.ListPropertyId
      JOIN Items i ON i.Id = lpiv.ItemId)

    SELECT @output = MAX(u.value)
      FROM U u
     WHERE u.listpropertyid = @ListPropertyId 
       AND u.itemid = @ItemId
  GROUP BY u.listpropertyid, u.itemid

  RETURN @output
END
GO
Gabriel McAdams
+2  A: 

Use:

DECLARE @output INT

BEGIN
  WITH U AS (
    SELECT i.Id AS ItemId,
           lpiv.Value,
           lp.Id AS ListPropertyId
      FROM ListPropertyItemValues lpiv
      JOIN ListPropertyItems lpi ON lpi.Id = lpiv.ListPropertyItemId 
      JOIN ListProperties lp ON lp.Id = lpi.ListPropertyId
      JOIN Items i ON i.Id = lpiv.ItemId)
    SELECT @output = MAX(u.value)
      FROM U u
     WHERE u.listpropertyid = @ListPropertyId 
       AND u.itemid = @ItemId
  GROUP BY u.listpropertyid, u.itemid

  RETURN @output
END

The SELECT @output = MAX(u.value) assigns the value to the @output variable, then you need to return it using the RETURN syntax. An alternative would be to set an out parameter, depending on your needs.

I also converted your JOIN syntax from ANSI-89 to ANSI-99. It's equivalent performance, but accommodates LEFT JOINs better than the older syntax and is widely supported on other databases (making it more portable if there's a need).

Also, try:

  SELECT @output = MAX(lpiv.Value)
    FROM ListPropertyItemValues lpiv
    JOIN ListPropertyItems lpi ON lpi.Id = lpiv.ListPropertyItemId 
    JOIN ListProperties lp ON lp.Id = lpi.ListPropertyId
    JOIN Items i ON i.Id = lpiv.ItemId
   WHERE lp.id = @ListPropertyId 
     AND i.id = @ItemId
GROUP BY lp.id, i.id

I don't think you need the CTE...

OMG Ponies
+1 Though, if the BEGIN is the one for the function, then shouldn't the declare of the @output variable go after it?
David Hall
@David: That's just a matter of personal taste. The scope of the `@output` variable is accessible within the BEGIN, I just keep my variable declaration out of the BEGIN for sake of being explicit about what is happening.
OMG Ponies
Thanks Ponies, but now I get the error `An expression of non-boolean type specified in a context where a condition is expected, near 'lpi'.`. I've updated my first post with the code you suggested, can you see what's wrong?
Mickel
The error is on line 17 ´SELECT i.Id AS ItemId,´
Mickel
@Mickel: Fixed it - I had `ON lpi lpi.id ...` in the `LISTPROPERTYITEMS` join.
OMG Ponies
Cheers! You're awesome :)
Mickel
+2  A: 

I've upvoted your question, but I don't like any of the answers. You should be changing your code to an inline table-valued function instead. This code will be considered procedural every time, and it's just a single query.

I wrote about it at http://msmvps.com/blogs/robfarley/archive/2009/12/05/dangers-of-begin-and-end.aspx, but I'll quickly explain what you should do:

CREATE FUNCTION GetLpivMax  
( 
    -- Add the parameters for the function here 
    @ItemId int, 
    @ListPropertyId int 
) 
RETURNS TABLE AS 
RETURN (
SELECT MAX(lpiv.Value) as LpivMax
    FROM ListPropertyItemValues lpiv 
    JOIN ListPropertyItems lpi ON lpi.Id = lpiv.ListPropertyItemId  
    JOIN ListProperties lp ON lp.Id = lpi.ListPropertyId 
    JOIN Items i ON i.Id = lpiv.ItemId 
   WHERE lp.id = @ListPropertyId  
     AND i.id = @ItemId 
GROUP BY lp.id, i.id 
);

Now use it like this:

SELECT ip.*, m.LpivMax
FROM ItemsAndProperties ip
CROSS APPLY
dbo.GetLpivMax(ip.ItemID, ip.ListPropertyID) m
;

You could use OUTER APPLY if you don't want to eliminate rows from your data set. You can add extra aggregates that will be completely ignored if you don't refer to them. But most of all, the Query Optimizer will simplify out your query, so that if it can avoid doing a big chunk of the work, it will.

Hope this helps...

Rob Farley
Cool. I had no idea :)
Mickel