Execute, VExecute,
AExecute and CExecute methods
Executes one or more SQL statements. This are the most used methods
of the SQLite3 COM. Through it all the database operations are
performed. See the Remarks section below for more details over the
different applications of the Execute method.
Syntax:
Set result = object.Execute(query [, firstrow [, rowcount]])
Set result = object.VExecute(query , firstrow , rowcount [, val1 [,
val2 [, ...]]])
Set result = object.AExecute(query , array, [firstrow [, rowcount
]])
Set result = object.CExecute(query , collection, [ firstrow [,
rowcount ]])
Parameters:
query - String containing one or more SQL queries to be
executed. The separate queries (if more than one is passed) must be
separated with ";". For VExecute, AExecute and CExecute
the query may contain one or more parameters in one of the following
forms:
?NNN |
|
A question mark followed by a number NNN holds a spot for the
NNN-th parameter. NNN must be between 1 and 999. Note that the numbers
are interpreted as names in SQLite3 COM. |
? |
|
A question mark that is not followed by a number holds a spot for the
next unused parameter. |
:AAAA
or
$AAAA |
|
A colon or a dollar-sign followed by an identifier name holds a spot for a named
parameter with the name AAAA. Named parameters are also numbered. The
number assigned is the next unused number. To avoid confusion, it is
best to avoid mixing named and numbered parameters. Each name is
assigned an unique index, thus you can repeat the same parameter more
than once in the query and pass only one value through the method.
The name of the argument does not include the $ or : character (for
CExecute)
|
VExecute can be called
with any number of arguments above 3. The 4-the argument of the
function is the 1-st query parameter, the 5-th is the 2-nd and so
on. Any value can be passed as argument. It will be converted to the
closest suitable database type and
substituted in the query.
AExecute is like
VExecute, but instead of variable number of arguments it expects an
array as second argument and uses its 0 element as 1-st query
parameter, 1-st element as 2-nd query parameter and so on.
CExecute expects as 2-nd
argument a collection. For example you can pass a VarDictionary
collection and ADO Fields collection or any other collection that
meets the requirements (see below). As the collection's elements
have names this is the only method that makes use of the named
arguments by their name. It is strongly recommended to use only
named parameters with CExecute, but if you do not have a choice see CollectionsBaseIndex
property for instructions on how to configure its behavior for
different kinds of collections.
The named arguments are useful not only with CExecute. Even when
used with the other two methods that substitute parameters by index
the named parameters enable you to put one parameter more than once
in the query.
When the number of the supplied arguments (VExecute) or the
number of the elements of the array (AExecute) and the collection (CExecute)
does not match the number of the parameters in the query NULL is
used for the parameters that have no matching values. If the
arguments (VExecute)/elements (AExecute, CExecute) are more than the
parameters actually used in the query some of them remain unused and
no error occurs. This behavior enables you to execute several
queries with a single call to a given method and pass generalized
list of parameters.
The parameter substitution is done separately for each SQL
statement passed to the method. I.e. if the query parameter contains
more than one SQL statement the substitution (and respectively the
indices of the parameters) is made the same way as if you have
called the method several times with each query separately.
firstrow - If the executed query(ies) return results this
tells the method from which row of the result will be fetched.
Default is 1 which is the result will be composed beginning with the
first result row.
rowcount - If the firstrow is set this specifies how many
rows of the actual resulting data will be fetched in total. If the
firstrow is 1 and rowcount is 0 (default) all the resulting data is
fetched. A combined usage of the both optional parameters allows
very convenient usage in scenarios where the results must be shown
in pages containing limited number of entries.
array - AExecute only. An array of values for the
parameters used in the queries. The elements are substituted as
follows - 0 element goes into the 1-st parameter, 1-st element to
the 2-nd parameter and so on. If the elements are not enough for all
the parameters NULL-s are used for the remaining portion.
collection - CExecute only. Almost any collection object
can be used as parameter source. The named parameters from the query
are searched in the collection by name and the unnamed parameters
are searched by index. This makes this the most powerful
parameterization technique and also provides an easy way to pass
values for a query parameters from external objects. For instance
an application that uses both ADO and SQLite3 COM can pass directly
an ADO recordset row (ADO Fields collection).
What kind of collections can be used as argument? The collection
object must have a default indexed property which should support
both extracting elements by name and by index. And this is the only
requirement. Some collections use 1-based indexing (VarDictionary,
ASP Request collections), others use 0-based indices (ADO recrodset).
To overcome this problem SQLite3 COM supports the property CollectionsBaseIndex
where you can specify the collection's base index. By default
1-based indexing is assumed.
Return value: result is a collection of
collections implemented by VarDictionary
objects. Thus the result itself is a VarDictionary object which
contains one item for each returned result row which in turn is
again VarDictionary object.
For example assume we have 3 records in a table named T and db is
SQLite COM object created and initialized earlier, then:
Set r = db.Execute("SELECT * FROM T")
Will return collection r which contains 3 items which are
collections too. Each of them will represent one result row. The
following sample code will iterate throughout the entire result:
' Cycle through all the rows
For I = 1 To r.Count
' For each row cycle through all the fields.
For J = 1 To r(I).Count
Response.Write "Field(" & r(I).Key(J) & "=" & r(I)(J) & ";"
Next
' Place a line between the rows to make them more readable.
Response.Write "<HR>"
Next
Se more details in the Remarks section and
in the Execute results page.
Examples:
The fields in each result row can be addressed by index or by name
as preferred. For example if the table T has the following structure
and data in it:
ID |
Name |
Age |
1 |
John |
12 |
2 |
Smith |
16 |
4 |
Anna |
11 |
We can use the following code to display it:
Set db = Server.CreateObject("newObjects.sqlite.dbutf8")
If db.Open(Server.MapPath("/db/mydb.db")) Then
Set r = db.Execute("SELECT * FROM T")
%>
<TABLE>
<TR>
<TH>Name</TH>
<TH>Age</TH>
</TR>
<%
For Row = 1 To r.Count
%>
<TR>
<TD><%= r(Row)("Name") %></TD>
<TD><%= r(Row)("Age") %></TD>
</TR>
<%
Next
%>
</TABLE>
<%
Else
%>
<H4>Cannot open the database</H4>
<%
End If
Suppose we have one more linked table that contains the e-mail
addresses for each of these persons:
PersonID |
EMail |
1 |
john@yahoo.com |
3 |
anna@hotmail.com |
1 |
john@hotmail.com |
2 |
smith@yahoo.com |
3 |
ann@mail.com |
If we want to generate a list where the number of the e-mail
addresses each person owns is listed we can use a query like this
one:
SELECT T.Name AS Name, Count(EMail) AS Emails FROM T LEFT OUTER JOIN E ON T.ID=E.PersonID GROUP BY
T.ID;
Where it is convenient to name the result fields explicitly using
AS SQL keyword. As the names of the fields in the result
collections are fetched from the result they will have the names we
set in the query and the above sample code can be changed this way
to produce the list:
Set db = Server.CreateObject("newObjects.sqlite.dbutf8")
If db.Open(Server.MapPath("/db/mydb.db")) Then
Set r = db.Execute("SELECT T.Name AS Name, Count(EMail) AS Emails " & _
"FROM T LEFT OUTER JOIN E ON T.ID=E.PersonID GROUP BY T.ID;")
%>
<TABLE>
<TR>
<TH>Name</TH>
<TH>Number of e-mail addresses</TH>
</TR>
<%
For Row = 1 To r.Count
%>
<TR>
<TD><%= r(Row)("Name") %></TD>
<TD><%= r(Row)("Emails") %></TD>
</TR>
<%
Next
%>
</TABLE>
<%
Else
%>
<H4>Cannot open the database</H4>
<%
End If
The results returned in these samples are small - only a few
rows. When we expect bigger results non-applicable for full listing
in a single page we may need to generate pages and show on each page
let say 10 records. The ability to run more than one query in a
single call to Execute allows us to fetch the total number of the
records and the records we need in one step. For example if we pass
to Execute the following queries:
SELECT Count(ID) AS RecordCount FROM T;
SELECT Name, Age FROM T ORDER BY Name;
We will receive mixed result. Its first row will contain only one
field - RecordCount which will tell us how many records we
have in total and the following rows will contain actual results.
Using the optional parameters of execute we can fetch the results
for the current page only (suppose we have much more records in
table T - for example hundreds). Appropriate call to the Execute
will look like this:
' Lets initialize the pageNumber with 0 if Page parameter does not exist or with the parameter's
' value if it is passed.
If Request("Page").Count > 0 Then pageNumber = Request("Page") Else pageNumber = 0
query = "SELECT Count(ID) AS RecordCount FROM T;"
query = query & "SELECT Name, Age FROM T ORDER BY Name;"
Set r = db.Execute(query,pageNumber * 10 + 2, 11)
Why 11 rows when we want 10 per page? Because the first row comes
from the first query always and it always returns only one-row
result. Also because of this we add 2 to the firstpage
argument instead of 1 - in order to include it into the result as
well. Also note that we assume 0-based page number in this sample
code. Then to list the current page we need code like this:
<TABLE>
<TR>
<TH>Name</TH>
<TH>Age</TH>
</TR>
<%
For Row = 2 To r.Count
%>
<TR>
<TD><%= r(Row)("Name") %></TD>
<TD><%= r(Row)("Age") %></TD>
</TR>
<%
Next
%>
</TABLE>
We begin with the second row of the result as the first row
contains the result of the first query which is the total number of
the result records.
To generate a set of links invoking the same page with a
parameter that specifies the page number to be displayed we may do
something like this:
For I = 0 To r(1)("RecordCount") / 10 + 1
%>
<A HREF="<%= Server.ServerVariables("SCRIPT_NAME") %>?Page=<%= I %>">
Page <%= I + 1 %>
</A>
Next
Which will display links: Page 1
Page 2 ... and so on.
In code like the above if a page number bigger than the last page
is specified no error will occur - just the results will be 0 - e.g.
only one row will be returned by the Execute and it will contain the
records count calculated by the first query.
Using parameters
Most of the queries an application would use cannot be composed
statically. Virtually all the applications need to compose at least
part of the WHERE clauses of the SELECT queries at run time. For
example add clause to compare an ID with a variable or something more
complicated. Most applications also need to compose INSERT, UPDATE
queries and put the new values in them.
Of course this can be done by constructing a string. The StringUtilities
object can help by enabling the application to format the query as
string. Still, there is a better way in SQLite3 COM. For example:
Set r = db.VExecute("INSERT INTO T (NAME,AGE) VALUES
(?,?)",1,0,"Nick",23)
will add a new record to the table T where the NAME column will
contain "Nick" and the Age column will be set to 23.
Thus by placing ? (question mark) in every place you want to
substitute an external value you can link variables from the
application to the query. The only inconvenience with VExecute is that
you always need to specify the firstrow (argument 2) and the rowcount
(argument 3). The method allows variable number of arguments - one for
each parameter used in the query.
What if you want to put the same parameter in more than one place
in the query? If we have a table T1 that has columns for the First,
Middle and the Family names of some people.
NameWeDoNotWant = "Smith"
Set r = db.VExecute("SELECT * FROM T WHERE First != $Name AND
Middle != $Name AND Family != $Name",1,0,NameWeDoNotWant)
This will extract all the people not having Smith as their First,
Middle or Family name. We passed only one variable but in the
statement we used the same named parameter 3 times. As it is the only
parameter all the 3 occurrences will refer to the same parameter - 1
Many times you will have the values you want to substitute in the
query in an array or a collection. They may be result of the work of
the other parts of the application, returned by an object you have
called previously and so on. To illustrate this here we will fill them
manually:
Dim a(1)
a(0) = "John"
a(1) = "Joan"
Set r = db.AExecute("SELECT * FROM T WHERE NAME != $Name1 AND
NAME != $Name2",a)
This will extract only the persons not named John or Joan. The same
result can be achieved without named parameters:
Set r = db.AExecute("SELECT * FROM T WHERE NAME != ? AND NAME
!= ?",a)
And we can make real use of the parameter names:
Set v = Server.CreateObject("newObjects.utilctls.VarDictionary")
v.Add "MyParam1", "John"
v.Add "MyParam2", "Joan"
Set r = db.CExecute("SELECT * FROM T WHERE NAME != $MyParam1 AND
NAME != &MyParam2",v)
A trivial example where you can see the effect can be done with
SELECT statement without FROM clause:
Set r = CExecute("SELECT $MyParam1 AS P1, $MyParam2 AS
P2",v)
By changing the contents of the collection you can see how the
query behaves and where the values will appear in the result. For
instance the above will return:
The last insert ID
The returned result contains more useful information. For example
the Info property of the result contains the last insert ID and for
example:
Set r = db.Execute("INSERT INTO T (Name,Age) VALUES ('Jack',16)")
lastid = r.Info
Set r = db.Execute("INSERT INTO E (PersonID,EMail) VALUES (" & lastid & ",'" & strEmail & "');
It can be used to determine the id for the next insert in a
linked table. If there is more than one INSERT statements in the
executed query the ID from the last one is returned. If there are no
INSERT statements in the executed query Info is 0 or the ID of a
previously executed INSERT statement. The application should rely on
this value only after execution of an INSERT SQL statement.
The names of the fields
Each row of the result is a VarDictionary
collection which contains named elements - one element per each
field in the order defined by the corresponding query in the string
passed to Execute, CExecute, VExecute or AExecute. To learn the field name, knowing the field index
you can use the Key property:
fieldname = r(row).Key(field)
Where the row is the row number and the field is
the field index. Both row number and the field index are 1-based.
The count of the rows in the result and
the number of the fields in a row
Each VarDictionary collection has property Count which returns
the count of the elements in it. Therefore the Count of the result (r
from the samples above) will be the total number of rows in the
result. The Count property of each row collection will return the
number of the fields in the row:
rowcount = r.Count ' Obtain the number of the rows in the
result
fieldcount = r(n).Count ' The number of the fields in the n-th
result row.
The type of the data contained in each
field
Each row contains information about the field types in its Info
property. The information can be returned as a collection of type
indicators (constants or names) one per each field or as semi-colon separated
list of type names or as a collection with number of elements equal
to the number of the fields. See details in the Execute
results.
By default the type of the returned fields is returned as type
constants. The type of a field is obtained:
t = r(R).Info(F)
Where R is the row index and F is the field index or name in that
row. See also the more detailed explanation and the constant values
in Execute results.
Upper/Lower case issues. As the SQLite database engine is
designed to work without need of local data it is not able to
perform upper/lower case operations over non-ASCII characters. If
you want case insensitive operations it is recommended to transform
the strings to upper case (or to lower case if you prefer) when
putting them into the db. Such a support will require large tables
which may become much bigger in size than the DLL itself and are not
planned for the near future. On the other hand the local machine may
not have all the data for all the languages and it is impossible to
relay on it for every language.
How to obtain more useful information? For example to
obtain the field definitions for a table you can use the:
PRAGMA table_info(table-name);
Which will return a SELECT-like result with the following
columns:
cid - the field id
name - the field name
type - the type specified
notnull - 0/1 - 1 means the field must not be null
dflt_value - the default value for the field (if present)
pk - 0/1 - 1 means the field is primary key
There are also other useful PRAGMA
directives you can use in various situations.
How to create auto-increment ID field? One column in the
table definition may be declared as of the type: INTEGER PRIMARY
KEY. This makes it auto-increment key field.
How to get handle of a record if the table has no appropriate
primary key field? In any query you can use one of the following
internally defined key field names: ROWID", "OID",
or "_ROWID_". All they do the same - they are the
internal row id-s. If an INTEGER PRIMARY KEY is defined in the table
definition it is the same as any of the above fields. If no such
field exists one of these can be used in any query to construct
WHERE clause or another expression where handle of the row is
needed. For example:
r = db.Execute("SELECT OID,* FROM E WHERE EMail LIKE '%yahoo%;")
For I = 1 To r.Count
Set r1 = db.Execute("UPDATE E SET EMail = 'removed' WHERE OID=" & r(I)("OID"))
Next
will change the yahoo emails to "removed" (of course
this can be done in a simple query - but here purpose is to show how
OID can be used to address a row).
How to simplify the SQL statements generation? You can use
the Sprintf or SQprintf methods of the StringUtilities
object to generate strings over specified format and variable set of
arguments. For example an insert SQL statement for the table T from
the above samples can be generated this way:
sql = Sprintf("INSERT INTO T (Name,Age) VALUES (%q,%u);",Request.Form("Name"),Request.Form("Age"))
Which we suppose is called with the parameters entered in a form
submitted to this page. As the entered values may contain ' apostrophes
(for example O'Hara) we need to escape them. the %q escape sequence
used above will not only double any apostrophes in the string but
will also automatically enclose it with apostrophes. For example if
we make this call:
sql = Sprintf("INSERT INTO T (Name,Age) VALUES (%q,%u);","O'Hara',23)
we will receive this string:
"INSERT INTO T (Name,Age) VALUES ('O''Hara',23);"
What to do with date/time values? There is no special
support for date/time values but you can get what you need. There
are several ways depending on what functionality will be needed. For
example if only sorting will be needed then you can use textual
values. If you use date format like this one "Year-Month-Date
Hour:Minute:Seconds" this will sort fine but you will not be
able to use operations over the dates. One of the bes
Applies to: SQLite COM
object
See also: Open, SQL
syntax reference, Pragma reference
Supported on:
Windows 95/NT and later
Windows CE 3.0 and later
Pocket PC/Windows Mobile 2003 and later
Windows CE.NET 4 and later
|