Wednesday, 1 February 2012

Performance in ASP.NET and ADO.NET


DataReaders, DataSets, Connection Pooling, Cache and SP’s
·        Connection Pooling in ADO.NET;
·        Reading DB data using DataReaders;
·        Using memory DataSets;
·        Performance with Stored Procedures.
·        C#, Visual Studio 2003, ADO.NET, ASP.NET.


In this article I will highlight some interesting tips to optimize ASP.NET applications. We’ll get acquainted with the powerful resources of data cache, practical use of stored procedures, connection pooling and other advanced techniques. You’ll learn how to use DataSets in memory to avoid unnecessary queries to the SQL server and, therefore, optimize data traffic. You’ll also be introduced to the interesting ADO.NET connection pooling resource.
To build the examples, I’ll use Visual Studio .NET 2003 and the SQL Server 2000 as database. The applications will be made using C#, but they can easily be written in VB.NET, in case you want to use that language.

Connection Pooling
In Visual Studio, click File>New>Project (or press Shift+Ctrl+N) and in the New Project window choose ASP.NET Web Application in the item Visual C# Projects. In the Location option, name the application and then click OK.
Starting from ToolBox, places a SqlConnection in the Web Form. Select the component and in the Properties window select New Connection in the property ConnectionString. In the editor that appears inform server IP address or server name in the first entry box. In User name and Password inform standard user and password for database access. And, finally, choose the Northwind database and click Ok. If you want, click the Test Connection button to check if the parameters are correct. By doing this, we setup the SQL Server connection using SQL provider, the first performance tip (never use OleDB or ODBC in this case).
Notice that in User name and Password we inform a standard database access user and password, but could have used integrated authentication. However, here comes the second valuable tip for optimization: supply a fixed user and password, in such a way that all users that connect to the application use the same credentials. If it’s necessary to restrain a certain user’s access, define that in Web.Config authorizations. Supplying a fixed user will make ADO.NET use the Connection Pooling resource effectively, without having performance loss.
Connection Pooling is the mechanism that allows ADO.NET to reuse database connections. Picture the following situation: a user access the application, we connect to the database to extract information and show it in the form. After that, we end the connection and feedback the result to the browser. Since Web applications are state-less, if this exact same user or another connects to the application, a new connection with database will need to be reestablished. Connecting to the database at each user’s request is literally a suicide in Web environment, where an application can have hundreds and even thousands of simultaneous connections.
The ADO.NET solves this in a sufficiently elegant manner: after the page is sent to the browser, the connection with database isn’t released, even if you’ve explicitly called SqlConnection close method. The ADO.NET automatically saves the connection in pool (picture this as a kind of connections cache). That is, the connection stays open with the database and persists between requests. When another user connects to the application, the ADO.NET checks if there exists an available connection in pool and in case it finds one, uses it. With this, all the time needed to locate the database server, establish a connection, authenticate a user and check permissions will no longer be taken at each request.
And the best part of it all, you don’t need to do anything to use this resource, since it’s already activated by default. Creating a Connection Pooling mechanism manually through code is something extremely complicated (unfortunately, I’ve had to got through that effort in a given point in time). In ADO.NET, we’ve already got that ready in the framework itself. Productivity is one of the strong points of .NET.

Note: Connection Pooling can only be used in a multi-thread environment (a Web application, for example), where we have several simultaneous threads processing client requests. It makes no sense, for example, use Connection Pooling in a traditional Windows Forms application (two layers). Internally, Connection Pooling uses an interesting resource of the Operational System to provide its functionality: semaphores

You can even control how ADO.NET works with Connection Pooling, making some adjustments in SqlConnection’s ConnectionString property. We can specify some parameters, see the main ones in Table 1.


Maximum number of connections that can stay in pool.
Minimum number of connections that can stay in pool.
Indicates if Connection Pooling is registered.
Table 1. Connection Pooling Parameters
Observe an example of how we could use some of these parameters in our application’s connection string (you can make this change straight in the ConnectionString property, starting from the Properties window):

workstation id=ASUS;packet size=4096;user id=sa;data source=ASUS;persist security info=False;initial catalog=Northwind;Pooling=True;Min Pool Size=50;Connection Lifetime=120;

Attention: always use the same ConnectionString (with the same values for all parameters) in all connection objects, so that they share the same Connection Pooling.
Using a DataReader to display data in a DataGrid
Following up on our example, first we’ll see how to display data from a given SQL Server table in a DataGrid control, using our previously configured connection.
The fastest way to extract data from a database is using a DataReader (a SqlDataReader, in case of the SQL Server provider). A DataReader is responsible for the reading of the data returned for a SQL query, using a data cursor. It is fast for the following reasons:
·        It is unidirectional: navigation through records is made in a sequential fashion (forward-only). Basically, we read a record to do something with it (display data in a control, for instance) and navigate to the next record;
·        It is read-only: it isn’t possible to modify a DataReader;
·        It doesn’t make cache: after a record is read, it is discarded from the memory.
The code in Listing 1 shows how to use a DataReader (place the code in the Web Form’s Page Load). Here we establish the connection to the database using SqlConnection’s Open method. After that, we use a SqlCommand to execute a Select command in the Products table. To run the query, we call upon the ExecuteReader method of SqlCommand (here called cmd), which return value is attributed to DataGrid’s DataSource property.

using System.Data.SqlClient;
...
private void Page_Load(object sender, System.EventArgs e)
{
    sqlConnection1.Open();
    try
    {
        SqlCommand cmd = new SqlCommand("select * from Products",sqlConnection1);
       DataGrid1.DataSource = cmd.ExecuteReader();
       DataGrid1.DataBind();                  
    }
    finally
    {
       sqlConnection1.Close();
    }
}

