views:

58

answers:

4

I have a function that takes 2 parameters: @iEmployeeID and @dDate.

It's purpose is to find a budget rate for the given parameters. In other words, it should find the largest date being smaller or equal to the @dDate argument, and return the rate that corresponds.

Budget rates are:

Start        Rate
-------      -----
01-01-2008   600
01-01-2009   800
01-01-2010   700
DECLARE @result decimal(38,20)

SELECT @result = decRate
FROM BudgetRates BR
WHERE BR.iRefEmployeeID = @iEmployeeID
GROUP BY decRate
HAVING MAX(BR.dStart) <= @dDate

RETURN @result

  • When supplied the argument 06-06-2008, it correctly returns 600.
  • When supplied the argument 03-03-2009, it correctly returns 800
  • When supplied the argument 02-02-2010, it should return 700. The function actually returns 800.

Where is the bug?

bug hunting: If I tweak around with the figures, it seems to pick the largest rate if it has 2 values to pick from.

+1  A: 

You should select top 1 using TOP 1 get the appropriate one with ORDER BY

DECLARE @result decimal(38,20)

SELECT TOP 1 @result = decRate
FROM BudgetRates BR
WHERE BR.iRefEmployeeID = @iEmployeeID
ORDER BY decRate DESC

RETURN @result
Raj More
When I do this I get. "Invalid use of a side-effecting operator 'SET ROW COUNT' within a function."
DoStuffZ
oh.. i forgot you can't do that in a function.. correcting for Top 1
Raj More
+4  A: 

Surely your code shouldn't be grouping at all?

SELECT TOP 1 @result = decRate
FROM BudgetRates BR
WHERE BR.iRefEmployeeID = @iEmployeeID
AND BR.dStart <= @dDate
ORDER BY BR.dStart DESC

RETURN @result
fritterfatboy
When I leave out the GROUP BY I get: Column 'BudgetRates.decRate' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. And Top 1 picks the rate 600.
DoStuffZ
@DoStuffz - You can't have left out the GROUP BY if you get that message.
Martin Smith
@DoStuffz: If `TOP 1` is picking a rate of 600, that sounds as if you left off the `DESC` on the `ORDER BY`.
Martin B
A: 

try this mate:

select @result = decRate
    from BudgetRates
      inner join (
       select max(BR.dStart) as MaxDate
       from BudgetRates
      where BR.dStart <= @dDate
        and BR.iRefEmployeeID = @iEmployeeID
                 ) temp on tmep.MaxDate = BudgetRates.dStart
                   and BudgetRates.iRefEmployeeID = @iEmployeeID
meeron
+1  A: 

Seems like you are supposed to use ranking functions here.

DECLARE @result decimal(38,20)

SELECT @result = decRate 
(
  SELECT decRate, ROW_NUMBER() OVER (ORDER BY BR.dStart DESC) rownum
  FROM BudgetRates BR
  WHERE BR.iRefEmployeeID = @iEmployeeID
  AND BR.dStart <= @dDate
) sub
WHERE rownum = 1

RETURN @result
ercan