3 Ways to Get the Schema of a Result Set in SQL Server

Sometimes when you run a query in SQL Server, you might want to know what the underlying data type of each column is, its precision, length, whether or not its nullable, etc.

If you’re querying just one table, you can get this sort of data with procedures such as sp_columns. But if your query runs across many tables, this could get unwieldy very quickly.

Fortunately there are several ways you can go about getting such metadata for a result set in SQL Server.

The sp_describe_first_result_set System Stored Procedure

The sp_describe_first_result_set system stored procedure was designed specifically for returning the metadata for a result set.

It accepts three parameters, but the last two are optional. The first parameter is the T-SQL query that you want to analyse.

Here’s an example of this procedure in action.

DECLARE @tsql_query nvarchar(max);
SET @tsql_query = 'SELECT
    ar.ArtistName,
    al.AlbumName,
    g.Genre
FROM 
    Artists ar 
    INNER JOIN Albums al 
    ON ar.ArtistId = al.ArtistId 
    INNER JOIN Homer.Music.dbo.Genres g 
    ON al.GenreId = g.GenreId';

EXEC sp_describe_first_result_set @tsql_query, null, 1;

You can actually provide the query as a string literal, but in this case I’ve put it in a variable called @tsql_query.

Result:

+-------------+------------------+------------+---------------+------------------+--------------------+--------------+-------------+---------+------------------------------+----------------+----------------------+--------------------+------------------+--------------------------------+---------------------+---------------------------+-------------------------+-----------------------+-------------------+---------------------+----------------------------+-----------------+-------------------+-----------------+----------------+-----------------+----------------------+-------------------------+-----------------+----------------------+------------------------+----------------------------+--------------------------+------------------------+---------------+--------------+--------------------+-------------------------+
| is_hidden   | column_ordinal   | name       | is_nullable   | system_type_id   | system_type_name   | max_length   | precision   | scale   | collation_name               | user_type_id   | user_type_database   | user_type_schema   | user_type_name   | assembly_qualified_type_name   | xml_collection_id   | xml_collection_database   | xml_collection_schema   | xml_collection_name   | is_xml_document   | is_case_sensitive   | is_fixed_length_clr_type   | source_server   | source_database   | source_schema   | source_table   | source_column   | is_identity_column   | is_part_of_unique_key   | is_updateable   | is_computed_column   | is_sparse_column_set   | ordinal_in_order_by_list   | order_by_is_descending   | order_by_list_length   | tds_type_id   | tds_length   | tds_collation_id   | tds_collation_sort_id   |
|-------------+------------------+------------+---------------+------------------+--------------------+--------------+-------------+---------+------------------------------+----------------+----------------------+--------------------+------------------+--------------------------------+---------------------+---------------------------+-------------------------+-----------------------+-------------------+---------------------+----------------------------+-----------------+-------------------+-----------------+----------------+-----------------+----------------------+-------------------------+-----------------+----------------------+------------------------+----------------------------+--------------------------+------------------------+---------------+--------------+--------------------+-------------------------|
| 0           | 1                | ArtistName | 0             | 231              | nvarchar(255)      | 510          | 0           | 0       | SQL_Latin1_General_CP1_CI_AS | NULL           | NULL                 | NULL               | NULL             | NULL                           | NULL                | NULL                      | NULL                    | NULL                  | 0                 | 0                   | 0                          | NULL            | Music             | dbo             | Artists        | ArtistName      | 0                    | 0                       | 1               | 0                    | 0                      | NULL                       | NULL                     | NULL                   | 231           | 510          | 13632521           | 52                      |
| 0           | 2                | AlbumName  | 0             | 231              | nvarchar(255)      | 510          | 0           | 0       | SQL_Latin1_General_CP1_CI_AS | NULL           | NULL                 | NULL               | NULL             | NULL                           | NULL                | NULL                      | NULL                    | NULL                  | 0                 | 0                   | 0                          | NULL            | Music             | dbo             | Albums         | AlbumName       | 0                    | 0                       | 1               | 0                    | 0                      | NULL                       | NULL                     | NULL                   | 231           | 510          | 13632521           | 52                      |
| 0           | 3                | Genre      | 0             | 231              | nvarchar(50)       | 100          | 0           | 0       | SQL_Latin1_General_CP1_CI_AS | NULL           | NULL                 | NULL               | NULL             | NULL                           | NULL                | NULL                      | NULL                    | NULL                  | 0                 | 0                   | 0                          | Homer           | Music             | dbo             | Genres         | Genre           | 0                    | 0                       | 1               | 0                    | 0                      | NULL                       | NULL                     | NULL                   | 231           | 100          | 13632521           | 52                      |
| 1           | 4                | ArtistId   | 0             | 56               | int                | 4            | 10          | 0       | NULL                         | NULL           | NULL                 | NULL               | NULL             | NULL                           | NULL                | NULL                      | NULL                    | NULL                  | 0                 | 0                   | 0                          | NULL            | Music             | dbo             | Artists        | ArtistId        | 1                    | 1                       | 0               | 0                    | 0                      | NULL                       | NULL                     | NULL                   | 56            | 4            | NULL               | NULL                    |
| 1           | 5                | AlbumId    | 0             | 56               | int                | 4            | 10          | 0       | NULL                         | NULL           | NULL                 | NULL               | NULL             | NULL                           | NULL                | NULL                      | NULL                    | NULL                  | 0                 | 0                   | 0                          | NULL            | Music             | dbo             | Albums         | AlbumId         | 1                    | 1                       | 0               | 0                    | 0                      | NULL                       | NULL                     | NULL                   | 56            | 4            | NULL               | NULL                    |
| 1           | 6                | GenreId    | 0             | 56               | int                | 4            | 10          | 0       | NULL                         | NULL           | NULL                 | NULL               | NULL             | NULL                           | NULL                | NULL                      | NULL                    | NULL                  | 0                 | 0                   | 0                          | Homer           | Music             | dbo             | Genres         | GenreId         | 0                    | 1                       | 1               | 0                    | 0                      | NULL                       | NULL                     | NULL                   | 56            | 4            | NULL               | NULL                    |
+-------------+------------------+------------+---------------+------------------+--------------------+--------------+-------------+---------+------------------------------+----------------+----------------------+--------------------+------------------+--------------------------------+---------------------+---------------------------+-------------------------+-----------------------+-------------------+---------------------+----------------------------+-----------------+-------------------+-----------------+----------------+-----------------+----------------------+-------------------------+-----------------+----------------------+------------------------+----------------------------+--------------------------+------------------------+---------------+--------------+--------------------+-------------------------+