Listing 1. Using the DataReader
But where it is the object SqlDataReader? A DataReader is never instanced directly. You will always get the reference to an object of this type through the calling upon a SqlCommand’s ExecuteReader method. You can then use the return object to sweep the obtained resultset, using its Read method. Here it wasn’t necessary to do this sweeping manually, since we used the DataBind resource of the ASP.NET for data connection with DataGrid, through the DataSource property.
Notice that all the execution code is wrapped in a try...finally block. The code inside the finally block will always be executed, unconditionally, even if an exception occurs (for example, if the command SQL typed wasn’t valid). With this we make sure that SqlConnection’s Close is always called after the execution of the code inside try, returning the connection to the pool. Another tip is to always keep the minimum code possible between Open and Close, only what makes use of the open connection.
Using DataSets in Cache
Without a doubt the main component in ADO.NET is DataSet. It can be used with any of ADO.NET providers, representing a data structure in memory. Here the term “memory” relates to the fact that we can create a query to a database table, extract information and use them after the connection is closed. For this reason, applications that use this component are known as “disconnected”.
While a DataReader demands that a connection is active so that a data reading is made, a DataSet can get its data once and keep them in memory for posterior use. This assures even more the scalability of ASP.NET applications. It’s up to you to detect when it is better to use a DataReader or a DataSet.
To be clearer, we’ll imagine a scenario: you work for a university and need to build the registration page for that institution’s application forms. In this page, there’s a form for candidate’s register, where there are some TextBoxes for the filling in of fields such as Name, Address, Date of Birth, etc. The only dynamic information (that comes from a database) is in a DropDownList, where the candidate can choose the course to which he wishes to subscribe to. This information is obtained from a database table, that contains all the courses with available vacant.
Now answer one thing: why connect to the database to fill in the DropDownList, every time a user opens the page, if the courses never change? In a situation such as this, the courses’ table would be modified in the database probably once a year or semester. This is a typical example of where we can use a DataSet instead of a DataReader.
Let’s see how to use the resource in the practice. Start a new ASP.NET application, following the same steps of the previous example. Place a SqlDataAdapter in the form and, in the wizard that’ll open, chose the connection that we’ve created previously. Use the following SQL instruction to get data from the Categories table (that stores information of product’s categories):

SELECT
    CategoryId, CategoryName
FROM
    Categories

Place a DropDownList on the form and on the Page_Load of the Web Form type the code presented in List 2.

private void Page_Load(object sender, System.EventArgs e)
{
    if (!IsPostBack)
    {
        DropDownList1.DataSource = dsCatProd();
        DropDownList1.DataMember = "Categories";
        DropDownList1.DataTextField = "CategoryName";
        DropDownList1.DataValueField = "CategoryId";
        DropDownList1.DataBind();      
    }
}
Listing 2. Page_Load event code
Here, we are simply testing if the page is being loaded for the first time (IsPostBack) to then initialize the DropDownList. Observe that we’ve attributed the value of the DataSource property for a function called dsCatProd (seen in Listing 3), that returns a DataSet. The dsCatProd function as shown in Listing 3.

private DataSet dsCatProd()
{
    if (Cache["dsCatProd"] == null)
    {
        DataSet ds = new DataSet();
       sqlDataAdapter1.Fill(ds,"CATEGORIES");
       Cache["dsCatProd"] = ds;
    }
    return (DataSet)Cache["dsCatProd"];
}
Listing 3. dsCatProd Function
In the previous code, we tested if a variable with the name we defined (“dsCatProd”) exists in the Cache. If it does exist, it’s because DataSet is already in memory (a user has probably already made the query previously). If not, we’ll remake the data cache, by calling the SqlDataAdapter’s Fill method to fill the DataSet’s data. And, finally, we place DataSet in memory (Cache object).
The Cache object is used in ASP.NET to share data between all of the application’s users. Even after the request is processed, the information remains in the server memory and can be used later. You can place as many objects as you wish in memory, needing only to supply a different name for each one, but be careful not to exaggerate in the amount of information that you store in the server.

Note: ASP.NET allows you to use a state server, allowing the cache stored information and session to reside in a process different other than the aspnet_wp.exe (the process used by the ASP.NET to run Web applications). It is also possible to specify a dedicated server (that has more memory, for example) only to store cache and session objects. Another alternative is to persist the sessions in the database itself, to save memory. State Servers are also used to share session data when multiple servers are used.

Figure 1 shows the application running. Make a test: open a second browser and access the same page however, stop the database server before. Observe that data will be shown even if the SQL Server is disabled; indicating that the information really hadn’t been obtained from the database, but from the cache that was already stored in the server memory.

image001.png
Figure 1
. Using a cached DataSet


Using DataViews
Let’s improve the example. We’ll allow the user to see the related products when he chooses a certain category. Instead of creating a new query to the database after the selection, we’ll filter a query that will already be resident in memory. That is, we’ll keep all the products in cache and use a DataView for filtering.
Place a ListBox in the form and a second SqlDataAdapter, configuring the following SQL instruction, which returns all the records from the table of products (Products):

SELECT
    CategoryId, ProductName
FROM
    Products

Instead of using a second DataSet to store the products, we’ll use the same created previously. This is possible in ADO.NET; all that needs to be done is pass the same DataSet as a parameter for the Fill method of both the SqlDataAdapters. Include then the following code, just below the Fill of the SqlDataAdapter1, which is in dsCatProd method:

...
sqlDataAdapter2.Fill(ds,"PRODUCTS");

Now, when the page is opened for the first time, DataSet will contain in memory data on all the products and categories of the database. All we need to do is manipulate this information in the best way, without the necessity of querying the database at every request. For example, to display the products related to the selected category, modify Page_Load as shown in Listing 4.

private void Page_Load(object sender, System.EventArgs e)
{
    if (!IsPostBack)
    {
        // DropDownList code
    }
    else
    {
        ListBox1.DataSource = dvCatProd(DropDownList1.SelectedValue);
        ListBox1.DataTextField = "ProductName";
        ListBox1.DataBind();
    }
}
Listing 4. Updated Page_Load event code
The code is similar to the used one to fill in the DropDownList, except that now we are using a DataView as data source for the ListBox control. The dvCatProd function receives the code from a category as a parameter and returns the related products, through a DataView, obtained from the memory DataSet. Its code is shown in Listing 5

private DataView dvCatProd(object CategoryId)
{
    DataView dv = new DataView();
    dv.Table = ((DataSet)Cache["dsCatProd"]).Tables["PRODUCTS"];
    dv.RowFilter = "CategoryId = " + CategoryId.ToString();
    return dv;
}
Listing 5. dvCatProd Function
A DataView is ideal to be used with memory DataSets. It allows you to filter and organize resultsets, supplying different visions of a same data set, without using any kind of SQL instruction or extra communication with the database. It is worth remembering that you can have several DataViews acting the same DataSet.
The last step is to setup AutoPostBack of DropDownList to True, so that the postback is made in the server when the user chooses an item from the control. Figure 2 shows the example in execution.

image003.png
Figure 2
. Using DataViews from a DataSet in memory
It is clear, if the data is modified in the database, they will not be reflected in the DataSet of cache. It is your job to remake the cache whenever necessary. ASP.NET possesses some resources to make this process easier. We can, for example, specify a cache expiration criterion, or create a dependence mechanism.
Stored Procedures
We could not speak of ASP.NET applications optimizations with ADO.NET without showing the use of Stored Procedures. They drastically increase the speed of Web solutions, as much in the obtaining of data from the SQL Server as in the update, insertion or exclusion of records. This happens because SQL server can optimize execution plans and pre-compile these instructions that reside in the database, and no longer in the customer application, which only takes charge of passing the appropriate parameters.
In this example, we will build a ASP.NET application that will make effective use of Stored Procedures, in such a way as to demonstrate the increase in scalability. As is custom, you’ll learn how Visual Studio can help us in this process, automatically creating the procedures in SQL Server through wizards of SqlDataAdapter.
In a new ASP.NET application, we will put some TextBoxes for data entry in the Web Form. To make this easier, we will use only the table’s main fields (ProductName, CategoryID and UnitPrice) and we will work only with the addition operation. The names (ID property) of the TextBoxes must be as tbProductName, tbCategoryID and tbUnitPrice, respectively. Put some Labels and setup the Text to indicate what be must filled in. A Button with the Text “Insert Product” will be used to add the information in the database. Use Figure 3 as a reference to setup your form.

