Quantcast
Channel: Erik Darling – Brent Ozar Unlimited®
Viewing all articles
Browse latest Browse all 370

SQL Server 2017 CU3 adds execution statistics for scalar-valued, user-defined functions

$
0
0

Estimated Plans Need Not Apply

This is, of course, only available in actual plans.

All together now:

But uh. Those numbers look weird to me.

Do they look weird to you?

Now, look, I’m not a math guy, but…

This is messed up

Stick with me, here.

If I run this query, QueryTimeStats registers 0 CPU and Elapsed time.

SELECT  TOP 10 u.Id
FROM    dbo.Users AS u;

Zero Bueno

If I run this query…

SELECT  TOP 10   u.Id, dbo.ScalarFunction(u.Id)
FROM    dbo.Users AS u;

Your mom

Our query that previously used 0 CPU time is now using about 9 seconds of CPU time, but the UDF is only responsible for about 3 milliseconds of it.

Ooooookayyyyyy

What’s the function doing?

Something that uses a whole lot more than 3 ms of CPU time.

CREATE FUNCTION dbo.ScalarFunction
(
    @uid INT
)
RETURNS BIGINT
WITH RETURNS NULL ON NULL INPUT, SCHEMABINDING
AS
    BEGIN
        DECLARE @BCount BIGINT;
        SELECT      @BCount = COUNT_BIG(*)
        FROM        dbo.Badges AS b
        WHERE       b.UserId = @uid
        GROUP BY    b.UserId;
        RETURN @BCount;
    END;

Here’s the query plan for it.

Rorre

Isolation

What if we only run the function?

SELECT dbo.ScalarFunction(1)

Tater chips

What if we take the table access out of the picture?

SELECT  u.Id, dbo.ScalarFunction(u.Id)
FROM    ( VALUES ( -1 ), ( 1 ), ( 2 ), ( 3 ), ( 4 ), ( 5 ), ( 8 ), ( 9 ), ( 10 ), ( 11 )) AS u ( Id );

Tater Salad

Now, look, selecting 10 integers from a values clause doesn’t generate 8.5 seconds of CPU time on its own.

If it does, I don’t think you’re on a version of SQL Server that even supports the values clause.

What if we just run the query that’s in the function?

DECLARE @BCount BIGINT;  
SELECT  @BCount = COUNT_BIG(*)  
FROM    dbo.Badges AS b  
WHERE   b.UserId = 1
GROUP BY b.UserId;

Tater Face

Pattern Forming

The query inside the function takes ~853ms of CPU time. The elapsed time is 143ms. The disparity here is due to parallelism. The plan uses a DOP of 6, which increases the CPU time to decrease the elapsed time. Hopefully. Usually.

When scalar valued functions run, they do so per-row returned by the query. Whether we use the Users table, or the values construct, our top 10 query invokes the function 10 times.

If you multiply the number of function executions by the CPU and elapsed time stats for the query inside the function (853 and 143, respectively), you’ll get about to where the CPU and elapsed times land for both the values clause and the query against the Users table.

I don’t expect millisecond timing to be perfect. Threads are weird, as a wise man once said.

But showing the UDF with a value of 3ms is wildly incorrect.

Thanks for reading!

Brent says: I’m not mad. I’m not even disappointed. I’m happy that plans are continuing to get additional investment from Microsoft, and I know this bug is gonna get fixed because whoever wanted this feature bad enough to get it coded is also going to be bummed out about the implementation. It’ll get there eventually, just like wait stats in execution plans – more on that in tomorrow’s post.


Viewing all articles
Browse latest Browse all 370