When PostgreSQL executes a SQL statement, a fair amount of work must be done to determine how the statement should be executed. When executing many very similar statements that differ only in the values used, such as in search criteria for SELECT statements, this can be very inefficient. This is because of the overhead PostgreSQL incurs each time it must parse and determine an execution plan from the SQL. Just as we can with host variables in embedded C (discussed in Chapter 14) and with prepared statements in Java (discussed in Chapter 17), we can also generate SQL statements with parameters using Npgsql.
First, we need to look at the NpgsqlParameter class, which lets us create parameters.
Creating Parameters with the NpgsqlParameter Class
The NpgsqlParameter class is used to create parameter variables, which can be associated with a SQL statement in an NpgsqlCommand object. It’s a relatively simple class; we really need to concern ourselves only with the parameter name and type, which are generally just passed in when the object is constructed. Table 18-9 lists the NpgsqlParameter properties.
Generally, you will only need to use the constructor, which has many signatures, permit- ting most of the properties to be set as the object is constructed. The constructor format you are mostly likely to need is as follows:
NpgsqlParameter(string parametername, NpgsqlDbType ptype)
The NpgsqlDbType is simply an enumeration of the possible data types. The principal elements are Boolean, Date, Double, Integer, Numeric, Real, Smallint, Text, Time, and Timestamp.
Creating Statements with Parameters
Parameters are sometimes useful even for statements that aren’t executed many times, as they can simplify construction of the SQL statement. They are also an important step on the way to prepared statements. To create a SQL statement that has parameters, we replace the actual value in the SQL string with a variable name, which must start with a colon. Here is an example:
SELECT * FROM customer WHERE customer_id = :cid
We can then bind the variable name to an NpgsqlParameter object, which has the name of the variable and the data type. For example, where cmd is an NpgsqlCommand object, we could replace a parameter :cid with a 32-bit integer parameter like this:
cmd.Parameters.Add(new NpgsqlParameter("cid", DbType.Int32));
These steps need to be performed only once for each SQL string.
Table 18-9. NpgsqlParameter Properties
Property Meaning
DbType Gets or sets the parameter type
Direction Indicates if the parameter is input-only, output-only, or bidirectional IsNullable Indicates if NULL values are allowed
NpgsqlDbType Gets or sets the type of the parameter
ParameterName Gets or sets the name of the parameter variable Precision Gets or sets the maximum number of digits Scale Gets or sets the number of decimal places Size Gets or sets the maximum in bytes of the column Value The actual data value to be used
Last, but not least, we replace the parameter with an actual value, which can be done many times in a program:
cmd.Parameters[0].Value = 2;
We can see this in practice in the following program, Getdata3.cs. The key lines are highlighted.
// getdata3.cs - a retrieve of data from the customer table using parameters using System;
using System.Data;
using Npgsql;
public class connect {
public static void Main(String[] args) { NpgsqlConnection conn = new NpgsqlConnection(
"Server=192.168.0.3;User Id=rick;Password=password;Database=bpfinal;");
try {
conn.Open();
NpgsqlCommand cmd = new NpgsqlCommand(
"SELECT * FROM customer WHERE customer_id = :cid OR fname = :fn", conn);
cmd.Parameters.Add(new NpgsqlParameter("cid", DbType.Int32));
cmd.Parameters.Add(new NpgsqlParameter("fn", DbType.String));
cmd.Parameters[0].Value = 2;
cmd.Parameters[1].Value = "Jenny";
NpgsqlDataReader datard = cmd.ExecuteReader();
while (datard.Read()) {
for (int i = 0; i < datard.FieldCount; i++) { Console.Write("{0}, ", datard[i]);
}
Console.WriteLine();
} }
finally { conn.Close();
} } }
Notice that we must be careful to get our parameters in the correct order as we move from replacing the variable names with parameter objects and then actual values.
Creating Prepared Statements
Now that we understand how to replace variables in SQL statements with actual values, it’s only a small step to see how we can then prepare the statement once, change the values, and reexecute it, without the database needing to reprocess the statement.
The Getdata4.cs script adds to the previous code, reusing a previously prepared statement with different values. Key lines are highlighted.
// getdata4.cs - a retrieve of data from the customer table using // parameters and prepared statements
using System;
using System.Data;
using Npgsql;
public class connect {
public static void Main(String[] args) { NpgsqlConnection conn = new NpgsqlConnection(
"Server=192.168.0.3;User Id=rick;Password=password;Database=bpfinal;");
try {
conn.Open();
NpgsqlCommand cmd = new NpgsqlCommand(
"SELECT * FROM customer WHERE customer_id = :cid OR fname = :fn", conn);
cmd.Parameters.Add(new NpgsqlParameter("cid", DbType.Int32));
cmd.Parameters.Add(new NpgsqlParameter("fn", DbType.String));
cmd.Prepare();
cmd.Parameters[0].Value = 2;
cmd.Parameters[1].Value = "Jenny";
NpgsqlDataReader datard = cmd.ExecuteReader();
while (datard.Read()) {
for (int i = 0; i < datard.FieldCount; i++) { Console.Write("{0}, ", datard[i]);
}
Console.WriteLine();
}
datard.Close();
cmd.Parameters[0].Value = 3;
cmd.Parameters[1].Value = "Adrian";
datard = cmd.ExecuteReader();
while (datard.Read()) {
for (int i = 0; i < datard.FieldCount; i++) { Console.Write("{0}, ", datard[i]);
}
Console.WriteLine();
} }
finally { conn.Close();
} } }
We prepare the statement before executing it the first time, and we can then simply change the values of parameters, without needing to rebind them to NpgsqlParameter objects, and reexecute the statement.