So I like kung-fu movies. I don’t care how bad the acting is, as long as there is some master/relative/friend who has been killed who needs avenged. Often good movies are made this way.
In software, a good idea often comes out of getting tired of seeing bugs on a sore subject, often not even your fault. So, in a fit of rage, I locked myself in my office for a few weeks and came up with the streaming table-valued function, since used as the proverbial duct-tape within the QP for all sorts of unforseen features. However, it has a “walking on rice paper without leaving footprints” vibe, so I will tell you a bit about this guy
.
(I did the QO part for this operator and a chunk of the architecture for it – there were others who worked on it too, however).
So a query operator takes rows in the bottom and spits rows out the top (yes, none of the exposed query plan graphs are drawn this way, but that’s how people talk about it
. Some operators take more than one input – most of the ones with 2 inputs are called “joins”. Some can take an arbitrary number of inputs (usually called UNIONs). Some take parameters (Compute Scalar). most don’t take parameters, however.
After you’ve done the 10 or so common operators in a query processor, there’s a lot of specialty problems that don’t really fit well into those operators. So people end up trying all sorts of crazy paths to get the data they need out to customers. Exuberence can often cause a few bugs, however, so eventually something needed to be done.
So, the “Streaming Table Valued Function” (STVF) is a relational operator (takes in rows and passes out rows). In this case, it takes in some number of scalar parameters (like any good function) and spits out zero or more rows. The beauty of this operator is that anyone can program anything to get returned, as long as they return back rows – it’s like a user-defined operator for internal folks to write features for customers. Amazingly, there are a lot of these, and you can see them in Dynamic Management Views (amongst others).
So, when you are looking at the plans in the plan cache or some other DMV, you are using this nifty operator:
SELECT usecounts, cacheobjtype, objtype, text
FROM sys.dm_exec_cached_plans
CROSS APPLY sys.dm_exec_sql_text(plan_handle)
WHERE usecounts > 1
ORDER BY usecounts DESC;
(actually you are using it _twice_ in this case, as there are two different sys.dm* references).
|–Nested Loops(Inner Join, OUTER REFERENCES:(SYSDMEXECCACHEDPLANS.[plan_handle]))
|–Sort(ORDER BY:(SYSDMEXECCACHEDPLANS.[usecounts] DESC))
| |–Filter(WHERE:(SYSDMEXECCACHEDPLANS.[usecounts]>(1)))
| |–Table-valued function
|–Table-valued function
In this plan, you can see that rows are pulled from one TVF, are filtered, then sorted, then passed into the second TVF.
The “OUTER REFERENCE” is the key that the loops join is passing something from the inner side to the outer side (remember in the crazy syntax used here, the first row below the Nested Loops is the “inner” and the second line is the “outer”. So, the Sort/Filter/TVF is the “inner” in this case.
These things differ from the traditional TVFs that you could create in SQL Server as a user. Those were actually backed by temporary tables in many cases, and this made it a pretty heavy-weight solution if you just want to pass a few rows into the server. _Streaming_ sounds a lot better than that, you must admit.
Many operators in a QP are streaming – filter is, as is a table scan. However a sort operator is called a “stop-and-go” operator because it generally consumes all of its input rows before producing the first output row. Streaming TVFs would fall into the “not stop-and-go” category.
This operator is used heavily starting in SQL 2005 in lots and lots of places. It is not really something you can directly reference (there is no exposed syntax for this operator), and as such it requires a bit of hunting to track down the various places it gets used.
I’ll leave it as an exercise for you guys to find a few non-DMV places that use STVFs. Any TVF that takes arguments is likely an STVF. So go look at those query plans, folks! First one who tells me a non-DMV/DMF STVF feature gets a star next to their name!
I also know that there is at least one new feature in SQL Server 2008 that uses the STVF.
Happy querying!
Conor Cunningham
4 Responses to Operator of the Day: Streaming Table-Valued Functions (aka the Dynamic Management View/Function)
Hi Conor,
First off – your blog is awesome
Perhaps you can answer a question that’s bugging me: Why can’t I get the query plan for user defined functions and why don’t they add to the query cost in cases where they definitely make it worse.
It’s probably complicated "graphically" when the function contains multiple statements, but it can help when debugging and tuning queries (even a correct executes count would do).
Thanks,
S. Neumann
iFTS?
To answer both comments:
1. I’m very glad that people are enjoying the blog. I like to write some unique content and I hope people get something out of it.
2. I’ll do a post about T-SQL UDFs and CLR UDFs at some point – you might not like what I have to say, but I’ll write about it
.
3. It’s humbling that Adam reads the blog. Methinks he has a bit of inside information, however, given that I think he’s still an MVP
. However, gold star to Adam! I am working on a statue in my backyard this evening to memorialize his accomplishment. Others can aspire to join him in the "gallery of awesome sql statues" if you answer future questions right (and my daughter doesn’t decide to hide them while playing out there). I will also schedule a post on "integrated" full-text search (iFTS), which I believe is a new feature in SQL 2008 (I hope it’s made it in, but I haven’t checked yet). I believe it uses the STVF functionality.
I can tell you that it wasn’t the one I was considering, so you guys can keep guessing if you want…
It’s even more humbling to me that you think it’s humbling that I read your blog <g> … and I would like my statue in platinum rather than gold, please!
Yes, iFTS is in the most recent CTP… but getting back to 2005 features, how about the XML data type’s .Nodes() method?