Copying tables among databases - c#

Let's say I want to copy all tables with their complete data from one database to another without specifically knowing detailed information about them(column count, data types...). The user would input a connection string to his database, and all data from it would be copied to an internal DB.
I tried to achieve it by using SqlConnection and writing direct T-SQL queries and managed to write a script that creates empty tables in Internal database with correct columns:
string createDestinationTableQuery = "create table " + schemaName + ".[" + tableName + "](";
DataTable ColumnsDT = new DataTable();
string getTableColumnDataQuery = "SELECT * FROM "+originalDBName+".INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = N'" + tableName +"'";
SqlCommand getTableColumnDataCommand = new SqlCommand(getTableColumnDataQuery, originalCon);
SqlDataAdapter TableDA = new SqlDataAdapter(getTableColumnDataCommand);
TableDA.Fill(ColumnsDT);
for (int x = 0; x < ColumnsDT.Rows.Count; x++)
{
createDestinationTableQuery += "[" + ColumnsDT.Rows[x].ItemArray[3].ToString() + "] " + "[" + ColumnsDT.Rows[x].ItemArray[7].ToString() + "], ";
}
createDestinationTableQuery = createDestinationTableQuery.Remove(createDestinationTableQuery.Length - 2);
createDestinationTableQuery += " )";
SqlCommand createDestinationTableCommand = new SqlCommand(createDestinationTableQuery, destinationCon);
createDestinationTableCommand.ExecuteNonQuery();
Console.WriteLine("Table " + schemaName + "." + tableName + " created succesfully!");
However, I am struggling with data insertion as the following code simply doesn't work:
DataTable dataTable = new DataTable();
string getTableDataquery = "select * from " + originalTableWithSchema;
SqlCommand getTableDataCommand = new SqlCommand(getTableDataquery, originalCon);
SqlDataAdapter da = new SqlDataAdapter(getTableDataCommand);
da.Fill(dataTable);
for (int x = 0; x < dataTable.Rows.Count; x++)
{
string insertQuery = "insert into " + schemaName + ".["+tableName+"](" ;
string values = "VALUES(";
for (int y = 0; y < dataTable.Columns.Count; y++)
{
insertQuery += dataTable.Columns[y].ColumnName + ", ";
values += dataTable.Rows[x].ItemArray[y].ToString() + ", ";
}
insertQuery = insertQuery.Remove(insertQuery.Length - 2);
insertQuery += " )";
values = values.Remove(values.Length - 2);
values += " )";
insertQuery += " " + values;
SqlCommand insertCommand = new SqlCommand(insertQuery, destinationCon);
insertCommand.ExecuteNonQuery();
}
da.Dispose();
How can I correctly Achieve this functionality? I was thinking of maybe scrapping all the code and using SMO instead?

If you are only looking to copy the data (because you have structure creation already working), then you could use DataTable to hold the data in a non-dbms specific structure, and a DataAdapter to generate the dbms specific insert statements. Here is an excerpt from code I wrote a while ago to copy data from Access to MySQL:
List<string> tableNames = new List<string>();
try
{
// Open connect to access db
sourceConn.Open();
// Build table names list from schema
foreach (DataRow row in sourceConn.GetSchema("Tables").Select("table_type = 'TABLE'"))
tableNames.Add(row["table_name"].ToString());
}
catch (Exception ex)
{
throw ex;
}
finally
{
if(sourceConn.State != ConnectionState.Closed)
sourceConn.Close();
}
foreach (string table in tableNames)
{
//Get all table data from Access
string query = string.Format("SELECT * FROM {0}", table);
DataTable accessTable = new DataTable(table);
try
{
sourceConn.Open();
System.Data.OleDb.OleDbCommand accessSqlCommand = new System.Data.OleDb.OleDbCommand(query, accessConn);
System.Data.OleDb.OleDbDataReader reader = (System.Data.OleDb.OleDbDataReader)accessSqlCommand.ExecuteReader();
// Load all table data into accessTable
accessTable.Load(reader);
}
catch(Exception ex)
{
throw ex;
}
finally
{
if(sourceConn.State != ConnectionState.Closed)
sourceConn.Close();
}
// Import data into MySQL
accessTable.AcceptChanges();
// The table should be empty, so set everything as new rows (will be inserted)
foreach (DataRow row in accessTable.Rows)
row.SetAdded();
try
{
destConn.Open();
MySql.Data.MySqlClient.MySqlDataAdapter da = new MySql.Data.MySqlClient.MySqlDataAdapter(query, mySqlConn);
MySql.Data.MySqlClient.MySqlCommandBuilder cb = new MySql.Data.MySqlClient.MySqlCommandBuilder(da);
da.InsertCommand = cb.GetInsertCommand();
// Update the destination table 128 rows at a time
da.UpdateBatchSize = 128;
// Perform inserts (and capture row counts for output)
int insertCount = da.Update(accessTable);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if(destConn.State != ConnectionState.Closed)
destConn.Close();
}
}
This could certainly be more efficient, but I wrote it for a quick conversion. Also, since this is copied and pasted you may need to tweak it. Hope it helps.