As you can see, this stored procedure returns a lot of columns.

If you look carefully at the query that I’m analysing, you’ll notice that one of the tables referenced is on a linked server called “Homer”. In the results produced by sp_describe_first_result_set, the value in the source_server column reflects this linked server for that underlying column.

In this example, I used 1 as the third argument. You can also use 0 or 2. See How sp_describe_first_result_set Works for an explanation and examples of how this setting affects the results.

The sys.dm_exec_describe_first_result_set System View

The sys.dm_exec_describe_first_result_set system view uses the same algorithm as sp_describe_first_result_set, and it returns the same information.

So we can modify our previous example to use sys.dm_exec_describe_first_result_set instead of sp_describe_first_result_set.

DECLARE @tsql_query nvarchar(max);
SET @tsql_query = 'SELECT
    ar.ArtistName,
    al.AlbumName,
    g.Genre
FROM 
    Artists ar 
    INNER JOIN Albums al 
    ON ar.ArtistId = al.ArtistId 
    INNER JOIN Homer.Music.dbo.Genres g 
    ON al.GenreId = g.GenreId';
SELECT * 
FROM sys.dm_exec_describe_first_result_set(
    @tsql_query, 
    null, 
    1
    );

Result:

+-------------+------------------+------------+---------------+------------------+--------------------+--------------+-------------+---------+------------------------------+----------------+----------------------+--------------------+------------------+--------------------------------+---------------------+---------------------------+-------------------------+-----------------------+-------------------+---------------------+----------------------------+-----------------+-------------------+-----------------+----------------+-----------------+----------------------+-------------------------+-----------------+----------------------+------------------------+----------------------------+--------------------------+------------------------+----------------+------------------+---------------+-----------------+--------------+-------------------+
| is_hidden   | column_ordinal   | name       | is_nullable   | system_type_id   | system_type_name   | max_length   | precision   | scale   | collation_name               | user_type_id   | user_type_database   | user_type_schema   | user_type_name   | assembly_qualified_type_name   | xml_collection_id   | xml_collection_database   | xml_collection_schema   | xml_collection_name   | is_xml_document   | is_case_sensitive   | is_fixed_length_clr_type   | source_server   | source_database   | source_schema   | source_table   | source_column   | is_identity_column   | is_part_of_unique_key   | is_updateable   | is_computed_column   | is_sparse_column_set   | ordinal_in_order_by_list   | order_by_is_descending   | order_by_list_length   | error_number   | error_severity   | error_state   | error_message   | error_type   | error_type_desc   |
|-------------+------------------+------------+---------------+------------------+--------------------+--------------+-------------+---------+------------------------------+----------------+----------------------+--------------------+------------------+--------------------------------+---------------------+---------------------------+-------------------------+-----------------------+-------------------+---------------------+----------------------------+-----------------+-------------------+-----------------+----------------+-----------------+----------------------+-------------------------+-----------------+----------------------+------------------------+----------------------------+--------------------------+------------------------+----------------+------------------+---------------+-----------------+--------------+-------------------|
| 0           | 1                | ArtistName | 0             | 231              | nvarchar(255)      | 510          | 0           | 0       | SQL_Latin1_General_CP1_CI_AS | NULL           | NULL                 | NULL               | NULL             | NULL                           | NULL                | NULL                      | NULL                    | NULL                  | 0                 | 0                   | 0                          | NULL            | Music             | dbo             | Artists        | ArtistName      | 0                    | 0                       | 1               | 0                    | 0                      | NULL                       | NULL                     | NULL                   | NULL           | NULL             | NULL          | NULL            | NULL         | NULL              |
| 0           | 2                | AlbumName  | 0             | 231              | nvarchar(255)      | 510          | 0           | 0       | SQL_Latin1_General_CP1_CI_AS | NULL           | NULL                 | NULL               | NULL             | NULL                           | NULL                | NULL                      | NULL                    | NULL                  | 0                 | 0                   | 0                          | NULL            | Music             | dbo             | Albums         | AlbumName       | 0                    | 0                       | 1               | 0                    | 0                      | NULL                       | NULL                     | NULL                   | NULL           | NULL             | NULL          | NULL            | NULL         | NULL              |
| 0           | 3                | Genre      | 0             | 231              | nvarchar(50)       | 100          | 0           | 0       | SQL_Latin1_General_CP1_CI_AS | NULL           | NULL                 | NULL               | NULL             | NULL                           | NULL                | NULL                      | NULL                    | NULL                  | 0                 | 0                   | 0                          | Homer           | Music             | dbo             | Genres         | Genre           | 0                    | 0                       | 1               | 0                    | 0                      | NULL                       | NULL                     | NULL                   | NULL           | NULL             | NULL          | NULL            | NULL         | NULL              |
| 1           | 4                | ArtistId   | 0             | 56               | int                | 4            | 10          | 0       | NULL                         | NULL           | NULL                 | NULL               | NULL             | NULL                           | NULL                | NULL                      | NULL                    | NULL                  | 0                 | 0                   | 0                          | NULL            | Music             | dbo             | Artists        | ArtistId        | 1                    | 1                       | 0               | 0                    | 0                      | NULL                       | NULL                     | NULL                   | NULL           | NULL             | NULL          | NULL            | NULL         | NULL              |
| 1           | 5                | AlbumId    | 0             | 56               | int                | 4            | 10          | 0       | NULL                         | NULL           | NULL                 | NULL               | NULL             | NULL                           | NULL                | NULL                      | NULL                    | NULL                  | 0                 | 0                   | 0                          | NULL            | Music             | dbo             | Albums         | AlbumId         | 1                    | 1                       | 0               | 0                    | 0                      | NULL                       | NULL                     | NULL                   | NULL           | NULL             | NULL          | NULL            | NULL         | NULL              |
| 1           | 6                | GenreId    | 0             | 56               | int                | 4            | 10          | 0       | NULL                         | NULL           | NULL                 | NULL               | NULL             | NULL                           | NULL                | NULL                      | NULL                    | NULL                  | 0                 | 0                   | 0                          | Homer           | Music             | dbo             | Genres         | GenreId         | 0                    | 1                       | 1               | 0                    | 0                      | NULL                       | NULL                     | NULL                   | NULL           | NULL             | NULL          | NULL            | NULL         | NULL              |
+-------------+------------------+------------+---------------+------------------+--------------------+--------------+-------------+---------+------------------------------+----------------+----------------------+--------------------+------------------+--------------------------------+---------------------+---------------------------+-------------------------+-----------------------+-------------------+---------------------+----------------------------+-----------------+-------------------+-----------------+----------------+-----------------+----------------------+-------------------------+-----------------+----------------------+------------------------+----------------------------+--------------------------+------------------------+----------------+------------------+---------------+-----------------+--------------+-------------------+

