We're experiencing performance issues in our SQL Server application (written in PHP, but we experience the same timings when running the queries in the management studio, so I don't think that's releveant).
The offending query is as follow:
SELECT
c.name, t.name AS type, c.is_nullable, c.is_identity,
object_definition(c.default_object_id) AS default_value,
c.precision, c.scale, c.max_length, c.collation_name,
CASE WHEN p.column_id IS NOT NULL THEN 1 ELSE 0 END AS is_primary,
CASE WHEN u.column_id IS NOT NULL THEN 1 ELSE 0 END AS is_unique
FROM
sys.columns AS c
LEFT JOIN sys.types AS t
ON c.user_type_id = t.user_type_id
LEFT JOIN (
SELECT DISTINCT
ic.object_id, ic.column_id
FROM sys.indexes ix
JOIN sys.index_columns ic
ON ix.object_id = ic.object_id
AND ix.index_id = ic.index_id
WHERE is_primary_key = 1
) AS p
ON p.object_id = c.object_id AND p.column_id = c.column_id
LEFT JOIN (
SELECT DISTINCT
ic.object_id, ic.column_id
FROM sys.indexes ix
JOIN sys.index_columns ic
ON ix.object_id = ic.object_id
AND ix.index_id = ic.index_id
WHERE is_unique = 1
) AS u
ON u.object_id = c.object_id AND u.column_id = c.column_id
WHERE
c.object_id = object_id('tblTestTable');
Testing locally on SQL Server 2014 Express we get a first-run time of about 0.3s with subsequent runs between 0.1s and 0.2s. On our production server, running the full version of SQL Server 2014, the performance is worse!
I would expect a query such as this (using the system tables) to be running much faster, e.g. in the 0.01 -> 0.05 range, which is the kind of performance we get for similar queries against our own user tables.
- Should I expect a query like this to be slow?
- If yes, is there an alternative, faster method of getting this information? If no, what should we do to optimise it?
I notice that these system views don't appear to have indexes. Is that a factor?
Also of relevance is that this query originally used the INFORMATION_SCHEMA views, but these were at least twice as slow as the current performance we are getting using sys (though possibly that was because the sub-selects were in the field list rather than in the join).
Note that the timings come from the properties window in the management studio, and are consistent with what I get if I microtime() the query execution from within PHP.
UPDATE #1
I have constructed a query using our user data tables, which is basically the same structure as the one above (or as close as I could get).
This query runs at about 0.14s on first run, and then between 0.015 and 0.07 on subsequent runs. This is the kind of performance I would be expecting for the sys
queries. Therefore, this appears to be an issue specific to the sys
tables, rather than a general server configuration issue.
I can post the query here if it would be helpful, but will hold-off for now in case it's just spammy.
UPDATE #2
As requested, here is the stats output with SET STATISTICS TIME|IO ON
for the original query, from a cold cache.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
(56 row(s) affected)
Table 'sysiscols'. Scan count 112, logical reads 224, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'sysidxstats'. Scan count 112, logical reads 224, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'sysschobjs'. Scan count 0, logical reads 448, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'syssingleobjrefs'. Scan count 0, logical reads 112, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'sysscalartypes'. Scan count 0, logical reads 112, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'syscolpars'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
(1 row(s) affected)
SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 210 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.