image005.png
Figure 3.
Main form of the application, with TextBoxes
The first step is to place a SqlDataAdapter from the Toolbox. An wizard will be showed, which will help in the setup of the various component options. Click in Next and in the screen that is displayed, click the New Connection button. Fill in the connection information, informing the SQL server name, user/password and database. Click in Test Connection to see if everything is working properly. Next, click OK and, back to the wizard, click Next.
The next screen is very important in the SqlDataAdapter setup process. This is the moment when we have to inform VS.NET how the component will gain access to the database: using standard SQL commands (Select, Update, Delete and Insert) or through Stored Procedures. Since we haven’t yet created the procedures in the database, we’ll ask VS.NET to do that automatically for us, by selecting the last option. Next, click Next.
In the following screen, we have to setup the query to be used by SqlDataAdapter in order to obtain SQL Server data. To assist in the creation of the query, we can use the Query Builder option, which will open a visual editor. In the Add Table window, double click Products and, after that, click Close. In the Query Builder, select the fields ProductID, ProductName, CategoryID and UnitPrice (the others will not be used in this example). Observe that the Select command is being generated automatically by the IDE. Based on Select, the IDE will then generate the codes of the procedures. Click Ok to confirm and then Next.
In the next screen, we can inform the names that will be given to the new procedures to be created in the SQL Server. Use the standard that you desire, here I called them “Upd_Product”, “Ins_Product” and so forth. In this same window, observe that we have the option “Yes, create them in the database for me”, indicating that the IDE can create the procedures in the database.
Clicking in Preview SQL Script, we can visualize the DDL code generated by the creation of the Stored Procedures. Observe that each procedure receives entry parameters, which are used to complement the SQL instructions that they use (Insert, Update, Delete and Select).
Click again in Next and check that in the last screen of the wizard a summary is displayed, indicating the actions to be made by the IDE. Click Finish to confirm the operations. At this moment, the IDE performed a series of operations for us, they are:
1 – Creation of four Stored Procedures in the database called, respectively, Sel_Products, Upd_Products, Ins_Products and Del_Products;
2 – Addition of two components to the designer of the application’s main Web Form, one SqlConnection and one SqlDataAdapter;
3 – Initializing of the SqlDataAdpater’s internal SqlCommands (DeleteCommand, InsertCommand, SelectCommand and UpdateCommand properties);
4 – For each internal SqlCommand described in the prior step, the IDE did the following:
·        Reference to the SelConnection1connection through the Connection property;
·        Setup CommandType for StoredProcedure (if there were traditional SQL Statements, this property would be setup as Text);
·        Set the CommandText property to the respective Stored Procedure created in the SQL Server;
·        Initialize the necessary parameters in the collection Parameteres.

The last step is to codify the Click event of the WebForm Button so that it executes the Stored Procedure and inserts the data from the form in the database. All we need to do is to capture the values of the TextBoxes and use them to fill the SqlDataAdapter InsertCommand parameters. The code of Listing 6 shows how to do this.

private void Button1_Click(object sender, System.EventArgs e)
{
       SqlCommand cmd = sqlDataAdapter1.InsertCommand;
       cmd.Parameters["@ProductName"].Value = tbProductName.Text;
       cmd.Parameters["@CategoryId"].Value = tbCategoryID.Text;
       cmd.Parameters["@UnitPrice"].Value = tbUnitPrice.Text;
       try
       {
             cmd.Connection.Open();
             cmd.ExecuteNonQuery();                        
       }
       finally
       {
             cmd.Connection.Close();
       }
}
Listing 6. Configuring the insertion using Stored Procedures
Remember to add the namespace System.Data.SqlClient in using. Run the application to test the insertion from the ASP.NET form. Inform the values in the TextBoxes and click in the button Insert Product to insert the data in the SQL server using the Stored Procedure created. Select the table and observe that the register was included successfully.
Obtaining data using Stored Procedures
In this second example, we will see how to create and gain access to a Stored Procedure that returns a data set, showing data in DataGrids. Our Stored Procedure will be special: it will return two ResultSets, in such a way as to optimize the solution, containing data from the tables of Categories and Products.
Instead of using the IDE wizard to create the procedure, we will use the Enterprise Manager of SQL Server. In the Northwind database, locate the item Stored Procedures and right click on it, choosing the New Stored Procedure option (name it “Sel_ProductsAndCategories”). Click the Check Sintax button to verify your typing. Listing 7 shows the script.

CREATE PROCEDURE dbo.Sel_ProductsAndCategories
AS
       SET NOCOUNT ON;
SELECT * FROM Categories;
SELECT * FROM Products;
GO
Listing 7. Stored Procedure Code Sel_ProductsCategories
In VS.NET, start a new ASP.NET application. Place a SqlConnection and setup the ConnectionString to access the Northwind database. Place also a SqlCommand and set your Connection to SqlConnection1. Set your CommandType to value Stored Procedure and in CommandText type Sel_ProductsAndCategories. And, finally, place two DataGrids.
In the Page_Load type the code in Listing 8. In the code, we call the ExecuteReader method of the SqlCommand to run the Stored Procedure, instead of the ExecuteNoQuery. This because the Stored Procedure returns a result set (actually, two), that it is returned in a SqlDataReader object (remember to add the System.Data.SqlClient in using).
Next, we make the DataBind of the DataReader for the first DataGrid, that will show data from the Categories table. In order for the SqlDataReader to advance to the second ResultSet, we call its NextResult method. We then make the DataBind for the second DataGrid, that will show data from the Products table.

private void Page_Load(object sender, System.EventArgs e)
{
       sqlConnection1.Open();
       try
       {
             SqlDataReader rd = sqlCommand1.ExecuteReader();
             DataGrid1.DataSource = rd;
             DataGrid1.DataBind();
             rd.NextResult();
             DataGrid2.DataSource = rd;
             DataGrid2.DataBind();            

       }
       finally
       {
             rd.Close();
               sqlConnection1.Close();
       }                  
}
Listing 8. Using a SqlDataReader to extract data from the database through Stored Procedures
 With this, our solution is already sufficiently optimized, since we are making use of DataReaders (the fastest way to read data from the database) along with Stored Procedures. Observe, also, that bringing two ResultSets in this, makes the result a lot faster if compared to an application that brings data from two tables using two Selects.
