简体   繁体   中英

Avoid calling a scalar function multiple times in an UPDATE statement

Is there any way to modify the following UPDATE statement so that the scalar function is called only once, not twice?

UPDATE a
    SET SomeField = b.SomeField
    FROM TableA AS a
    JOIN TableB b ON b.Link = a.Link
    WHERE b.CostMin <= @Cost * dbo.OneScalarFunction(@CurrencyFrom, b.Currency)
        AND b.CostMax >= @Cost * dbo.OneScalarFunction(@CurrencyFrom, b.Currency)

PS Using the BETWEEN operator does not help - SQL Server calls the scalar function twice anyway.

Try the BETWEEN operator.

WHERE functionCall(..) BETWEEN minValue AND maxValue

http://www.w3schools.com/sql/sql_between.asp

This is often significantly more performant, but requires you to change the scalar function to an inline table valued function.

UPDATE
  a
SET
  SomeField = b.SomeField
FROM
  TableA AS a
CROSS APPLY
  dbo.oneInlineTableValuedFunction(@CurrencyFrom, e.Currency) AS ITVF
INNER JOIN
  TableB b
    ON b.Link = a.Link
WHERE
      b.CostMin <= @Cost * ITVF.exchangeRate
  AND b.CostMax >= @Cost * ITVF.exchangeRate

Although table valued functions return tables, you can choose to retun just one row with one field. Then you're using it effectively as a scalar function - But, you get all the benefits of how SQL Server can optimise the query above...
- If the TVF is Inline (and not multi-statement)
- The TVF gets expanded out into the query
- The result is performance dramatiaclly better than scalar functions


Example Inline Table Valued Function:

CREATE FUNCTION dbo.oneInlineTableValuedFunction (
                      @currencyFrom   VARCHAR(32),
                      @currencyTo     VARCHAR(32)
)
RETURNS TABLE
AS
RETURN (
  SELECT
    exchangeRate
  FROM
    dbo.someTable
  WHERE
        currencyFrom = @currencyFrom
    AND currencyTo   = @currencyTo
)

Deliberately trivial

One example post about this: scalar-functions-inlining-and-performance

If you seach the web for INLINE CROSS APPLY SCALAR FUNCTION PERFORMANCE I'm sure you'll get a whole lot more.

You could try using BETWEEN (as below), although you'd need to test it as I've got a sneaking suspicion the database might split this out to execute it as >= and <= anyway..

UPDATE a
    SET SomeField = b.SomeField
    FROM TableA AS a
    JOIN TableB b ON b.Link = a.Link
    WHERE  @Cost * dbo.OneScalarFunction(@CurrencyFrom, e.Currency) BETWEEN b.CostMin AND b.CostMax

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.

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