I have a pretty complex query, but I applied some indexing and now it runs very smoothly in less than 1 second. The structure of the query is like this (I find it unnecessary to post the full query as I will later prove – the fault is not in the query itself):
DECLARE @period varchar(6);
SET @period = '201302';
DECLARE @day datetime;
SET @day = dba.fnu_firstdate(@period);//returns 2013-02-01
SELECT
user_id,
(SELECT CAST(MAX(c1) AS varchar) FROM table t WHERE t.user_id = table.user_id AND when = DATEADD(day, 0, @day)) Day01,
...
(SELECT CAST(MAX(c1) AS varchar) FROM table t WHERE t.user_id = table.user_id AND when = DATEADD(day, 30, @day)) Day31
FROM
table
So yeah, if I execute this query, it takes about 1 second to complete, which is perfectly fine for me. However, as you can see, I need to supply parameter for it. Thus I changed it to a table valued function so I could easily make select queries from it:
CREATE FUNCTION fnu_data(@period varchar(6))
RETURNS @results TABLE
(
id int,
Day01 varchar(10) null,
...
Day31 varchar(10) null
)
AS
BEGIN
DECLARE @day datetime;
SET @day = dba.fnu_firstdate(@period);
INSERT INTO @results
(
id,
Day01,
...
Day31
)
SELECT
SELECT
user_id,
(SELECT CAST(MAX(c1) AS varchar) FROM table t WHERE t.user_id = table.user_id AND when = DATEADD(day, 0, @day)) Day01,
...
(SELECT CAST(MAX(c1) AS varchar) FROM table t WHERE t.user_id = table.user_id AND when = DATEADD(day, 30, @day)) Day31
FROM
table
RETURN
Now when I do
SELECT * FROM dba.fnu_data('201302')
it takes 6 seconds, which is way too long. Advised by my colleague I tried adding a primary index on id and replacing every subselect as joins, but it extended the time to execute the query to 8 seconds. (P.S. the query returns ~3200 rows).
In my opinion, the culprit is the insertion, but I do not see how I can get rid of it.
What can I do to improve my query?
Not sure what might cause the difference in performance between the standalone
SELECTand theINSERT ... SELECTas part of your function, but I could suggest a rewrite to your SELECT statement, as your SELECT looks definitely suboptimal to me.You seem to be doing a pivot, for which there’s a native syntax in SQL Server 2005+. Consider the following query:
It prepares the data for the specified month as a separate step, using a common table expression, then pivots the results with aggregating, using the PIVOT syntax.
Note that the above does the entire job using a single statement, which is also a SELECT statement. That means you could transform your multi-statement TVF into an inline TVF:
An inline TVF has the advantage before a multi-statement TVF in that the plan for it is chosen in consideration with the entire query in which the function is being called. An inline TVF is like a view in this respect.
Note that the transformation must be done using
DROP+CREATE, like above, because multi-statement TVFs and inline TVFs are distinct kinds of objects in SQL Server, and one can’t be ALTERed into the other.