Conclusions
Using the techniques seen in this article, you will now be able to optimize your ASP.NET applications so that they have a better performance and scalability, leaving their final users much more satisfied with their response time. The use of Stored Procedures, when done in an appropriate manner, can assure the success of its ASP.NET solution with ADO.NET, being unbeatable if compared to any another existing technology. DataSets in memory and DataReaders also are excellent ingredients to turbo your Web Site with ASP.NET and ADO.NET.

C# StringBuilder Optimization Tip

In this tip, you want to see how you can optimize the StringBuilder type's Append method in certain cases that deal with short strings. Instead of appending a string literal that contains two characters, you can append two characters separately. Here, we describe why this optimization works and when to apply it.

Test program

This program benchmark harness demonstrates how you can express a two-character append operation in two different ways. In the first tight loop, two space characters are appended in separate method calls; in the second tight loop, a two-character string is appended in a single method call. The first loop has better performance despite being longer to type into code.
Program that tests StringBuilder optimization tip [C#]

using System;
using System.Diagnostics;
using System.Text;

class Program
{
    const int _max = 10000000;
    static void Main()
    {
 StringBuilder builder = new StringBuilder();
 var s1 = Stopwatch.StartNew();
 for (int i = 0; i < _max; i++)
 {
     // Append two characters.
     builder.Append(' ');
     builder.Append(' ');
 }
 s1.Stop();
 builder = new StringBuilder();
 var s2 = Stopwatch.StartNew();
 for (int i = 0; i < _max; i++)
 {
     // Append two characters combined into a string.
     builder.Append("  ");
 }
 s2.Stop();
 Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000 * 1000) /
     _max).ToString("0.00 ns"));
 Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000 * 1000) /
     _max).ToString("0.00 ns"));
 Console.Read();
    }
}

Output

12.99 ns
15.29 ns

Why does this work? The reason appending two characters separately to a StringBuilder is faster is because the string type in the C# language has some complexities that characters do not have. A char type can be represented by a small integer, while a string type must be allocated on the managed heap and have its own memory location.
The string type is a full-fledged object while a char is a number only. To resolve the string argument in Append, the StringBuilder must reference all the characters separately. However, the control path for the char overload is more direct and therefore faster.

When to use char appends? At some point, using separate character appends will slow down the method you are writing. My testing showed that using chars for 1, 2, 3 and often 4 character long strings was more efficient. Longer than that and the strings were more efficient. This may involve locality of reference or simply better string copying routines, such as those that unroll loops and use unsafe code.
What about Response.Write? The Response.Write method, which is often used in ASP.NET, can use this same optimization and the results are sometimes more pronounced. I tested Response.Write and found that single-character writes were faster in sequences of two than two-character string writes.

Summary

In this tip, we looked at one way you can perform micro-optimizations on the StringBuilder type in the C# programming language. While this is not a dramatic performance boost in many cases where StringBuilder is used, it can provide a measurable speedup to much StringBuilder code as well as Response.Write code.

C# Replace Optimization

You have a method in your C# program that replaces many strings with other strings. This method can require a lot of time to run because it tries to replace so many things. In this article, we look at a way you can improve the performance of this kind of method without using any advanced data structures.

Examples

