|
|||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Table of ContentsIntroductionThe other day I needed to add a tremendous amount of indexes spanning over 50 tables to a SQL Server database. All I had was a list of column names that needed to be indexed in any table that the column appears in. This is an unusual situation for me to be in. I felt overwhelmed when thinking of manually opening each table to see if it has the columns in question then adding an index to each one. This would have taken me several hours. Then I remembered I have an algorithm I use to replicate and/or move data between two or more databases. I figured I could apply this algorithm to my problem. It took me 15 minutes to apply and test it. I felt this was a good use of my time since it saved me hours of manual work. After I was done, I thought this could be a simple example of how to use the algorithm, and to demonstrate a method for using ADO.NET in a connected way. The only assumption made is that you are knowledgeable in C# programming and are familiar with the .NET Framework. The article does not assume that you know ADO.NET. The areas that can be skipped by those familiar with ADO.NET are noted. However, even though I try to explain the ADO.NET portions, I do so only to the extent used by this application. This is not an ADO.NET tutorial, there are plenty of good ones on this site, and this article does not attempt to be one. DisclaimerThis example application is just that, it is an example of one way to do something. It is not meant as the only way or even the best way. I am sure there are other ways to solve this problem, all of which could be better than the way shown in this article. The article is meant to be used as a tool to teach someone how-to do something, and is not meant to be an end-all solution. If you have a better, more efficient way to solve this problem, please share it with the world by writing and posting an article on Code Project. Then, please create a new thread in this article to point us to your newly posted article here on Code Project. Code Project is a great place to share ideas on multiple ways to implement something or to solve a problem. However, I do not welcome threads in my articles that only serve to flame someone, start an argument, or boast their holier-than-thou ideas. The DetailsFirst, let's start with a very simple break-down of the algorithm:
You will see the different parts as we progress through the code, specifically the All the code is contained in a single class called Libraries UsedThis library is needed because the application uses using System.Text;
These libraries are the two ADO.NET libraries. using System.Data;
using System.Data.SqlClient;
Private MembersThis section is documented and is self-explanatory. All the members are // Connection used to OUTPUT from SQL Server
private SqlConnection oSqlConnOUT = null;
// Connection used to INPUT to SQL Server
private SqlConnection oSqlConnIN = null;
private SqlTransaction oSqlTransaction = null;
// SQL Server Connection Details
private String sSqlServer = "localhost";
private String sSqlDatabase = "DataBaseName";
private String sSqlUser = "sa";
private String sSqlPassword = "password";
private String[] sColumnsToIndex = new String[]
{
"userid", "date", "split", "logid", "groupid",
"anid", "time", "jacksquatid", "ohyeah", "type",
"keyno", "item", "itemid", "value", "whatid"
};
// Set the Prefix for the Index
// If done manually SQL Server gives a default of IX_
// Therefore I do this as well, change to match your naming conventions.
private readonly String sIndexPrefix = "IX_";
Main Methodstatic void Main(string[] args)
{
new SqlIndexer().Start();
// Used when running inside of VS comment out if run from command line
Console.WriteLine("\n\n...Press [ENTER] To Close Window...");
Console.ReadLine();
}
This is very simple, it instantiates the ConstructorThis section is documented and should be self-explanatory if you have experience with ADO.NET, if so, feel free to skip the next paragraph. /// <summary>
/// When SqlIndexer is created it creates the needed connections.
/// </summary>
private SqlIndexer()
{
oSqlConnOUT = new SqlConnection("Database=" + sSqlDatabase +
";Server=" + sSqlServer +
";User ID=" + sSqlUser +
";Password=" + sSqlPassword + ";");
oSqlConnIN = new SqlConnection("Database=" + sSqlDatabase +
";Server=" + sSqlServer +
";User ID=" + sSqlUser +
";Password=" + sSqlPassword + ";");
}
Here is where the class instantiates the Start MethodThis section is documented and should be self-explanatory if you have experience with ADO.NET. If so, feel free to skip the remaining paragraphs in this section, jumping right to the Index Method section. /// <summary>
/// Starts the whole process of opening connections to the database,
/// and beginning a transaction. Also handles if the transaction is
/// committed or rolled back. Then finally it makes sure that all
/// the open connections get closed.
///
/// It hands off the SQL communication details to the Index method.
/// </summary>
private void Start()
{
try
{
// Lets get those connections open before we use them
oSqlConnIN.Open();
oSqlConnOUT.Open();
// Start a transaction
oSqlTransaction = oSqlConnIN.BeginTransaction();
Index();
// If all is well then commit
oSqlTransaction.Commit();
}
catch (Exception e)
{
// Show the message and the stack trace
Console.WriteLine(e.Message + "\n");
Console.WriteLine(e.StackTrace);
// Rollback if there are any problems
if (oSqlTransaction != null)
oSqlTransaction.Rollback();
}
finally
{
// Always make sure all connections get closed!
if (oSqlConnIN != null)
oSqlConnIN.Close();
if (oSqlConnOUT != null)
oSqlConnOUT.Close();
}
}
Here is where the class actually opens the two connections to the database. After that, it begins a transaction. A Transaction is a process that is used by a database to maintain its integrity. To do this, all SQL operations are batched into a transaction, and then you can decide to execute all of them completely or none of them at all. With ADO.NET, you call After we have begun a transaction, we call the Next, a Index MethodThis section is documented and should be self-explanatory if you have experience with ADO.NET. If so, feel free to skip the remaining paragraphs in this section, jumping right to the Conclusion section. /// <summary>
/// Loops through each column name in the string array sColumnsToIndex.
/// Then finds all of the tables with that contain
/// a column the current column name.
/// Then loops through all the tables creating an index for current column.
/// </summary>
private void Index()
{
for (int i = 0; i < sColumnsToIndex.Length; i++)
{
// Keep the user informed of what is going on
Console.WriteLine("\n\nCurrent Column: " + sColumnsToIndex[i]);
StringBuilder oSelectTableNamesQuery = new StringBuilder();
// The select statement to find all tables that contain a column with the
// current column name. Need to NOLOCK the sysobjects table since the
// SqlReader will be reading this table while the create index process is
// going this is one of the tables it updates. Since we are not getting a
// column that is being updated it "should" be safe.
//
// WARNING: This will only work on SQL Server 7.0 or higher
// These system tables exist in SQL Server DBMS
// Each DBMS has its own way of handling this information
oSelectTableNamesQuery.Append("SELECT DISTINCT so.name 'table'");
oSelectTableNamesQuery.Append("FROM sysobjects so (NOLOCK)");
oSelectTableNamesQuery.Append("INNER JOIN syscolumns sc on so.id = sc.id ");
oSelectTableNamesQuery.Append("WHERE so.xtype ='u' ");
oSelectTableNamesQuery.Append("AND sc.name like '");
oSelectTableNamesQuery.Append(sColumnsToIndex[i]);
oSelectTableNamesQuery.Append("'");
A SqlCommand oGetTablesCommand =
new SqlCommand(oSelectTableNamesQuery.ToString(), oSqlConnOUT);
SqlDataReader oSqlReader = null;
try
{
oSqlReader = oGetTablesCommand.ExecuteReader();
}
catch (SqlException e)
{
// Display the query then re-throw the exception.
Console.WriteLine("\n\n" + oSelectTableNamesQuery.ToString() + "\n\n");
throw e;
}
Here, it has created a object oCurrentTable = null;
// Keep the user informed of what is going on
Console.Write("Tables:");
while (oSqlReader.Read())
{
StringBuilder oCreateIndexNonQuery = new StringBuilder();
// Get the next table name from the reader
oCurrentTable = oSqlReader.GetValue(0);
// Creating the CREATE INDEX query for each
// current column in the current table.
oCreateIndexNonQuery.Append("CREATE INDEX ");
oCreateIndexNonQuery.Append(sIndexPrefix);
oCreateIndexNonQuery.Append(sColumnsToIndex[i]);
oCreateIndexNonQuery.Append(" ON ");
oCreateIndexNonQuery.Append(oCurrentTable);
oCreateIndexNonQuery.Append(" (");
oCreateIndexNonQuery.Append(sColumnsToIndex[i]);
oCreateIndexNonQuery.Append(")");
Another SqlCommand oCreateIndexCommand =
new SqlCommand(oCreateIndexNonQuery.ToString(), oSqlConnIN);
// Link this command to the transaction.
oCreateIndexCommand.Transaction = oSqlTransaction;
try
{
oCreateIndexCommand.ExecuteNonQuery();
}
catch (SqlException e)
{
// Display the query then re-throw the exception.
Console.WriteLine("\n\n" + oCreateIndexNonQuery.ToString() + "\n\n");
throw e;
}
Here, it has created another // Keep the user informed of what is going on
Console.Write(" " + oCurrentTable);
}
// Need to close Reader before next loop
oSqlReader.Close();
}
// Make sure we clean up after the last Write method.
Console.WriteLine("");
}
Finally, it closes the Other Applications OfThe algorithm implemented in the You would still use two connections, just that they would be to different databases, maybe even different servers, or even mixing ODBC with SQL, or OLEDB. To make the algorithm more reusable, you would want to pass in arguments into the method that you would need. Like a collection of tables to replicate, and any data transformation information you would need. Then you would need to adjust the commands you build to be more or less dynamic based on the information you pass in. Regardless, the basic principles would still remain the same. ConclusionI hope you find this article useful and maybe even learned something. Feel free to start a thread if you have questions about something in this article. I apologize in advance for any typos in this article.
|
||||||||||||||||||||||||||||||||||||||||||||