It might be worth thinking about using a linked server. Once a linked server is defined in the destination server, a table can be created and automatically filled with data using a SELECT…INTO statement.
Query executed in destination server database:
SELECT * INTO NewTableName FROM
SourceServername.SourceDatabasename.dbo.SourceTableName

Related

Data gets duplicated in datagridview when query applied?

I am trying to perform CRUD operation on winform
This is for ASP.NET winform in which whenever I try to insert, update or delete the data to or from the database first of all rows and inside content gets duplicated
https://imgur.com/a/d5jgv6H
however, upon restarting the application data shows up correctly
private void button2_Click(object sender, EventArgs e)
{
//insert
try
{
con.Open();
//.text property gets/Sets text associated with this control
String name = textBox1.Text.ToString();
String address = textBox2.Text.ToString();
String number = textBox3.Text.ToString();
long pnumber = Int64.Parse(number);
String sem = textBox4.Text.ToString();
long semester = Int64.Parse(sem);
string branch = comboBox1.SelectedItem.ToString();
String query = "insert into student values('" + name + "','" + address + "'," + pnumber + "," + semester + ",'" + branch + "')";
SqlCommand sqlcom = new SqlCommand(query, con);
int i = sqlcom.ExecuteNonQuery();
if (i >= 1)
{
MessageBox.Show("Student has been Registered: " + name);
}
else
{
MessageBox.Show("Registration Failed ! ");
}
//clearing data
button1_Click(sender, e);
show();
con.Close();
}
catch (Exception exp)
{
MessageBox.Show("Error is : " + exp.ToString());
}
}
void show()
{
String query = "select * from student";
SqlDataAdapter adapter = new SqlDataAdapter(query, con);
DataTable dataInTable = new DataTable();
adapter.Fill(dataInTable);
//DataRow represents row of data in DataTable
foreach (DataRow item in dataInTable.Rows)
{
int n = dataGridView1.Rows.Add();
dataGridView1.Rows[n].Cells[0].Value = item[0].ToString();
dataGridView1.Rows[n].Cells[1].Value = item[1].ToString();
dataGridView1.Rows[n].Cells[2].Value = item[2].ToString();
dataGridView1.Rows[n].Cells[3].Value = item[3].ToString();
dataGridView1.Rows[n].Cells[4].Value = item[4].ToString();
}
}
The query works fine but rows still get duplicated. What's the problem?
I can't find any bug, so I assume I am doing something incorrectly.
Your show method adds rows to the grid, but it never removes the rows which are already in the grid.
Normally one would use data binding to populate a DataGridView or other data-bound controls. I'm going to assume you have reasons for just adding rows directly (perhaps it's simpler for your needs) and not change that. Given that structure, probably the easiest thing to do is just to clear the grid before adding the new rows:
void show()
{
String query = "select * from student";
SqlDataAdapter adapter = new SqlDataAdapter(query, con);
DataTable dataInTable = new DataTable();
adapter.Fill(dataInTable);
dataGridView1.Rows.Clear(); // <--- here
foreach (DataRow item in dataInTable.Rows)
{
int n = dataGridView1.Rows.Add();
dataGridView1.Rows[n].Cells[0].Value = item[0].ToString();
dataGridView1.Rows[n].Cells[1].Value = item[1].ToString();
dataGridView1.Rows[n].Cells[2].Value = item[2].ToString();
dataGridView1.Rows[n].Cells[3].Value = item[3].ToString();
dataGridView1.Rows[n].Cells[4].Value = item[4].ToString();
}
}
That way any time you are about to populate the grid with new data, you first clear the existing data from it.