Let's look at the initial version of the method. It runs four Replace calls on the formal parameter to the method. String literals are used as the argument. When looking at the string literals, you can see that they contain some common substrings: two contain "<span>C" and two contain "<span>D". This property can be used to optimize.
First version of method [C#]

static string A(string text)
{
    text = text.Replace("<span>Cat ", "<span>Cats ");
    text = text.Replace("<span>Clear ", "<span>Clears ");
    text = text.Replace("<span>Dog ", "<span>Dogs ");
    text = text.Replace("<span>Draw ", "<span>Draws ");
    return text;
}

Second version of method [C#]

static string B(string text)
{
    if (text.Contains("<span>C"))
    {
 text = text.Replace("<span>Cat ", "<span>Cats ");
 text = text.Replace("<span>Clear ", "<span>Clears ");
    }
    if (text.Contains("<span>D"))
    {
 text = text.Replace("<span>Dog ", "<span>Dogs ");
 text = text.Replace("<span>Draw ", "<span>Draws ");
    }
    return text;
}
Second version. Now let's look at the optimized version. This version uses the Contains method around all the Replace calls with the specified common string literal. Thus, in version B, the first two Replace calls are only run when the common pattern is found; the second two Replace calls are also guarded.
Note: Another approach would be to use text.Contains("<span>"). This could be beneficial depending on how common that string is in the hypothetical data set.

What about StringBuilder?

The StringBuilder type contains a Replace method. This can help optimize certain situations, but the Replace call will require a search through the entire string still. If not many replacements occur, the StringBuilder will actually be slower because of the initial cost of the StringBuilder.

Summary

This replace optimization uses a heuristic to improve performance. If all the replacements always are run, guarding them in this way will hurt performance. If there are no good substrings to test for, this optimization will also not help. However, if many replacements are run, not all actually change the string, and they contain common character sequences, this technique can reduce the computational requirements of your method considerably.

C# Optimization Secrets

You want to see ways you can add performance optimizations to your C# programs, focusing on the level of the code statements and methods. While high-level considerations, and factors external to your code are often most important, such as computer processor and network speed, there are many low-level performance optimizations you can do inside the C# language that can improve performance. We describes these optimizations.
Tip: Focus on the "hot paths" in your program for optimizations.

Overview

In this overview, we describe the general considerations when optimizing your C# code. First, the C# language is compiled, and with the .NET Framework, you can attain performance close to languages such as C or C++.
Generally, using the simplest features of the language provides the best performance; for example, using the for-loop and avoiding parameters and return values is typically fastest. You must balance these performance goals with code readability and understandability.

Benchmark

At all levels of performance optimization, you should be taking measurements on the changes you make to methods. You can do this with the .NET Framework methods available in the Stopwatch type. It often pays to create a multitude of console programs where the methods are benchmarked repeatedly on data as it changes. You should always avoid regressing performance unless there is a clear reason to do so.

Static methods

In the C# language, non-inlined instance methods are always slower than non-inlined static methods. The reason for this is that to call an instance method, the instance reference must be resolved, to determine what method to call. Static methods do not use an instance reference.
If you look at the intermediate language, you will see that static methods can be invoked with fewer instructions. You can see an experiment based on the callvirt and call instructions on this site.

Avoid parameters

When you call any method in the C# language that was not inlined, the runtime will actually physically copy the variables you pass as arguments to the formal parameter slot memory in the called method. This causes stack memory operations and incurs a performance hit. It is faster to minimize arguments, and even use constants in the called methods instead of passing them arguments.

Avoid local variables

When you call a method in your C# program, the runtime allocates a separate memory region to store all the local variable slots. This memory is allocated on the stack even if you do not access the variables in the function call. Therefore, you can call methods faster if they have fewer variables in them.
One way you can do this is isolate rarely used parts of methods in separate methods. This makes the fast path in the called method more efficient, which can have a significant performance gain.

Constants

In the .NET Framework, constants are not assigned a memory region, but are instead considered values. Therefore, you can never assign a constant, but loading the constant into memory is more efficient because it can injected directly into the instruction stream. This eliminates any memory accesses outside of the memory, improving locality of reference. The performance advantage of const fields is demonstrated on this site.

Static fields

Static fields are faster than instance fields, for the same reason that static methods are faster than instance methods. When you load a static field into memory, you do not need the runtime to resolve the instance expression. Loading an instance field must have the object instance first resolved. Even in an object instance, loading a static field is faster because no instance expression instruction is ever used. Please review the article on this topic.

Inline methods

Unlike the C++ language, the C# language does not allow you to suggest a method be inlined into its enclosing method call spots. Often, the .NET Framework is conservative here and will not inline medium-sized or large methods. However, you can manually paste a method body into its call spot.
Typically, this improves performance in micro-benchmarks, and it is really easy to do. However, it will make code harder to modify; it is only suggested for a very few, critical spots in programs.

Switch

You will find that the switch statement compiles in a different way than if-statements typically do. For example, if you use a switch on an int, you will often get jump statements, which are similar to a computed goto mechanism. Using jump tables makes switches much faster than some if-statements; please see the pertinent article for more details. Also, using a char switch on a string is very fast.

Flattened arrays

Using two-dimensional arrays in C# is relatively slow. However, you can explicitly create a one-dimensional array and access it through arithmetic that supposes it is a two-dimensional array. This is sometimes called flattening an array. You must use multiplication and addition to acquire the correct element address. Typically, this optimization will improve the performance of accessing any array, and it is used extensively on this site.

Jagged arrays

While flattened arrays are typically most efficient, they are sometimes very impractical. In these cases, you can use jagged arrays to improve the lookup performance. The .NET Framework enables faster accesses to jagged arrays than to 2D arrays. Please note that jagged arrays may cause slower garbage collections, because each jagged array element will be treated separately by the garbage collector.

StringBuilder

If you are doing significant appending of strings using the C# language, the StringBuilder type can improve performance. This is because the string type is immutable and can not be changed without reallocating the entire object. Sometimes, using strings instead of StringBuilder for concatenations is faster; this is typically the case when using very small strings or doing infrequent appends.

Char arrays

Using char arrays in your C# code is sometimes the fastest way to build up a string. Typically, you will combine char arrays with for-loops and character testing expressions. This logic is more painful to develop and test, but the time savings can be very significant, making certain routines more than ten times faster, while reducing memory allocations as well.

Byte arrays

In the C# language, the smallest unit of addressable storage is the byte type. You can store ASCII characters in a single byte, as well as small numbers. If you can store your data in an array of bytes, this allows you to save memory. For example, an array of characters or a string uses two bytes per character; an array of bytes can represent that data in one byte per character, result in about half the total memory usage.

Arrays

In the .NET Framework, you have many options for collections, such as the List type, and various other types such as ArrayList. While these types are convenient and should be used when necessary, it is always more efficient to use a simple array if this is possible.
The reason for this is that the more complex collections such as List are actually composed of internal arrays. They add logic to avoid the burden of managing the array size on each use. However, if you do not need this logic, or can adjust your code so that the logic is not needed, using an array will be faster.

Capacities

For collections in the .NET Framework and C# language, you can use an optional capacity argument to influence the intial buffer sizes. It is best to pass a reasonable parameter in most cases when creating a collection such as a Dictionary or List. This avoids many allocations when adding elements that were not anticipated. Please see the pertinent article on Dot Net Perls for details.

Rewrite loops

Here, we describe ways that you can rewrite the loops in your C# programs to improve performance. While the foreach loop can have good performance in many cases, it is best to use the for-loop in all performance-critical sections when possible. The reason for this is that not only do for-loops sometimes have better raw performance, you can often reuse the index variable (induction variable) to optimize other parts of the loop or method.
Typically, the while loop, the for loop and the do-while loop have the best performance. Also, it is sometimes beneficial—and sometimes harmful—to "hoist" the maximum loop variable outside of the for-loop statement.

 Consider structs
Unless you know more about the C# language than I do, it is typically best to avoid structs entirely. If you use structs, you must be careful to not pass the struct as a parameter to methods often, or performance will degrade to worse than using a class type. The reason for this is that structs are copied in their entirety on each function call or return value.
Structs can improve the performance of the garbage collector by reducing the number of distinct objects. Also, you can sometimes use separate arrays instead of arrays of structs, which can improve performance further.

Lookup tables

While switch statements or hashtables such as Dictionary in the C# language can provide good performance, using a lookup table is frequently the optimal choice. For example, instead of testing each character using logic when lowercasing a string, you can translate each character through a lookup table. The lookup table can be implemented as a character array. Another example is that you can implement the ROT13 algorithm with a lookup table, improving performance by more than two times.

Char argument

Often, you may need to pass a single character to a certain method in your programs. For example, the StringBuilder type allows you to append a single char; the Response.Write method also allows you to write a single char. It is more efficient to pass a char instead of a single-char string. The char is a value type, and is represented by two bytes, while a string is a reference type and requires over 20 bytes. This site contains an exploration of StringBuilder char argument performance.

Avoid ToString

In this tip, we assert that it is poor programming style to use the ToString method unnecessarily. Sometimes, developers will call ToString on a character in a string, and then test it against a single-character string literal. This is grossly inefficient; instead, use a character testing expression with two chars.
Please reference the specific article on this topic for more details here. The article shows this mistake results in code that is ten times slower than the correct approach.

Int string cache

Many C# programs use the ToString method on integer values frequently. Unfortunately, this requires an allocation on the managed heap for the new string. This will cause the next garbage collection to become slower. You can actually use a lookup table to optimize common cases for the integer ToString operation. This site demonstrates how this lookup table can make the ToString method thirty times faster.

IL Disassembler

For .NET development, you should be opening your methods with the IL Disassembler tool provided by Microsoft. This is a free tool and it provides an interface for you to view the MSIL (Microsoft Intermediate Language) output of all your compiled Release executables. It is sometimes useful to save copies of the intermediate language as you make changes, or to even count instructions.

Avoid sorting

Often, you can avoid performing a sort operation on an array or string simply by testing whether the input string or array is already sorted. Sometimes, this makes a big performance improvement. In other cases, this slows down your programs. Please see the article about checking alphabetical characters for more information.

Avoid string conversions

In this optimization tip, we note that you can actually avoid many string-based conversions. For example, you may need to ensure that a string is lowercased. If the string is already lowercase, you can avoid allocating a new string entirely. However, the framework ToLower method will not avoid this for you; you must manually test to see if no lowercasing is necessary, as with a for-loop over the characters.

Avoid Path methods

Unfortunately, the Path methods in the System.IO namespace are somewhat slow for many applications. Sometimes they can cause unnecessary allocations to occur, copying strings more than once. You can sometimes use character-based algorithms to minimize allocations, improving performance by nearly three times.

Dictionary

It is important that you use hashtables in your programs when appropriate. The Dictionary collection in the .NET Framework is not optimal in many cases, but provides good performance in many different situations. While we assume a fundamental knowledge of algorithms and searching here, the Dictionary is an essential tip in any performance article.

Read this site

The site you are reading, Dot Net Perls, contains a multitude of optimization experiments, often proven with benchmarks that provide times in nanoseconds per method call. Resources such as this site can be invaluable for certain tasks in programming; before Dot Net Perls came about, no such site had this information on optimization.

Compiler theory

While experimentation such as benchmarking and analyzing instructions generated can result in excellent program performance, without understanding the core theories of compilers you may be lacking knowledge about program performance. Unfortunately, compiler theory involves a great deal of advanced mathematics and can be very dense to start with.
My observation is that only a tiny minority of application developers have a significant knowledge of compiler theory; this topic may be more suitable to academic computer scientists and not rapid application development programmers. A good book on this subject is the dragon book.

Temporal locality

Another way you can optimize a program significantly is by rearranging it to increase temporal locality. This means that methods that act on a certain part of memory (such as the hard disk) are run at all once. You can find out more about this optimization here.

Misnomer

The term optimization is actually a misnomer in computer science. A program can never be truly optimized. Because compiler theory is undecidable, a program can never be proven to be optimally efficient—perhaps another approach is faster?

 Resources

There are many pages on this website that are focused on optimization tips. These pages are listed below; most of them show how you can rewrite a certain pattern of code to something arguably more efficient. Please be aware some of these optimizations result in code that is less maintainable.



















SQLite : The Quick and Dirty Setup for .NET.

1) Download SQLite
While you can get the generic windows binary on the SQLite download page, I’m going to recommend you instead grab the ADO.NET 2.0 Provider for SQLite from sourceforge. I’m not saying this is the most performant version (it does have an ADO wrapper with its attendant malarkey), but it really is a super-easy starting implementation that’s probably good enough for the long haul.
2) Copy the resultant DLL (System.Data.SQLite.DLL) to your project and add a reference.
3) Download and install one of the billions of SQLite GUI clients. I’ve been using the aptly named "SQLite Administrator" (FREE) which has a sweet, Query Analyzer-alike interface. You can find a big list of SLQLite gui clients here http://www.sqlite.org/cvstrac/wiki?p=ManagementTools if you are so inclined.
4) Through the GUI, create a database and make a test table of whatever floats your boat. The result will be a single file with a .s3db extension.
5) There is no step 5! DONE! You can now query, insert, update, delete, create, truncate, etc, to your heart’s content using the System.Data.SQLite ADO wrapper. Here is a little helper db util type class to show you the basic schleck:

