简体   繁体   中英

Create Access Table from SQLDataReader

I am having trouble determining the best way to do this. We are maintaining a complex DB which the average business analyst will have a lot of trouble dealing with. Another developer created a couple of stored procedures as reports which we had been exporting for the users on demand into excel spreadsheets.

The client requested that these results be exported into an access database.

If this were 2 or 3 columns, I would just create a "template" accdb file and shove the results of the stored procedure in there. This query, however, brings back over 100 columns right now, and knowing the clients, it will be expanded in the future. So I am trying to create the table on the fly from the result set...whatever it is.

I /think/ the right way to do this is with a SqlDataReader but I'm stuck on how to get the proper "SQL"-ish field types . My current code looks like:

static void CreateAccessTableFromReader(string tableName, SqlDataReader reader) {
  List<string> columns = new List<string>();
  string createTable = @"CREATE TABLE {0} ({1})";

  int fieldCount = reader.FieldCount;
  for (int i = 0; i < fieldCount; i++) {
    columns.Add(String.Format("[{0}] {1}({2})",
      //column name (this seems to be right)
      reader.GetName(i), 
      //column type __(this is wrong.. I want 'VARCHAR, INT, etc')__
      reader.GetProviderSpecificFieldType(i),
      //column size (e.g. the numeric part of VARCHAR(15))
      reader.???
    ));
    //right now I'm getting results from the above line like:
    //[ColumnName] System.Data.SqlTypes.SqlString
    //and I'm looking for something like:
    //[ColumnName] VARCHAR(15)
  }
  using (var cmd = conn_Access.CreateCommand()) {
    cmd.CommandType = CommandType.Text;
    cmd.CommandText = String.Format(createTable, tableName, String.Join(", ", columns));
    cmd.ExecuteNonQuery();
  }
}

Worth noting:

EDIT :

This is my "final solution". The comments suggesting using a linked server certainly would have been an easier approach, but there was one thing preventing me from doing that which didn't occur to me at first, the data needs to exist in the Access DB as the users may consume the data away from where they would have network access to the underlying DB. As such, I kept banging away at this approach and here's what I came up with:

Note that I'd still be open to suggestions for a "better way", but this solves my current problem

static void CreateTableFromReader(string tableName, SqlDataReader reader) {
  List<string> columns = new List<string>();
  string createTable = @"CREATE TABLE {0} ({1})";

  var dt = reader.GetSchemaTable();
  foreach (DataRow dr in dt.Rows) {
    switch (dr["DataTypeName"].ToString().ToLower()) {
      case "varchar":
        columns.Add(String.Format("[{0}] {1}({2})",
          dr["ColumnName"],
          dr["DataTypeName"],
          dr["ColumnSize"]
        ));
        break;
      case "money": //i know this is redundant but being explicit helps clarity sometimes
      case "date":
      case "integer":
      default:
        columns.Add(String.Format("[{0}] {1}",
          dr["ColumnName"],
          dr["DataTypeName"]
        ));
        break;
    }
  }
  using (var cmd = conn_Access.CreateCommand()) {
    cmd.CommandType = CommandType.Text;
    cmd.CommandText = String.Format(createTable, tableName, String.Join(", ", columns));
    cmd.ExecuteNonQuery();
  }
}

This is my "final solution". The comments suggesting using a linked server certainly would have been an easier approach, but there was one thing preventing me from doing that which didn't occur to me at first, the data needs to exist in the Access DB as the users may consume the data away from where they would have network access to the underlying DB. As such, I kept banging away at this approach and here's what I came up with:

Note that I'd still be open to suggestions for a "better way", but this solves my current problem

static void CreateTableFromReader(string tableName, SqlDataReader reader) {
  List<string> columns = new List<string>();
  string createTable = @"CREATE TABLE {0} ({1})";

  var dt = reader.GetSchemaTable();
  foreach (DataRow dr in dt.Rows) {
    switch (dr["DataTypeName"].ToString().ToLower()) {
      case "varchar":
        columns.Add(String.Format("[{0}] {1}({2})",
          dr["ColumnName"],
          dr["DataTypeName"],
          dr["ColumnSize"]
        ));
        break;
      case "money": //i know this is redundant but being explicit helps clarity sometimes
      case "date":
      case "integer":
      default:
        columns.Add(String.Format("[{0}] {1}",
          dr["ColumnName"],
          dr["DataTypeName"]
        ));
        break;
    }
  }
  using (var cmd = conn_Access.CreateCommand()) {
    cmd.CommandType = CommandType.Text;
    cmd.CommandText = String.Format(createTable, tableName, String.Join(", ", columns));
    cmd.ExecuteNonQuery();
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM