获取select查询返回的结果的模式

时间:2014-02-21 08:53:21

标签: sql-server database sql-server-2008-r2 relational-database

我们可以获得SELECT Query返回的结果的模式吗?代码如下:

string SQLQuery = "SELECT DISTINCT c.name 'Column Name',  t.Name 'Data type' FROM" +
                   " sys.columns c INNER JOIN " +
                   " sys.types t ON c.system_type_id = t.system_type_id" +
                   " LEFT OUTER JOIN " +
                   " sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id" +
                   " LEFT OUTER JOIN" +
                   " sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id" +
                   " WHERE" +
                   " c.object_id = OBJECT_ID('[DB].[dbo].[" + ddlTable.SelectedItem.Text + "]') AND t.name <> 'sysname'";

此代码返回列名以及指定表的数据类型。我的要求是从select查询中获取列名和数据类型,而不是直接指定表名。喜欢

  string SQLQuery = "SELECT DISTINCT c.name 'Column Name',  t.Name 'Data type' FROM" +
                       " sys.columns c INNER JOIN " +
                       " sys.types t ON c.system_type_id = t.system_type_id" +
                       " LEFT OUTER JOIN " +
                       " sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id" +
                       " LEFT OUTER JOIN" +
                       " sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id" +
                       " WHERE" +
                       " c.object_id = OBJECT_ID('SELECT col1, col2 from table(s)') AND t.name <> 'sysname'";

虽然这不起作用。

3 个答案:

答案 0 :(得分:1)

您可以将查询结果选入INTO临时表,并从该表中获取元数据。如果选择这样的解决方案,则需要确保每次调用临时表的名称为UNIQUE,否则information_schema视图中的元数据将在会话中共享。

if OBJECT_ID('tempdb..#tmp') is not null drop table #tmp

select * 
into #tmp
from Customer where 1 = 0 
-- select with a false predicate in order to ONLY get
--     metadata of the query and no rows of data.

select * from #tmp
-- Then select metadata from information_schema view
select COLUMN_NAME, DATA_TYPE from tempdb.information_schema.columns where TABLE_NAME like '#tmp%'

Result:
CustomerId  int
CustomerType    int
Name    nvarchar
IsActive    bit

或者,如果你想要,你可以更轻松地在C#中,这可能不会出错。获取查询结果集的元数据非常容易:

static void Main(string[] args)
        {
            string connStr = "Data Source=localhost;Initial Catalog=AdventureWorks;Integrated Security=True";
            SqlCommand cmd = new SqlCommand("select * from Orders where 1 = 0", new SqlConnection(connStr));

            SqlDataAdapter ad = new SqlDataAdapter(cmd);

            DataSet ds = new DataSet();
            ad.FillSchema(ds, SchemaType.Mapped);
            var metaTable = ds.Tables[0];

            foreach (DataColumn col in metaTable.Columns)
            {
                Console.WriteLine("{0} : {1}", col.DataType, col.ColumnName);
            }

         }

输出:

System.Int32 : OrderId
System.Int32 : CustomerId
System.Int32 : ArticleId
System.Decimal : TotalAmount
System.DateTime : OrderDate

答案 1 :(得分:0)

我知道这个问题针对的是SQL 2008,但是对于2012+而言,有一个内置的存储过程sp_describe_first_result_set可以获得所需的结果。

请参阅docs

答案 2 :(得分:0)

从您的示例中可以看出,您使用的是C#或类似的东西。假设您正在尝试获取架构数据以便在程序中执行操作而不是构建仅限SQL的机制,我可能会有一个解决方案。

鉴于任意SELECT查询,我们可以使用SqlDataReader.GetSchemaTable获取架构。

不幸的是我们需要执行查询,但我们可以通过将输出切换为0行来减少影响。假设我们总是有一个SELECT语句没有TOP <n>子句你可以使用:

var sql = "SELECT TOP 0 " + query.SubString("SELECT ".Length);

或者你可以将整个东西包裹在外部SELECT中,如下所示:

var sql = "SELECT TOP 0 * FROM (" + query + ") qq";

这可以捕获大多数表格,它应该是相当明显的。

从此处我们为查询创建DbCommand并打开DbDataReader实例以获取架构。架构将进入DataTable,然后您可以读取输出。

DataTable schema;

using (var command = connection.CreateCommand())
{
    command.CommandText = sql;
    using (var reader = command.ExecuteReader())
        schema = reader.GetSchemaTable();
}

此时schema包含查询中每个字段的行,其中包含您可能需要的所有信息。

我为自己的一个程序添加了这个扩展方法:

public static Dictionary<string, (Type DataType, bool AllowDBNull, bool IsReadOnly)> GetQuerySchema(this IDbConnection connection, string query)
{
    var conn = connection as DbConnection;
    if (conn == null)
        return null;

    if (conn.State != ConnectionState.Open)
        conn.Open();

    string sql;
    if (query.StartsWith("SELECT"))
        sql = "SELECT TOP 0 * FROM (" + query + ") qq";
    else
        sql = "SELECT TOP 0 * FROM " + query;

    DataTable schema;
    using (var command = conn.CreateCommand())
    {
        command.CommandText = sql;
        using (var reader = command.ExecuteReader())
            schema = reader.GetSchemaTable();
    }

    var res = 
        schema.Rows.OfType<DataRow>()
        .ToDictionary
        (
            _ => _.Field<string>("ColumnName"),
            _ => 
            (
                _.Field<Type>("DataType"),
                _.Field<bool>("AllowDBNull"),
                _.Field<bool>("IsReadOnly")
            )
        );
    return res;
}