using System;
using System.Data;
using System.Data.SQLite;

namespace SqlExample
{
    class Program
    {
        private const string Connectionstring = "Data Source=C:CafeX.s3db";

        public static DataTable GetDataTable(string sql)
        {
            var dt = new DataTable();

            var cnn = new SQLiteConnection(Connectionstring);
            using (cnn)
            {
                cnn.Open();
                var mycommand = new SQLiteCommand(cnn);
                mycommand.CommandText = sql;
                SQLiteDataReader reader = mycommand.ExecuteReader();
                dt.Load(reader);
                reader.Close();
            }

            return dt;
        }

        public static int ExecuteNonQuery(string sql)
        {
            int rowupdate;
            var cnn = new SQLiteConnection(Connectionstring);
            using (cnn)
            {
                cnn.Open();
                var mycommand = new SQLiteCommand(cnn) { CommandText = sql };
                rowupdate = mycommand.ExecuteNonQuery();
            }
            return rowupdate;
        }

        public static object ExecuteScalar(string sql)
        {
            var cnn = new SQLiteConnection(Connectionstring);
            cnn.Open();
            var mycommand = new SQLiteCommand(cnn) { CommandText = sql };
            var value = mycommand.ExecuteScalar();
            cnn.Close();
            cnn.Dispose();
            return value != null ? value.ToString() : null;
        }

        static void Main(string[] args)
        {
            const string sqlinsert = "insert into UserInfo(UserName, FullName) values('hcubiu', 'Michael Lee')";
            ExecuteNonQuery(sqlinsert);
            const string sqlselect = "select * from UserInfo";
            var d = GetDataTable(sqlselect);
            if(d.Rows.Count>0)
            {
                for (var i = 0; i < d.Rows.Count; i++)
                {
                    Console.WriteLine("{0} - {1} - {2}", d.Rows[i]["UserId"], d.Rows[i]["UserName"], d.Rows[i]["FullName"]);
                }
            }
            Console.ReadLine();
        }
    }
}

SQL As Understood By SQLite(CREATE TABLE)


CREATE TABLE

create-table-stmt:

syntax diagram create-table-stmt

column-def:

syntax diagram column-def

type-name:

syntax diagram type-name

column-constraint:

syntax diagram column-constraint

table-constraint:

syntax diagram table-constraint

foreign-key-clause:

syntax diagram foreign-key-clause
The "CREATE TABLE" command is used to create a new table in an SQLite database. A CREATE TABLE command specifies the following attributes of the new table:
  • The name of the new table.
  • The database in which the new table is created. Tables may be created in the main database, the temp database, or in any attached database.
  • The name of each column in the table.
  • The declared type of each column in the table.
  • A default value or expression for each column in the table.
  • A default collation sequence to use with each column.
  • Optionally, a PRIMARY KEY for the table. Both single column and composite (multiple column) primary keys are supported.
  • A set of SQL constraints for each table. SQLite supports UNIQUE, NOT NULL, CHECK and FOREIGN KEY constraints.
Every CREATE TABLE statement must specify a name for the new table. Table names that begin with "sqlite_" are reserved for internal use. It is an error to attempt to create a table with a name that starts with "sqlite_".
If a <database-name> is specified, it must be either "main", "temp", or the name of an attached database. In this case the new table is created in the named database. If the "TEMP" or "TEMPORARY" keyword occurs between the "CREATE" and "TABLE" then the new table is created in the temp database. It is an error to specify both a <database-name> and the TEMP or TEMPORARY keyword, unless the <database-name> is "temp". If no database name is specified and the TEMP keyword is not present then the table is created in the main database.
It is usually an error to attempt to create a new table in a database that already contains a table, index or view of the same name. However, if the "IF NOT EXISTS" clause is specified as part of the CREATE TABLE statement and a table or view of the same name already exists, the CREATE TABLE command simply has no effect (and no error message is returned). An error is still returned if the table cannot be created because of an existing index, even if the "IF NOT EXISTS" clause is specified.
It is not an error to create a table that has the same name as an existing trigger.
Tables are removed using the DROP TABLE statement.