How to copy data from datatable to a database table?

I am working on an application, wherein I want to create a table with data from a datatable.
Scenario is:
User selects a table and data is filled in a DataTable
Based on the DataTable schema I am creating a table in a temporary database (this database can be SQL Server, Oracle (user-defined))
Once the table is created, I want to copy data from DataTable to temp table
My code:
using (OleDbConnection con = new OleDbConnection(localConnString))
{
try
{
con.Open();
List<string> col = new List<string>();
StringBuilder sb = new StringBuilder();
StringBuilder insertQuery = new StringBuilder();
insertQuery.Append("Insert INTO " + tableName + " Values(");
foreach (DataRow row in dtSchema.Rows)
{
string type = getColumnType(row.Field<object>("DataType").ToString());
string column ="["+ row.Field<string>("ColumnName").ToString() + "] " + type;
col.Add(column);
}
sb.Append(string.Join(",", col));
string createTableQuery = "CREATE TABLE ["+tableName+"] (" + sb.ToString().Trim() + ")";
OleDbCommand command = new OleDbCommand(createTableQuery, con);
command.ExecuteNonQuery();
con.Close();
// Code to copy data from DataTable to temp table
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
I want to use something like SqlBulkCopy, but I am using OLEDB.
Any help would be appreciable.

using one button to insert and update different db tables

I am using the same button "Save" to update a table called AnalysisExperiments and also to insert data into a table called "Analysis". however the update is not working. Here is the code:
#region Insert Data into Analysis Table
if (checkIfRepeatedJobNumber(tbJobNumber.Text.Trim()))
{
MessageBox.Show("Job Number is already exist.", "Repeated Data");
return;
}
string query = "insert into Analysis (ID, WellName, EstimatedStartDate, SOWComments, ProgressComments, Field, FocalPoint, StudyCompleted, Company, ReservoirPressure, ReservoirTemp, SelectedSamples) " +
"values (#ID, #WellName, #SamplingDate, #SOWComments, #ProgressComments, #Field, #FocalPoint, #StudyCompleted, #Company, #ReservoirPressure, #ReservoirTemp, #SelectedSamples)";
OleDbCommand cmd = new OleDbCommand(query, conn);
cmd.Parameters.AddWithValue("#ID", tbJobNumber.Text);
cmd.Parameters.AddWithValue("#WellName", tbWellName.Text);
cmd.Parameters.AddWithValue("#SamplingDate", dtpSamplingDate.Text);
cmd.Parameters.AddWithValue("#SOWComments", tbSOW_Comments.Text);
cmd.Parameters.AddWithValue("#ProgressComments", tbProgressComments.Text);
cmd.Parameters.AddWithValue("#Field", tbFieldName.Text);
cmd.Parameters.AddWithValue("#FocalPoint", tbFocalName.Text);
if (radYes.Checked)
cmd.Parameters.AddWithValue("#StudyCompleted", "Yes");
else
cmd.Parameters.AddWithValue("#StudyCompleted", "No");
cmd.Parameters.AddWithValue("#Company", tbCompany.Text);
cmd.Parameters.AddWithValue("#ReservoirPressure", tbReservoirPressure.Text);
cmd.Parameters.AddWithValue("#ReservoirTemp", tbReservoirTemp.Text);
cmd.Parameters.AddWithValue("##SelectedSamples", tbSelectedSamples.Text);
try
{
conn.Open();
int j = cmd.ExecuteNonQuery();
if (j == 1)
{
MessageBox.Show("Done");
this.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
conn.Close();
}
#endregion
#region update analysis Exp but still no working
#region Update database
try
{ int k = 0;
OleDbDataAdapter da;
da = new OleDbDataAdapter("select* from [AnalysisExperiments]", conn);
string ExpQuery = "update AnalysisExperiments set SampleNumber = #SampleNumber, Status = #Status where ID = '" + tbJobNumber.Text + "' and Experiment = '";
foreach (DataGridViewRow row in dgvExperiments.Rows)
{
ExpQuery += row.Cells["Experiment"].Value.ToString() + "'";
OleDbCommand updateCommand = new OleDbCommand(ExpQuery, conn);
updateCommand.Parameters.Add("#SampleNumber", OleDbType.VarWChar);
MessageBox.Show(row.Cells["SampleNumber"].Value.ToString() + " | " + row.Cells["Status"].Value.ToString() + " | " + row.Cells["Experiment"].Value.ToString());
updateCommand.Parameters["#SampleNumber"].Value = row.Cells["SampleNumber"].Value.ToString();
updateCommand.Parameters.Add("#Status", OleDbType.Boolean);
updateCommand.Parameters["#Status"].Value = row.Cells["Status"].Value;
da.UpdateCommand = updateCommand;
conn.Open();
k = da.UpdateCommand.ExecuteNonQuery();
conn.Close();
}
if (k == 1)
MessageBox.Show("Done");
else
{
MessageBox.Show("Nothing Updated!");
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
#endregion
#endregion
knowing that the compiler skips the loop in the update region.
At the second loop the query syntax becomes invalid cause the += that concatenates the previous text with the new one
Dim newQuery = ExQuery + row.Cells["Experiment"].Value.ToString() + "'";
And then use the new string for the ExecuteNonQuery.
Still this approach has many problems. You should separate this code in different methods and use parameters also for the last value
Have you tried to debug the foreach line to see if there are any rows in your dgvExperiments and if they are of type (or subtype of) DataGridViewRow ?

C# SQL Insert with a very large number of parameters

I have had a problem for a few days and nothing online seems to do it.
I have an SQL table that has 150 columns. I am reading data from an ODBC connection and I want to insert that data into the SQL table. Basically duplicate the ODBC table as SQL.
My problem is that if I put everything in a string and insert it I face a hell of a time with escape characters and exceptions that I can't figure out.
Is there a way to parametrize my insert values that doesn't involve me naming each and every on of them separatly.
This is what I have right now. Also, if anyone knows an easier way to move an ODBC table to an SQL form please let me know
private void fillTable(string tableName)
{
String query = "SELECT * FROM " + tableName;
OdbcCommand command = new OdbcCommand(query, Program.myConnection);
OdbcDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess);
int columnCount = reader.FieldCount;
for (int i = 0; i < columnCount; i++)
{
SqlCommand sCommand = new SqlCommand("ALTER TABLE " + tableName + " ADD " + reader.GetName(i) + " varchar(MAX)", Program.myConnection2);
sCommand.ExecuteNonQuery();
}
string row="";
while (!reader.IsClosed)
{
try
{
row = "";
reader.Read();
for (int i = 0; i < columnCount; i++)
{
if (reader != null)
{
if (reader.GetString(i).Contains('\''))
{
Console.WriteLine("REPLACED QUOT");
String s = reader.GetString(i).Replace("\'", "A");
Console.WriteLine(s);
row += s;
}
else
{
row += "\'" + reader.GetString(i).Trim() + "\',";
}
// Console.WriteLine("FILLER: " + reader.GetString(i));
}
//Console.WriteLine(row);
}
//Console.WriteLine();
row = row.Substring(0, row.Length - 1);
SqlCommand insertCommand = new SqlCommand("INSERT INTO " + tableName + " VALUES(\'1\'," + row + ")", Program.myConnection2);
insertCommand.ExecuteNonQuery();
}
catch (SqlException exp)
{
Console.WriteLine(exp.StackTrace);
Console.WriteLine(row);
// this.Close();
}
}
Program.myConnection2.Close();
}
You can write a method that creats parameter names automatically, adds it to command, and returns the name so that you can use it in the query:
private int _paramCounter = 1;
private string CreateParameter(SqlCommand command, object value) {
string name = "#P" + _paramCounter.ToString();
_paramCounter++;
command.Parameters.AddWithValue(name, value);
return name;
}
Usage:
row += CreateParameter(insertCommand, reader.GetString(i).Trim()) + ",";
Note that you need to create the command object before you loop through the columns. Also, although not needed, you might want to reset the _paramCounter for each row, otherwise the parameter names get longer in the end.

How to retrieve all fields for a given record using OracleDataReader?

My question, which is similar to this one, is how can I use OracleDataReader to retrieve all the fields for a given record? Currently, I've been using this method, which returns only one column value at a time:
public string Select_File(string filename, string subdirectory, string envID)
{
Data_Access da = new Data_Access();
OracleConnection conn = da.openDB();
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "SELECT * FROM EIP_Deployment_Files"
+ " WHERE Filename ='" + filename + "'"
+ " AND Subdirectory = '" + subdirectory + "'"
+ " AND Environment_ID = '" + envID + "'";
cmd.CommandType = CommandType.Text;
string x;
OracleDataReader dr = cmd.ExecuteReader();
if (dr.HasRows) // file exists in DB
{
dr.Read();
x = dr.GetString(2).ToString(); // return baseline filename (index 2)
}
else
{
x = "New File";
}
cmd.Dispose();
da.CloseDB(conn);
return x;
}
I'm sure that this method is far from perfect and ppl will be quick to point that out (I was basically given it by my supervisor since I didn't have any prior experience in ASP.NET) but all I really care about is that it works. My question is: how can it be modified to return all the fields for a given record?
The fields will be of either VARCHAR2, CHAR, or DATE datatypes, (if that makes a difference) and some of these values may be null. I'm thinking I could convert them to strings and return them as a list?
if u want something like this:
List<User> lstUser = new List<User>();
string sqlQuery = "Select * from User_T where User_Name='" + oUser.UserName + "' And Password='" +oUser.Password + "' AND IsActive='"+1+"' AND IsDelete='"+0+"'";
string connectionString = "Data Source=ORCL;User Id=ACCOUNTS;Password=ACCOUNTS";
using (DBManager dbManager = new DBManager(connectionString))
{
try
{
dbManager.Open();
OracleDataReader dataReader = dbManager.ExecuteDataReader(sqlQuery);
while (dataReader.Read())
{
oUser = new User();
oUser.Id = Convert.ToInt32(dataReader["ID"]);
oUser.CompanyId = Convert.ToInt32(dataReader["Company_ID"]);
oUser.BranchId = Convert.ToInt32(dataReader["Branch_ID"]);
oUser.UserName = Convert.ToString(dataReader["User_Name"]);
lstUser.Add(oUser);
}
dataReader.Close();
dataReader.Dispose();
}
catch
(Exception)
{
}
finally
{
dbManager.Close();
dbManager.Dispose();
}
To read all the data from the columns of the current row in a DataReader, you can simply use GetValues(), and extract the values from the array - they will be Objects, of database types.
Object[] values;
int numColumns = dr.GetValues(values); //after "reading" a row
for (int i = 0; i < numColumns; i++) {
//read values[i]
}
MSDN - "For most applications, the GetValues method provides an efficient means for retrieving all columns, rather than retrieving each column individually."
Sorry for posting an answer to a very old question. As none of the answers are correct (either they have security issues or not checking for DBNull), I have decided to post my own.
public async Task<StringBuilder> FetchFileDetailsAsync(string filename, string subdirectory, string envId)
{
var sb = new StringBuilder();
//TODO: Check the parameters
const string connectionString = "user id=userid;password=secret;data source=" +
"(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=10.0.0.8)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=xe)))";
const string selectQuery = "SELECT * FROM EIP_Deployment_Files"
+ " WHERE Filename = :filename"
+ " AND Subdirectory = :subdirectory"
+ " AND Environment_ID = :envID"
+ " AND rownum<=1";
using (var connection = new OracleConnection(connectionString))
using (var cmd = new OracleCommand(selectQuery, connection) {BindByName = true, FetchSize = 1 /*As we are expecting only one record*/})
{
cmd.Parameters.Add(":filename", OracleDbType.Varchar2).Value = filename;
cmd.Parameters.Add(":subdirectory", OracleDbType.Varchar2).Value = subdirectory;
cmd.Parameters.Add(":envID", OracleDbType.Varchar2).Value = envId;
//TODO: Add Exception Handling
await connection.OpenAsync();
var dataReader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection);
var rowValues = new object[dataReader.FieldCount];
if (dataReader.Read())
{
dataReader.GetValues(rowValues);
for (var keyValueCounter = 0; keyValueCounter < rowValues.Length; keyValueCounter++)
{
sb.AppendFormat("{0}:{1}", dataReader.GetName(keyValueCounter),
rowValues[keyValueCounter] is DBNull ? string.Empty : rowValues[keyValueCounter])
.AppendLine();
}
}
else
{
//No records found, do something here
}
dataReader.Close();
dataReader.Dispose();
}
return sb;
}

Categories

Resources