1

我有一个 SQL SELECT 语句,直到运行时才知道,它可能包含 JOIN 和内部选择。我需要从 C# 中确定语句返回结果的每一列的名称和数据类型。我倾向于做类似的事情:

string orginalSelectStatement = "SELECT * FROM MyTable";

string selectStatement = string.Format("SELECT TOP 0 * FROM ({0}) s", orginalSelectStatement);
SqlConnection connection = new SqlConnection(@"MyConnectionString");
SqlDataAdapter adapter = new SqlDataAdapter(selectStatement, connection);

DataTable table = new DataTable();
adapter.Fill(table);

foreach (DataColumn column in table.Columns)
{
    Console.WriteLine("Name: {0}; Type: {1}", column.ColumnName, column.DataType);
}

有没有更好的方法来做我想做的事情?“更好”是指完成相同任务的资源较少的方式或完成相同任务的更确定的方式(即,据我所知,我刚刚给出的代码片段在某些情况下会失败)。

解决方案:首先,我的TOP 0技巧很糟糕,即对于这样的事情:

SELECT TOP 0 * FROM (SELECT 0 AS A, 1 AS A) S

换句话说,在子选择中,如果两个事物被别名为相同的名称,则会引发错误。所以它不在图片中。但是,为了完整起见,我继续测试了它,以及两个建议的解决方案: SET FMTONLY ONGetSchemaTable.

以下是结果(每 1,000 个查询以毫秒为单位):

模式时间:3130

TOP 0 时间:2808

FMTONLY 上线时间:2937

我的建议是GetSchemaTable,因为它更有可能通过删除SET FMTONLY ONas valid SQL 来适应未来,并且它解决了别名问题,即使它稍微慢一些。 但是,如果您“知道”重复的列名永远不会成为问题,那么TOP 0GetSchemaTableSET FMTONLY ON.

这是我的实验代码:

int schemaTime = 0;
int topTime = 0;
int fmtOnTime = 0;

SqlConnection connection = new SqlConnection(@"MyConnectionString");
connection.Open();

SqlCommand schemaCommand = new SqlCommand("SELECT * FROM MyTable", connection);
SqlCommand topCommand = new SqlCommand("SELECT TOP 0 * FROM (SELECT * FROM MyTable) S", connection);
SqlCommand fmtOnCommand = new SqlCommand("SET FMTONLY ON; SELECT * FROM MyTable", connection);

for (int i = 0; i < 1000; i++)
{
    {
        DateTime start = DateTime.Now;
        using (SqlDataReader reader = schemaCommand.ExecuteReader(CommandBehavior.SchemaOnly))
        {
            DataTable table = reader.GetSchemaTable();
        }
        DateTime stop = DateTime.Now;
        TimeSpan span = stop - start;
        schemaTime += span.Milliseconds;
    }

    {
        DateTime start = DateTime.Now;

        DataTable table = new DataTable();
        SqlDataAdapter adapter = new SqlDataAdapter(topCommand);
        adapter.Fill(table);

        DateTime stop = DateTime.Now;
        TimeSpan span = stop - start;
        topTime += span.Milliseconds;
    }

    {
        DateTime start = DateTime.Now;

        DataTable table = new DataTable();
        SqlDataAdapter adapter = new SqlDataAdapter(fmtOnCommand);
        adapter.Fill(table);

        DateTime stop = DateTime.Now;
        TimeSpan span = stop - start;
        fmtOnTime += span.Milliseconds;
    }
}

Console.WriteLine("Schema Time: " + schemaTime);
Console.WriteLine("TOP 0 Time: " + topTime);
Console.WriteLine("FMTONLY ON Time: " + fmtOnTime);

connection.Close();
4

3 回答 3

5

您可以使用GetSchemaTable做您想做的事。

这里有一个如何使用它的例子。

于 2012-04-18T19:38:29.093 回答
2

如果使用 SQL Server,我会尝试使用SET FMTONLY ON

仅将元数据返回给客户端。可用于在不实际运行查询的情况下测试响应的格式。

显然在 SQL Server 2012 上,有更好的方法。全部在链接的 MSDN 文章中指定。

顺便说一句,这种技术是 LINQ To SQL 在内部用来确定存储过程等返回的结果集的技术。

于 2012-04-18T19:41:07.727 回答
0

动态 SQL 总是有点雷区,但您可以在查询上设置 FMTONLY ON - 这意味着查询将只返回元数据,就像没有返回任何结果一样。所以:

string selectStatement = string.Format("SET FMTONLY ON; {0}", orginalSelectStatement);

或者,如果您没有绑定到 ADO,您是否可以不走Linq-to-SQL路线并生成一个数据上下文,它将您的所有数据库模式映射到代码及其相关类型?您还可以查看一些微型 ORM,例如Dapper.Net

还有很多其他的 ORM。

于 2012-04-18T19:53:32.463 回答