【问题标题】:Get schema of the result returned by select query获取选择查询返回的结果的架构
【发布时间】:2014-02-21 08:53:20
【问题描述】:

我们可以得到 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'";

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

  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'";

虽然这行不通。

【问题讨论】:

  • 此查询是否会被任何应用程序使用?像 C# 应用程序或类似的应用程序?
  • @PeterHenell 是的。会的。
  • 根据您需要此信息的原因,答案可能会有所不同。为什么需要获取查询结果的元数据?
  • @PeterHenell 我正在使用 SharePoint。要求是将选择查询的结果填充到 SharePoint 列表中。列表和数据的架构将被动态创建。

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


【解决方案1】:

您可以将查询结果选择到一个临时表中,并从该表中获取元数据。如果您选择这样的解决方案,您需要确保临时表的名称每次调用都是唯一的,否则 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

【讨论】:

  • 我会努力回去的。谢谢。
  • 我建议将 PRECISION 和 SCALE 添加到 information_schema 选择的列列表中
【解决方案2】:

我知道这个问题针对的是 SQL 2008,但很容易知道 2012+ 有一个名为 sp_describe_first_result_set 的内置存储过程,它可以得到所需的结果。

docs

【讨论】:

  • 感谢分享,杰夫
【解决方案3】:

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

给定一个任意的SELECT 查询,我们可以使用SqlDataReader.GetSchemaTable 获取架构。

不幸的是,我们需要执行查询,但我们可以通过将输出减少到 0 行来稍微减少影响。假设我们总是有一个 SELECT 语句,没有您可以使用的 TOP &lt;n&gt; 子句:

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;
}

【讨论】:

    猜你喜欢
    • 2019-05-10
    • 1970-01-01
    • 2019-03-23
    • 2017-10-08
    • 1970-01-01
    • 2016-05-08
    • 2017-02-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多