CREATE TABLE ... AS SELECT Statements

A "CREATE TABLE ... AS SELECT" statement creates and populates a database table based on the results of a SELECT statement. The table has the same number of columns as the rows returned by the SELECT statement. The name of each column is the same as the name of the corresponding column in the result set of the SELECT statement. The declared type of each column is determined by the expression affinity of the corresponding expression in the result set of the SELECT statement, as follows:

Expression AffinityColumn Declared Type
TEXT"TEXT"
NUMERIC"NUM"
INTEGER"INT"
REAL"REAL"
NONE"" (empty string)

A table created using CREATE TABLE AS has no PRIMARY KEY and no constraints of any kind. The default value of each column is NULL. The default collation sequence for each column of the new table is BINARY.
Tables created using CREATE TABLE AS are initially populated with the rows of data returned by the SELECT statement. Rows are assigned contiguously ascending rowid values, starting with 1, in the order that they are returned by the SELECT statement.

Column Definitions

Unless it is a CREATE TABLE ... AS SELECT statement, a CREATE TABLE includes one or more column definitions, optionally followed by a list of table constraints. Each column definition consists of the name of the column, optionally followed by the declared type of the column, then one or more optional column constraints. Included in the definition of "column constraints" for the purposes of the previous statement are the COLLATE and DEFAULT clauses, even though these are not really constraints in the sense that they do not restrict the data that the table may contain. The other constraints - NOT NULL, CHECK, UNIQUE, PRIMARY KEY and FOREIGN KEY constraints - impose restrictions on the tables data, and are are described under SQL Data Constraints below.
Unlike most SQL databases, SQLite does not restrict the type of data that may be inserted into a column based on the columns declared type. Instead, SQLite uses dynamic typing. The declared type of a column is used to determine the affinity of the column only.
The DEFAULT clause specifies a default value to use for the column if no value is explicitly provided by the user when doing an INSERT. If there is no explicit DEFAULT clause attached to a column definition, then the default value of the column is NULL. An explicit DEFAULT clause may specify that the default value is NULL, a string constant, a blob constant, a signed-number, or any constant expression enclosed in parentheses. An explicit default value may also be one of the special case-independent keywords CURRENT_TIME, CURRENT_DATE or CURRENT_TIMESTAMP. For the purposes of the DEFAULT clause, an expression is considered constant provided that it does not contain any sub-queries or string constants enclosed in double quotes.
Each time a row is inserted into the table by an INSERT statement that does not provide explicit values for all table columns the values stored in the new row are determined by their default values, as follows:
  • If the default value of the column is a constant NULL, text, blob or signed-number value, then that value is used directly in the new row.
  • If the default value of a column is an expression in parentheses, then the expression is evaluated once for each row inserted and the results used in the new row.
  • If the default value of a column is CURRENT_TIME, CURRENT_DATE or CURRENT_TIMESTAMP, then the value used in the new row is a text representation of the current UTC date and/or time. For CURRENT_TIME, the format of the value is "HH:MM:SS". For CURRENT_DATE, "YYYY-MM-DD". The format for CURRENT_TIMESTAMP is "YYYY-MM-DD HH:MM:SS".
The COLLATE clause specifies the name of a collating sequence to use as the default collation sequence for the column. If no COLLATE clause is specified, the default collation sequence is BINARY.
The number of columns in a table is limited by the SQLITE_MAX_COLUMN compile-time parameter. A single row of a table cannot store more thanSQLITE_MAX_LENGTH bytes of data. Both of these limits can be lowered at runtime using the sqlite3_limit() C/C++ interface.

SQL Data Constraints

Each table in SQLite may have at most one PRIMARY KEY. If the keywords PRIMARY KEY are added to a column definition, then the primary key for the table consists of that single column. Or, if a PRIMARY KEY clause is specified as a table-constraint, then the primary key of the table consists of the list of columns specified as part of the PRIMARY KEY clause. If there is more than one PRIMARY KEY clause in a single CREATE TABLE statement, it is an error.
If a table has a single column primary key, and the declared type of that column is "INTEGER", then the column is known as an INTEGER PRIMARY KEY. See below for a description of the special properties and behaviors associated with an INTEGER PRIMARY KEY.
Each row in a table with a primary key must feature a unique combination of values in its primary key columns. For the purposes of determining the uniqueness of primary key values, NULL values are considered distinct from all other values, including other NULLs. If an INSERT or UPDATEstatement attempts to modify the table content so that two or more rows feature identical primary key values, it is a constraint violation. According to the SQL standard, PRIMARY KEY should always imply NOT NULL. Unfortunately, due to a long-standing coding oversight, this is not the case in SQLite. Unless the column is an INTEGER PRIMARY KEY SQLite allows NULL values in a PRIMARY KEY column. We could change SQLite to conform to the standard (and we might do so in the future), but by the time the oversight was discovered, SQLite was in such wide use that we feared breaking legacy code if we fixed the problem. So for now we have chosen to continue allowing NULLs in PRIMARY KEY columns. Developers should be aware, however, that we may change SQLite to conform to the SQL standard in future and should design new programs accordingly.
UNIQUE constraint is similar to a PRIMARY KEY constraint, except that a single table may have any number of UNIQUE constraints. For each UNIQUE constraint on the table, each row must feature a unique combination of values in the columns identified by the UNIQUE constraint. As with PRIMARY KEY constraints, for the purposes of UNIQUE constraints NULL values are considered distinct from all other values (including other NULLs). If an INSERT or UPDATE statement attempts to modify the table content so that two or more rows feature identical values in a set of columns that are subject to a UNIQUE constraint, it is a constraint violation.
INTEGER PRIMARY KEY columns aside, both UNIQUE and PRIMARY KEY constraints are implemented by creating an index in the database (in the same way as a "CREATE UNIQUE INDEX" statement would). Such an index is used like any other index in the database to optimize queries. As a result, there often no advantage (but significant overhead) in creating an index on a set of columns that are already collectively subject to a UNIQUE or PRIMARY KEY constraint.
CHECK constraint may be attached to a column definition or specified as a table constraint. In practice it makes no difference. Each time a new row is inserted into the table or an existing row is updated, the expression associated with each CHECK constraint is evaluated and cast to a NUMERIC value in the same way as a CAST expression. If the result is zero (integer value 0 or real value 0.0), then a constraint violation has occurred. If the CHECK expression evaluates to NULL, or any other non-zero value, it is not a constraint violation. The expression of a CHECK constraint may not contain a subquery.
CHECK constraints have been supported since version 3.3.0. Prior to version 3.3.0, CHECK constraints were parsed but not enforced.
NOT NULL constraint may only be attached to a column definition, not specified as a table constraint. Not surprisingly, a NOT NULL constraint dictates that the associated column may not contain a NULL value. Attempting to set the column value to NULL when inserting a new row or updating an existing one causes a constraint violation.
Exactly how a constraint violation is dealt with is determined by the constraint conflict resolution algorithm. Each PRIMARY KEY, UNIQUE, NOT NULL and CHECK constraint has a default conflict resolution algorithm. PRIMARY KEY, UNIQUE and NOT NULL constraints may be explicitly assigned a default conflict resolution algorithm by including a conflict-clause in their definitions. Or, if a constraint definition does not include a conflict-clauseor it is a CHECK constraint, the default conflict resolution algorithm is ABORT. Different constraints within the same table may have different default conflict resolution algorithms. See the section titled ON CONFLICT for additional information.

