如何将SQL表数据读入C# DataTable

118
我读了很多关于将DataTable插入到SQL表中的帖子,但是如何将SQL表拉入C#/.NET DataTable呢?

8
在DataAdapter上使用Fill方法? - John Bledsoe
http://msdn.microsoft.com/en-us/library/system.data.common.dataadapter.fill.aspx - gbn
6个回答

191
试试这个(这只是伪代码)
using System;
using System.Data;
using System.Data.SqlClient;


public class PullDataTest
{
    // your data table
    private DataTable dataTable = new DataTable();

    // your method to pull data from database to datatable   
    public void PullData()
    {
        string connString = @"your connection string here";
        string query = "select * from table";

        SqlConnection conn = new SqlConnection(connString);        
        SqlCommand cmd = new SqlCommand(query, conn);
        conn.Open();

        // create data adapter
        SqlDataAdapter da = new SqlDataAdapter(cmd);
        // this will query your database and return the result to your datatable
        da.Fill(dataTable);
        conn.Close();
        da.Dispose();
    }
}

22
在调用da.Fill(dataTable)之前,必须先初始化datatable字段。 - Dabblernl
@yonan2236 除了数据表之外,从T-SQL中获取输出参数怎么样?如何获取输出参数?这是可能的吗?有示例吗? - Ahmad Ebrahimi
2
这段代码容易出错,不建议以这种方式使用可用资源。请参考@Tim Rogers的答案获取清晰的解决方案。 - Xan-Kun Clark-Davis
除此之外,看一下 LINQ(如果还没有)因为它在这里确实可以做一些魔术 :-) - Xan-Kun Clark-Davis
1
尽管这段代码可以运行,但它没有利用 try/catchusing() 来处理错误。 - Si8
da.Fill需要一个DataSet而不是DataTable吗? @Xan-KunClark-Davis,您是否有一个类似的LINQ示例? - David Mays

90
var table = new DataTable();    
using (var da = new SqlDataAdapter("SELECT * FROM mytable", "connection string"))
{      
    da.Fill(table);
}

8
如果抛出异常,被接受回答中的代码会泄漏资源。如果你了解使用using的完全等效方式,你可能就不会那么厌恶它了。 - Ben Voigt
@Xan-KunClark-Davis 为什么你会鄙视 Using 呢?这就像鄙视 With 或者 Try-Catch 一样。我恰恰相反,如果一个类不支持 Using,我会感到失望。 - SteveCinq
等等,什么?谁鄙视使用try?我很困惑... - Xan-Kun Clark-Davis
这个答案如果能提供一个示例,说明如何将SqlDataAdapter外部的变量应用到SqlDataAdapter内部使用的SELECT语句中,将会大大有益。 - undefined

13
有很多方法。使用 ADO.NET 并在数据适配器上使用 fill 方法来获取 DataTable:
using (SqlDataAdapter dataAdapter
    = new SqlDataAdapter ("SELECT blah FROM blahblah ", sqlConn))
{
    // create the DataSet 
    DataSet dataSet = new DataSet(); 
    // fill the DataSet using our DataAdapter 
    dataAdapter.Fill (dataSet);
}

你可以从数据集中获取数据表。
在另一个答案中,没有使用数据集。相反,它使用了
// create data adapter
SqlDataAdapter da = new SqlDataAdapter(cmd);
// this will query your database and return the result to your datatable
da.Fill(dataTable);

哪个更好选择取决于我的需求。
我强烈建议使用实体框架;使用数据表和数据集并不是一个好主意。它们没有类型安全性,这意味着只能在运行时进行调试。使用强类型集合(可以通过使用LINQ2SQL或实体框架获得)会让你的生活变得更轻松。
数据表=好,数据集=邪恶。如果你正在使用ADO.NET,那么你可以同时使用这两种技术(EF、linq2sql、dapper、nhibernate、每月的ORM),因为它们通常是建立在ADO.NET之上的。你所获得的优势是,当模式发生变化时,你可以更容易地更新你的模型,前提是你有正确的抽象级别,通过利用代码生成。
ADO.NET适配器使用提供程序来公开数据库的类型信息,例如默认情况下它使用SQL Server提供程序,你还可以插入 - 例如 - devart PostgreSQL提供程序,仍然可以访问类型信息,这将允许你像上面那样使用你选择的ORM(几乎没有痛苦 - 有一些怪癖) - 我相信微软也提供了一个Oracle提供程序。这样做的整个目的是尽可能地抽象数据库实现。

2
类型化数据集具有类型安全性和强类型集合,就像 EF 一样。但这些仅适用于当您的应用程序与数据库紧密耦合时。如果您正在编写必须与许多不同数据库一起使用的工具,则类型安全性是一个无望的愿望。 - Ross Presser
1
在.NET中,类型化数据集是XML疯狂和悲伤的可怕创造物。我从未在一个愿意接受维护所有这些Microsoft类型化数据集开销的地方工作过。我认为即使微软现在也不建议这样做。至于多个数据库的类型安全性,当然可以实现 - 关键是尽快将其转换为类型化集合并传递,以便将类型问题限制在特定位置。ORM将有助于解决此问题,并且可以很好地与多个数据库配合使用。如果您不喜欢EF,请使用像Dapper这样更轻的东西。 - John Nicholas
1
你没有理解我的意思。如果你正在编写一个通用工具,它不知道将要连接到哪种数据库,那么类型安全是一个无望的愿望。 - Ross Presser
1
给定了SQL。此外,如果您不知道是什么样的数据库,那么为什么它必须是一个数据库呢?这种通用工具的应用将会是什么?如果您需要连接真正不同的数据库,那么您将在存储库模式后面进行抽象处理,然后在其中需要不同专业化的数据库适配器,在那时您将了解具体内容。事实是消费代码具有类型预期 -> 适配器中的类型断言。您的约束条件意味着您不知道数据库语言,因此无法查询。 - John Nicholas
3
假设你正在编写一个 SSMS 克隆版? - Ross Presser
显示剩余5条评论