As with sp_describe_first_result_set, you can change the second and third arguments. See How sys.dm_exec_describe_first_result_set Works for examples.

Using OPENROWSET

If you look at the source code for sys.dm_exec_describe_first_result_set, you’ll see that it uses OPENROWSET to achieve its results.

Similarly, we could use OPENROWSET to do a similar thing. For example, we could use OPENROWSET to run the top zero results of a query into a temporary table, then use the sp_columns procedure to return column information about that temporary table (which reflects the result set of our query).

SELECT TOP 0 * INTO #TempTable 
FROM OPENROWSET(
    'SQLNCLI', 
    'Server=localhost;Trusted_Connection=yes;', 
    'SELECT
    ar.ArtistName,
    al.AlbumName,
    g.Genre
FROM 
    Music.dbo.Artists ar 
    INNER JOIN Music.dbo.Albums al 
    ON ar.ArtistId = al.ArtistId 
    INNER JOIN Music.dbo.Genres g 
    ON al.GenreId = g.GenreId');
EXEC('USE tempdb EXEC sp_columns #TempTable DROP TABLE #TempTable');

Result:

+-------------------+---------------+----------------------------------------------------------------------------------------------------------------------------------+---------------+-------------+-------------+-------------+----------+---------+---------+------------+-----------+--------------+-----------------+--------------------+---------------------+--------------------+---------------+----------------+
| TABLE_QUALIFIER   | TABLE_OWNER   | TABLE_NAME                                                                                                                       | COLUMN_NAME   | DATA_TYPE   | TYPE_NAME   | PRECISION   | LENGTH   | SCALE   | RADIX   | NULLABLE   | REMARKS   | COLUMN_DEF   | SQL_DATA_TYPE   | SQL_DATETIME_SUB   | CHAR_OCTET_LENGTH   | ORDINAL_POSITION   | IS_NULLABLE   | SS_DATA_TYPE   |
|-------------------+---------------+----------------------------------------------------------------------------------------------------------------------------------+---------------+-------------+-------------+-------------+----------+---------+---------+------------+-----------+--------------+-----------------+--------------------+---------------------+--------------------+---------------+----------------|
| tempdb            | dbo           | #TempTable__________________________________________________________________________________________________________00000000000C | ArtistName    | -9          | nvarchar    | 255         | 510      | NULL    | NULL    | 0          | NULL      | NULL         | -9              | NULL               | 510                 | 1                  | NO            | 39             |
| tempdb            | dbo           | #TempTable__________________________________________________________________________________________________________00000000000C | AlbumName     | -9          | nvarchar    | 255         | 510      | NULL    | NULL    | 0          | NULL      | NULL         | -9              | NULL               | 510                 | 2                  | NO            | 39             |
| tempdb            | dbo           | #TempTable__________________________________________________________________________________________________________00000000000C | Genre         | -9          | nvarchar    | 50          | 100      | NULL    | NULL    | 0          | NULL      | NULL         | -9              | NULL               | 100                 | 3                  | NO            | 39             |
+-------------------+---------------+----------------------------------------------------------------------------------------------------------------------------------+---------------+-------------+-------------+-------------+----------+---------+---------+------------+-----------+--------------+-----------------+--------------------+---------------------+--------------------+---------------+----------------+

You could replace sp_columns with sp_help if preferred.

I think the first two methods are better, but at least it’s another option (especially if you’re using an older version of SQL Server that doesn’t support sys.dm_exec_describe_first_result_set or sp_describe_first_result_set.