2

Lets have a following query:

SELECT * FROM {tablename} WHERE ColumnId = dbo.GetId()

where dbo.GetId() is non-deterministic user defined function. The question is whether dbo.GetId() is called only once for entire query and its result is then applied or is it called for each row? I think it is called for every row, but I don't know of any way how to prove it.

Also would following query be more efficient?

DECLARE @Id int
SET @Id = dbo.GetId()
SELECT * FROM {tablename} WHERE ColumnId = @Id
michal.jakubeczy
  • 8,221
  • 1
  • 59
  • 63

3 Answers3

3

I doubt this is guaranteed anywhere. Use a variable if you want to ensure it.

I amended @Prdp's example

CREATE VIEW vw_rand
AS
  SELECT Rand() ran

GO

/*Return 0 or 1 with 50% probability*/
CREATE FUNCTION dbo.Udf_non_deterministic ()
RETURNS INT
AS
  BEGIN
      RETURN
        (SELECT CAST(10000 * ran AS INT) % 2
         FROM   vw_rand)
  END

go

SELECT *
FROM master..spt_values
WHERE dbo.Udf_non_deterministic() = 1

In this case it is only evaluated once. Either all rows are returned or zero.

The reason for this is that the plan has a filter with a startup predicate.

enter image description here

The startup expression predicate is [tempdb].[dbo].[Udf_non_deterministic]()=(1).

This is only evaluated once when the filter is opened to see whether to get rows from the subtree at all - not for each row passing through it.

But conversely the below returns a different number of rows each time indicating that it is evaluated per row. The comparison to the column prevents it being evaluated up front in the filter as with the previous example.

SELECT *
FROM master..spt_values
WHERE dbo.Udf_non_deterministic() = (number - number)

And this rewrite goes back to evaluating once (for me) but CROSS APPLY still gave multiple evaluations.

SELECT *
FROM master..spt_values
OUTER APPLY(SELECT  dbo.Udf_non_deterministic() ) AS C(X)
WHERE X = (number - number)

enter image description here

Martin Smith
  • 438,706
  • 87
  • 741
  • 845
1

Here is one way to prove it

View

View is created to add a Nondeterministic inbuilt Functions inside user defined function

CREATE VIEW vw_rand
AS
  SELECT Rand() ran

Nondeterministic Functions

Now create a Nondeterministic user defined Functions using the above view

CREATE FUNCTION Udf_non_deterministic ()
RETURNS FLOAT
AS
  BEGIN
      RETURN
        (SELECT ran
         FROM   vw_rand)
  END

Sample table

CREATE TABLE #test
  (
     id   INT,
     name VARCHAR(50)
  )

INSERT #test
VALUES (1,'a'),
       (2,'b'),
       (3,'c'),
       (4,'d')

SELECT dbo.Udf_non_deterministic (), *
FROM   #test 

Result:

id  name    non_deterministic_val
1   a       0.203123494465542
2   b       0.888439497446073
3   c       0.633749721616085
4   d       0.104620204364744

As you can see for all the rows the function is called

Pரதீப்
  • 91,748
  • 19
  • 131
  • 172
0

Yes it does get called once per row. See following thread for debugging functions SQL Functions - Logging

And yes the below query is efficient as the function is called only once.

DECLARE @Id int
SET @Id = dbo.GetId()
SELECT * FROM {tablename} WHERE ColumnId = @Id
Community
  • 1
  • 1
DhruvJoshi
  • 17,041
  • 6
  • 41
  • 60