11

厂商无关版本,仅依赖于ADO.NET接口; 有两种方式:

public DataTable Read1<T>(string query) where T : IDbConnection, new()
{
    using (var conn = new T())
    {
        using (var cmd = conn.CreateCommand())
        {
            cmd.CommandText = query;
            cmd.Connection.ConnectionString = _connectionString;
            cmd.Connection.Open();
            var table = new DataTable();
            table.Load(cmd.ExecuteReader());
            return table;
        }
    }
}

public DataTable Read2<S, T>(string query) where S : IDbConnection, new() 
                                           where T : IDbDataAdapter, IDisposable, new()
{
    using (var conn = new S())
    {
        using (var da = new T())
        {
            using (da.SelectCommand = conn.CreateCommand())
            {
                da.SelectCommand.CommandText = query;
                da.SelectCommand.Connection.ConnectionString = _connectionString;
                DataSet ds = new DataSet(); //conn is opened by dataadapter
                da.Fill(ds);
                return ds.Tables[0];
            }
        }
    }
}

我进行了一些性能测试,第二种方法总是比第一种更出色。

Stopwatch sw = Stopwatch.StartNew();
DataTable dt = null;
for (int i = 0; i < 100; i++)
{
    dt = Read1<MySqlConnection>(query); // ~9800ms
    dt = Read2<MySqlConnection, MySqlDataAdapter>(query); // ~2300ms

    dt = Read1<SQLiteConnection>(query); // ~4000ms
    dt = Read2<SQLiteConnection, SQLiteDataAdapter>(query); // ~2000ms

    dt = Read1<SqlCeConnection>(query); // ~5700ms
    dt = Read2<SqlCeConnection, SqlCeDataAdapter>(query); // ~5700ms

    dt = Read1<SqlConnection>(query); // ~850ms
    dt = Read2<SqlConnection, SqlDataAdapter>(query); // ~600ms

    dt = Read1<VistaDBConnection>(query); // ~3900ms
    dt = Read2<VistaDBConnection, VistaDBDataAdapter>(query); // ~3700ms
}
sw.Stop();
MessageBox.Show(sw.Elapsed.TotalMilliseconds.ToString());
Read1 看起来更加舒适,但数据适配器执行效率更高(不要混淆一个数据库优于另一个数据库,因为查询都是不同的)。两者之间的差异取决于查询。原因可能是 Load 方法需要检查每一行的各种约束条件(来自文档),而 Fill 方法位于 DataAdapter 上,它们专门用于快速创建 DataTable。请注意保留 HTML 标签。

4
要达到与“DataSet”相同的速度,您需要用“.BeginLoadData()”和“.EndLoadData()”包围“DataTable.Load()”。 - Nikola Bogdanović

1

集中式模型:您可以从任何地方使用它!

您只需要从您的函数调用以下格式到这个类

DataSet ds = new DataSet();
SqlParameter[] p = new SqlParameter[1];
string Query = "Describe Query Information/either sp, text or TableDirect";
DbConnectionHelper dbh = new DbConnectionHelper ();
ds = dbh. DBConnection("Here you use your Table Name", p , string Query, CommandType.StoredProcedure);

就是这样。它是完美的方法。

public class DbConnectionHelper {
   public DataSet DBConnection(string TableName, SqlParameter[] p, string Query, CommandType cmdText) {
    string connString = @ "your connection string here";
    //Object Declaration
    DataSet ds = new DataSet();
    SqlConnection con = new SqlConnection();
    SqlCommand cmd = new SqlCommand();
    SqlDataAdapter sda = new SqlDataAdapter();
    try {
     //Get Connection string and Make Connection
     con.ConnectionString = connString; //Get the Connection String
     if (con.State == ConnectionState.Closed) {
      con.Open(); //Connection Open
     }
     if (cmdText == CommandType.StoredProcedure) //Type : Stored Procedure
     {
      cmd.CommandType = CommandType.StoredProcedure;
      cmd.CommandText = Query;
      if (p.Length > 0) // If Any parameter is there means, we need to add.
      {
       for (int i = 0; i < p.Length; i++) {
        cmd.Parameters.Add(p[i]);
       }
      }
     }
     if (cmdText == CommandType.Text) // Type : Text
     {
      cmd.CommandType = CommandType.Text;
      cmd.CommandText = Query;
     }
     if (cmdText == CommandType.TableDirect) //Type: Table Direct
     {
      cmd.CommandType = CommandType.Text;
      cmd.CommandText = Query;
     }
     cmd.Connection = con; //Get Connection in Command
     sda.SelectCommand = cmd; // Select Command From Command to SqlDataAdaptor
     sda.Fill(ds, TableName); // Execute Query and Get Result into DataSet
     con.Close(); //Connection Close
    } catch (Exception ex) {

     throw ex; //Here you need to handle Exception
    }
    return ds;
   }
  }

0
如果您使用最新版本的C#(8版本之后),则代码变得更简单,因为using语句不需要花括号。
var table = new DataTable();
using var da = new SqlDataAdapter(sql, connectionString);
da.Fill(table);     

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接