Visual Basic Q&A

As a software engineer, I focus on .NET, especially asp.net, C#, WCF and so on, and I am also very interested in Search Engine Optimization.

Entries Tagged ‘query’

PRB: Error When You Use MS Data Shape Provider in Data Environment

Symptoms
When you try to use the Query Designer against the MS Shape Provider inthe Data Environment, the following error message is returned:

Query Designer encountered a ADODB.Properties error:
Query Designer encountered a ADODB.Properties error:
Query Designer encountered a ADODB.Properties error:
ADO could not find the object in the collection corresponding to the
name or ordinal reference requested by the application.(11)Upon clicking OK, a second error message is returned:

Object Variable or With block variable not set.
Resolution
This behavior is by design.

How To Optimize Queries in Visual Basic

Symptoms
Visual Basic allows you to retrieve data from Jet databases (MDB files) byusing Structured Query Language (SQL). These query operations can be mademore efficient by implementing some of the suggestions in this article.
This article assumes that you are using the Microsoft Jet database engine.If you are querying ODBC tables, many of these points still apply. Formore information regarding improving performance of ODBC queries, pleasesearch on the following words in the Microsoft Knowledge Base:
ODBC and Optimizing and Tables
Resolution
Here are some tips for optimizing your SQL queries:
Performance AnalyzerIf you have Microsoft Access 95 or 97, you can open the database and usethe Performance Analyzer to profile your queries and suggest improvements.
Table DesignWhen defining a field in a table, choose the smallest data typeappropriate for the data in the field. This increases the number ofrecords that can fit on a page.
Fields you use in joins should have the same or compatible data types.
Compact the DatabaseThis has two performance benefits:
The Microsoft Jet database engine uses a cost-based method ofoptimization. As your database grows, the optimization scheme may nolonger be efficient. Compacting the database updates the databasestatistics and re-optimizes all queries.As your database grows, it will become fragmented. Compacting writesall the data in a table into contiguous pages on the hard disk, improvingperformance of sequential scans.
To compact your database, use the CompactDatabase statement. This examplecompacts the database and makes a backup:

DBEngine.CompactDatabase “C:\VB\BIBLIO.MDB”, “C:\VB\BIBLIO2.MDB”Kill “C:\VB\BIBLIO.BAK”Name “C:\VB\BIBLIO.MDB” As “C:\VB\BIBLIO.BAK”Name “C:\VB\BIBLIO2.MDB” As “C:\VB\BIBLIO.MDB”
If your database is updated heavily, you may want to consider compactingnightly.
Avoid Expressions in Query OutputExpressions in query output can cause query optimization problems if thequery is used later as input to another query and you add criteria tothe calculated output. In the following example, Query1 is used as inputfor a second SELECT statement:

Dim DB As DatabaseDim RS As RecordSetSet DB = DBEngine.Workspaces(0).Opendatabase(“Biblio.MDB”)DB.CreateQueryDef(“Query1″, _”SELECT IIF(Au_ID=1,’Hello’,'Goodbye’) AS X FROM Authors”)Set RS = DB.OpenRecordSet(“SELECT * FROM Query1 WHERE X=’Hello’”)
Because the IIF() expression in Query1 cannot be optimized, the WHEREcondition in second SELECT statement also cannot be optimized. If anexpression gets buried deeply enough in a query tree, you can forget thatit is there. As a result, your entire string of queries cannot beoptimized.
If you can, merge the SQL into a single level of nesting:

Set RS = DB.OpenRecordSet(“SELECT * FROM Authors WHERE Au_ID=1″)
For more complex nested queries, expose the fields that make up theexpression:

DB.CreateQueryDef(“Query1″, _”SELECT IIF(Au_ID=1,’Hello’,'Goodbye’) AS X, Au_ID, FROM Authors”)Set RS = DB.OpenRecordSet(“SELECT * FROM Query1 WHERE Au_ID=1″)
If you cannot avoid calculated values in query output, place them in thetop-level query and not in lower-level queries.
Output Only the Fields NeededWhen creating a query, return only the fields you need. If a field doesn’thave to be in the SELECT clause, don’t add it. The above example ofexposing additional fields to make nested queries more efficient is anexception.
GROUP BY, Joins, and AggregatesThis is an issue when you are joining two tables. For example, if you jointwo tables on the Customer Name field, and also GROUP BY the Customer Namefield, make sure that both the GROUP BY field (Customer Name) and thefield that is in the aggregate (Sum, Count, and so on) come from the sametable.
NOTE: This query is less efficient because the SUM aggregate is on the Ordtable and the GROUP BY clause is on the Cust table:

SELECT Cust.CustID,FIRST(Cust.CustName) AS CustName,SUM(Ord.Price) AS TotalFROM Cust INNER JOIN Ord ON Cust.CustID = Ord.CustIDGROUP BY Cust.CustID
A more efficient query would be to GROUP BY on Ord.CustID:

SELECT Ord.CustID,FIRST(Cust.CustName) AS CustName,SUM(Ord.Price) AS TotalFROM Cust INNER JOIN Ord ON Cust.CustID = Ord.CustIDGROUP BY Ord.CustID
NOTE: The First and Last functions do not have the overhead of otheraggregates and should not weigh very heavily in this decision.
GROUP BY As Few Fields As PossibleThe more fields in the GROUP BY clause, the longer the query takes toexecute. Use the First aggregate function to help reduce the number offields required in the GROUP BY clause.
Less efficient:

SELECT Cust.CustID,Cust.CustName,Cust.Phone,SUM(Ord.Price) AS TotalFROM Cust INNER JOIN Ord ON Cust.CustID = Ord.CustIDGROUP BY Cust.CustID, Cust.CustName, Cust.Phone
More efficient:

SELECT Ord.CustID,FIRST(Cust.CustName) AS CustName,FIRST(Cust.Phone) AS Phone,SUM(Ord.Price) AS TotalFROM Cust INNER JOIN Ord ON Cust.CustID = Ord.CustIDGROUP BY Ord.CustID
Nest GROUP BY Clause Before JoiningIf you are joining two tables and only grouping by fields in one of them,it may be more efficient to split the SELECT statement into two queries.making the SELECT statement with the GROUP BY clause into a nested queryjoined to the non-grouped table in the top-level query.
Less efficient:

SELECT Ord.CustID,FIRST(Cust.CustName) AS CustName,FIRST(Cust.Phone) AS Phone,SUM(Ord.Price) AS TotalFROM Cust INNER JOIN Ord ON Cust.CustID = Ord.CustIDGROUP BY Ord.CustID
More efficient:

Query1:SELECT CustID, SUM(Price) AS TotalFROM OrdGROUP BY CustIDQuery2:SELECT Query1.CustID, Cust.CustName, Cust.Phone, Query1.TotalFROM Cust INNER JOIN Ord ON Cust.CustID = Ord.CustID
Index Both Fields Use in JoinWhen joining tables, try to index the fields on both sides of a join. Thiscan speed query execution by allowing the query optimizer to use moresophisticated internal join strategy.
However, if you know one table is going to remain relatively small (occupy1-2 2K pages), it may be more efficient to remove indexes in thattable because fewer pages will have to be read into memory. You should trythis on a case-by-case basis.
Add Indexes to Speed Searches and SortsPlace an index on all fields that are used in a join or in a restriction.With the use of Rushmore query optimization technology, the Microsoft Jet2.0 and later database engine is able to take advantage of multipleindexes on a single table, which makes indexing multiple fieldsadvantageous.
Avoid restrictive query criteria on calculated and non-indexed columnswhenever possible.
Use sorting judiciously, especially with calculated and non-indexed fields.
Use Optimizable ExpressionsTry to construct your queries so that Rushmore technology can be used tohelp optimize them. Rushmore is a data-access technology that permits setsof records to be queried very efficiently. With Rushmore, when you usecertain types of expressions in query criteria, your query will run muchfaster. Rushmore does not automatically speed up all your queries. You mustconstruct your queries in a certain way for Rushmore to be able to improvethem.
Use the REFERENCES section at the end of the article to locate morespecific information.
Use COUNT(*) Instead of COUNT([Column Name])The Microsoft Jet database engine has special optimizations that allowCOUNT(*) to be executed much faster than COUNT([Column Name]).
NOTE: These two operations also have slightly different behavior:
Count(*) counts all rows returned.Count([Column Name]) counts all rows where [Column Name] is not NULL.
Avoid LIKE on ParametersBecause the value of the parameter is unknown at the time the query iscompiled, indexes will not be used. You can gain performance byconcatenating the parameter value as a literal in the SQL statement.
Use the REFERENCES section at the end of the article to locate morespecific information.
Avoid LIKE and Leading WildcardIf you use the LIKE operator with a wildcard to find approximate matches,use only one asterisk at the end of character string to ensure that anindex is used. For example, the following criteria uses an index:

Like “Smith”Like “Sm*”
The following criteria does not use an index:

Like “*sen”Like “*sen*”
Test Joins with RestrictionsIf you use criteria to restrict the values in a field used in a join, testwhether the query runs faster with the criteria placed on the “one” sideor the “many” side of the join. In some queries, you get fasterperformance by adding the criteria to the field on the “one” side of thejoin instead of the “many” side.
Use Intermediate TablesUse SELECT INTO statements to create work tables, especially if theresults are going to be used in a number of other queries. The more workyou can do up-front, the more efficient the process.
Avoid NOT IN with SubSelectsUsing sub-selects and NOT IN is poorly optimized. Converting to nestedqueries or OUTER JOINs are more efficient. The following example findscustomers without orders:
Less efficient:

SELECT Customers.*FROM CustomersWHERE Customers.[Customer ID]NOT IN (SELECT [Customer ID] FROM Orders);
More efficient:

SELECT Customers.*FROM Customers LEFT JOIN OrdersON Customers.[Customer ID] = Orders.[Customer ID]WHERE ((Orders.[Customer ID] Is Null));

How To Create and Implement a UserConnection

Symptoms
The UserConnection Designer is one of the new ActiveX Designers includedwith Visual Basic 5.0 and higher. It allows you to wrap SQL or a StoredProcedure with code at design time so you can call it as a method at runtime. This way, Visual Basic will list the parameters of your storedprocedure in the object browser just as it does with any other VBA method.This new functionality can replace the rdoPreparedStatement and rdoQueryobjects, although these are still available.
The following is an excerpt on the UserConnection from Books Online:
The UserConnection designer uses Visual Basic’s ActiveX designerarchitecture to provide design-time support for programmatic dataaccess. It allows you to create connection and query objects atdesign time. These connections and queries are persisted as project-levelobjects. You can pre-set properties, define new properties and methods,and write code behind the objects to catch events.
Resolution
This sample uses SQL Server and the Pubs database, but you can change theDSN, UID, PWD, and SQL to match any ODBC database you may be using.
Task One: Create the Example Stored ProcedureThis sample SQL Server stored procedure accepts an input parameter ofau_id and returns a resultset and a test output variable. The testoutput variable is hard-coded and has no meaning except to demonstratehow to return an output using a UserConnection. If you are not usingSQL Server, you might have to modify the SQL into an acceptable formatfor your database server.To create this stored procedure you will need a tool that allows you toexecute SQL such as ISQL/W or MSQuery included with SQL Server. Selectthe Pubs database, then place the following Create Procedure T-SQL inthe SQL window of your selected tool and execute it:

Create procedure GetAuthorInfo@au_id Varchar(11), @testOut Varchar(10) OUTPUT AsSelect * from authors Where au_id = @au_idSelect @testOut = ‘Hello’
Task Two: Create the UserconnectionStart a new project in Visual Basic and choose “Standard EXE.” Form1 iscreated by default.From the Project menu, select Components, select the Designer tab, andthen place a check next to Microsoft UserConnection.From the Project menu, select Add ActiveX Designer, then selectMicrosoft UserConnection. This will bring up a dialog titledUserConnection1 Properties.On the Connection tab, select either a DSN or a DSN-Less connection andfill in the appropriate information. If you select DSN-less, make sureyou specify a database in the Other ODBC Attributes area withdatabase=pubs. On the Authentication tab, fill in your username andpassword, and place a check next to Save Connection Information forDesign time. On the Miscellaneous tab in the Other section, choose UseODBC Cursor Library because you have more than one Select statement inyour stored procedure.Click OK to save this information, and return to the Designer window.Press the F4 key to display the Properties window and change the Nameproperty from UserConnection1 to StoredProcs.Insert a new Query by right-clicking on StoredProcs and choosingInsert Query or by clicking on the Insert Query toolbar icon. Change thename of the Query from Query1 to GetAuthorInfo. From the Source ofQuery, choose Based on Stored Procedure, then select the storedprocedure you created earlier, GetAuthorInfo.If you select the Parameters tab, you can review the input and outputparameters. Your GetAuthorInfo Query setup is now finished. ClickOK to close the Dialog.To place code in the connection event of your UserConnection, selectView Code from the UserConnection toolbar. Choose UserConnection infrom the upper-left combo box, then choose the Connect Event from theupper-right combo box. Place the following code in the UserConnectionConnect event:

Debug.Print “Connect”
Task Three: The Userconnection CodeAdd a CommandButton, Command1, to Form1 of Project1.Paste the following code in the General Declarations section ofForm1:

Private Sub Command1_Click()Dim objSP As New StoredProcs’creatable UC objectDim objRs As rdoResultset’pointer to rdoResultsetDim lngRet As Long, strOut As String’vars to hold outputobjSP.EstablishConnection rdDriverNoPrompt’establish connectionlngRet = objSP.GetAuthorInfo(“648-92-1872″, strOut) ‘SP methodSet objRs = objSP.LastQueryResults’get results off objRsDebug.Print lngRet, strOut, objRs(0)’output example resultsEnd Sub Start the program or press the F5 key.Click the Command1 button to execute the UserConnection code. TheStored Procedure return value, output value, and the first column ofthe returned resultset will print in the Debug window.

How To Create a Parameter Query via Data Access Objects

Symptoms
This article explains how to create and use a parameter query using DataAccess Objects (DAO). A parameter query is a type of QueryDef specific tothe Microsoft Jet database engine used by Visual Basic, Microsoft Access,and other products. Parameter queries enable you to automate the processof changing query criteria. With a parameter query, you can set new valuesfor the parameters each time you run the query.
Resolution
A parameter query is created in a program by using the CreateQueryDef()function. Following is the syntax for the CreateQueryDef() function:
Set querydef = database.CreateQueryDef(name, sqltext)
querydef – a QueryDef object
database – a Database object
name- string containing query name
sqltext- string containing the SQL query text The sqltext string is optional or it can be defined by using the .SQLproperty of the QueryDef. To create a parameter query, place thePARAMETERS statement in the sqltext string. Here is the syntax for thePARAMETERS statement:
PARAMETERS parametertext datatype
parametertext – name of the parameter
datatype- type of the parameter The following table lists the appropriate Microsoft Jet SQL data typethat should be used with the PARAMETERS statement as well as thecorresponding Microsoft Access field type, Visual Basic variable type,and constant value from the DATACONS.TXT file.
NOTE: Visual Basic 4.0 and later and Office 95 and later versions of Basicsupport additional data types (Boolean, Byte, Byte Array). The table belowlists equivalent Basic types for earlier versions of Basic:

MicrosoftMicrosoftVisualAccess SQLAccess FieldBasic TypeDATACONS.TXT Constant———————————————————————BitYes/NoInteger/BooleanDB_BOOLEAN = 1ByteByteInteger/ByteDB_BYTE = 2ShortIntegerIntegerDB_INTEGER = 3LongLong IntegerLongDB_LONG = 4CurrencyCurrencyDoubleDB_CURRENCY = 5IEEESingleSingleSingleDB_SINGLE = 6IEEEDoubleDoubleDoubleDB_DOUBLE = 7DateTimeDate/TimeVariantDB_DATE = 8BinaryBinaryString/Byte ArrayTextTextStringDB_TEXT = 10LongBinaryOLE ObjectString/Byte Array DB_LONGBINARY = 11LongTextMemoStringDB_MEMO = 12 Following the PARAMETERS statement in the sqltext string, place the query.The query can refer to the parameter (parametertext) named in thePARAMETERS statement. Wherever the query refers to a parameter, the currentvalue will be substituted when the query is executed.
For example, if the query text is:
PARAMETERS i SHORT; SELECT fld FROM tbl WHERE fld=i and the parameter i was set to 42 in the program, the parameter i would besubstituted and the resulting query would be equivalent to:
SELECT fld FROM tbl WHERE fld=42
Multiple Parameters in a PARAMETERS Statement It is also possible to have multiple parameters in a PARAMETERS statement.To do this, use commas to separate the parameters as follows:
PARAMETERS parametertext datatype, parametertext datatype, … Prior to executing the query, set the parameters using this syntax:
querydef!parametertext = value
querydef- a QueryDef objectparametertext – the name of the parameter in the PARAMETERS statementvalue- the value the parameter will have In the previous example, you would use QD!i=42 before executing the query.
Once the parameters are set, you are ready to execute the query. There arethree methods (Execute, CreateDynaset, and CreateSnapshot) supported by aQueryDef that will cause the query to be executed.
Example Parameter Queries The following example illustrates the use of a short parameter in a query.The example has two parts. The first part creates a new QueryDef forBIBLIO.MDB (the sample Microsoft Access database that ships with VisualBasic) and should be executed only once. The second part uses the QueryDefto create a snapshot, which is then displayed. To test the example, placeeach of the following code segments in a CommandButton Click eventprocedure.
NOTE: There are some minor differences between the older DAO syntax used byVisual Basic 3.0 and Access 1.x and the newer syntax used by the otherproducts this article applies to. Old and New DAO syntax are indicated bycomments:

‘Create QueryDef “by date”Dim Db As DatabaseDim Qd As QueryDefSet Db = OpenDatabase(“C:\VB\BIBLIO.MDB”)’ OldSet Db = DBEngine(0).OpenDatabase(“C:\VB4-32\BIBLIO.MDB”) ‘ NewSet Qd = Db.CreateQueryDef(“By date”) ‘Create the query “By date”QdText = “PARAMETERS dp Short; “QdText = QdText & “SELECT * from Titles WHERE [Year Published] = dp”Qd.SQL = QdTextPrint Qd.SQLQd.Close’ Create Snapshot from QueryDefDim Rs As Snapshot’ OldSet Qd = Db.OpenQueryDef(“By Date”)’ OldDim Rs As Recordset’ NewSet Qd = Db.QueryDefs(“By Date”)’ NewQd!dp = 1991′Set the value of the dp parameterSet Rs = Qd.CreateSnapshot()’ OldSet Rs = Qd.OpenRecordset(dbOpenSnapshot)’ NewDo Until Rs.EOFFor i = 1 To Rs.Fields.Count – 1Print Rs(i);’Display results of queryNextPrintRs.MoveNextLoopRs.CloseQd.CloseDb.CloseThe second example shows how to use an action parameter query. Note thataction queries are invoked with the Execute method, not CreateDynaset orCreateSnapshot, because they do not return records:

‘Create QueryDefDim Db As DatabaseDim Qd As QueryDefSet Db = OpenDatabase(“C:\VB\BIBLIO.MDB”)’ OldSet Db = DBEngine(0).OpenDatabase(“C:\VB4-32\BIBLIO.MDB”) ‘ NewSet Qd = Db.CreateQueryDef(“Delete by name”) ‘Create the queryQdText = “PARAMETERS p1 Text; “QdText = QdText & “DELETE * FROM Authors WHERE Author = p1;”Qd.SQL = QdTextPrint Qd.SQLQd.Close’ Execute the QueryDefSet Qd = Db.OpenQueryDef(“Delete by name”)’ OldSet Qd = Db.QueryDefs(“Delete by name”)’ NewQd!p1 = “Bob”Qd.Execute’Perform the action queryQd.CloseDb.Close