ROWIDs and the INTEGER PRIMARY KEY

Every row of every SQLite table has a 64-bit signed integer key that uniquely identifies the row within its table. This integer is usually called the "rowid". The rowid value can be accessed using one of the special case-independent names "rowid", "oid", or "_rowid_" in place of a column name. If a table contains a user defined column named "rowid", "oid" or "_rowid_", then that name always refers the explicitly declared column and cannot be used to retrieve the integer rowid value.
The data for each table in SQLite is stored as a B-Tree structure containing an entry for each table row, using the rowid value as the key. This means that retrieving or sorting records by rowid is fast. Searching for a record with a specific rowid, or for all records with rowids within a specified range is around twice as fast as a similar search made by specifying any other PRIMARY KEY or indexed value.
With one exception, if a table has a primary key that consists of a single column, and the declared type of that column is "INTEGER" in any mixture of upper and lower case, then the column becomes an alias for the rowid. Such a column is usually referred to as an "integer primary key". A PRIMARY KEY column only becomes an integer primary key if the declared type name is exactly "INTEGER". Other integer type names like "INT" or "BIGINT" or "SHORT INTEGER" or "UNSIGNED INTEGER" causes the primary key column to behave as an ordinary table column with integer affinityand a unique index, not as an alias for the rowid.
The exception mentioned above is that if the declaration of a column with declared type "INTEGER" includes an "PRIMARY KEY DESC" clause, it does not become an alias for the rowid and is not classified as an integer primary key. This quirk is not by design. It is due to a bug in early versions of SQLite. But fixing the bug could result in very serious backwards incompatibilities. The SQLite developers feel that goofy behavior in a corner case is far better than a compatibility break, so the original behavior is retained. This means that the following three table declarations all cause the column "x" to be an alias for the rowid (an integer primary key):
  • CREATE TABLE t(x INTEGER PRIMARY KEY ASC, y, z);
  • CREATE TABLE t(x INTEGER, y, z, PRIMARY KEY(x ASC));
  • CREATE TABLE t(x INTEGER, y, z, PRIMARY KEY(x DESC));
But the following declaration does not result in "x" being an alias for the rowid:
  • CREATE TABLE t(x INTEGER PRIMARY KEY DESC, y, z);
Rowid values may be modified using an UPDATE statement in the same way as any other column value can, either using one of the built-in aliases ("rowid", "oid" or "_rowid_") or by using an alias created by an integer primary key. Similarly, an INSERT statement may provide a value to use as the rowid for each row inserted. Unlike normal SQLite columns, an integer primary key or rowid column must contain integer values. Integer primary key or rowid columns are not able to hold floating point values, strings, BLOBs, or NULLs.
If an UPDATE statement attempts to set an integer primary key or rowid column to a NULL or blob value, or to a string or real value that cannot be losslessly converted to an integer, a "datatype mismatch" error occurs and the statement is aborted. If an INSERT statement attempts to insert a blob value, or a string or real value that cannot be losslessly converted to an integer into an integer primary key or rowid column, a "datatype mismatch" error occurs and the statement is aborted.
If an INSERT statement attempts to insert a NULL value into a rowid or integer primary key column, the system chooses an integer value to use as the rowid automatically. A detailed description of how this is done is provided separately.
The parent key of a foreign key constraint is not allowed to use the rowid. The parent key must used named columns only.

About SQLite


SQLite is a in-process library that implements a self-containedserverlesszero-configurationtransactionalSQL database engine. The code for SQLite is in the public domain and is thus free for use for any purpose, commercial or private. SQLite is currently found in more applications than we can count, including severalhigh-profile projects.
SQLite is an embedded SQL database engine. Unlike most other SQL databases, SQLite does not have a separate server process. SQLite reads and writes directly to ordinary disk files. A complete SQL database with multiple tables, indices, triggers, and views, is contained in a single disk file. The database file format is cross-platform - you can freely copy a database between 32-bit and 64-bit systems or between big-endian andlittle-endian architectures. These features make SQLite a popular choice as an Application File Format. Think of SQLite not as a replacement for Oracle but as a replacement for fopen()
SQLite is a compact library. With all features enabled, the library size can be less than 350KiB, depending on the target platform and compiler optimization settings. (64-bit code is larger. And some compiler optimizations such as aggressive function inlining and loop unrolling can cause the object code to be much larger.) If optional features are omitted, the size of the SQLite library can be reduced below 200KiB. SQLite can also be made to run in minimal stack space (4KiB) and very little heap (100KiB), making SQLite a popular database engine choice on memory constrained gadgets such as cellphones, PDAs, and MP3 players. There is a tradeoff between memory usage and speed. SQLite generally runs faster the more memory you give it. Nevertheless, performance is usually quite good even in low-memory environments.
SQLite is very carefully tested prior to every release and has a reputation for being very reliable. Most of the SQLite source code is devoted purely to testing and verification. An automated test suite runs millions and millions of test cases involving hundreds of millions of individual SQL statements and achieves 100% branch test coverage. SQLite responds gracefully to memory allocation failures and disk I/O errors. Transactions are ACID even if interrupted by system crashes or power failures. All of this is verified by the automated tests using special test harnesses which simulate system failures. Of course, even with all this testing, there are still bugs. But unlike some similar projects (especially commercial competitors) SQLite is open and honest about all bugs and provides bugs lists including lists of critical bugs and minute-by-minute chronologies of bug reports and code changes.
The SQLite code base is supported by an international team of developers who work on SQLite full-time. The developers continue to expand the capabilities of SQLite and enhance its reliability and performance while maintaining backwards compatibility with the published interface specSQL syntax, and database file format. The source code is absolutely free to anybody who wants it, but professional support is also available.
We the developers hope that you find SQLite useful and we charge you to use it well: to make good and beautiful products that are fast, reliable, and simple to use. Seek forgiveness for yourself as you forgive others. And just as you have received SQLite for free, so also freely give, paying the debt forward.