Wednesday, September 10, 2008

[vb6] Why ADO's Find method is the devil

Takeaway: How do you use ADO's Find method with multiple criteria? Easy. You don't. But don't fret, you should be using Filter anyway. Take a look at this superior alternative.

Q: How can I get ADO's Find method to find records based on the values in two different columns?

—Ali Siasifar


A: Well, the short answer is that you can't. ADO's Recordset.Find method works on one column, and one column only. Having said that, you could use something like the code in Listing A, which finds all records in a hypothetical table where the City and Country fields are "London" and "UK," respectively. It does this by finding the first record where Country is "UK" and checking the City field to see if it equals "London." If it does, we have found one of the records we are looking for. If not, we look for the next record where Country is "UK" and try again.

I have to ask, though, why are you using Find? Although Find is appropriate in some situations, using it to locate records is generally very inefficient, both in terms of speed and memory use. Find works by examining each record in a Recordset for the criteria you give it after you have created the Recordset and retrieved all the data from your database. Retrieving lots of unwanted records, particularly down the wire from a server, when you are really interested in only a handful—or even worse, just one bit of data—creates a lot of unnecessary overhead. Unless you have a very compelling reason for using Find, I'd recommend using an alternative approach, like filtering your Recordset or using SQL, for serious performance gains.

Filtering
The Recordset.Filter method filters out records that don't match one or more criteria. You can specify multiple conditions in a filter by using and or or, so using Filter would allow you to check multiple columns. For example, suppose we open a Recordset and execute the following code:
 
rs.Filter = "LastName='Adams' and FirstName='Lamont'"

The Recordsetrs would then contain only records where the LastName and FirstName fields were Adams and Lamont, respectively. If no records existed that match these criteria, rs would be empty, with rs.EOF and rs.BOF both true. To remove a filter from a Recordset, set the Filter property to an empty string ("") or the constant adFilterNone.

Filter does have its drawbacks, of course. Filtering a Recordset requires the creation of a new cursor and makes the "topmost" record in the filtered set the current record. Like Find, a Filter occurs on the client side after the Recordset has been created and data retrieved from the database. So once again, you could be retrieving more data that you need. All these factors contribute to make filtering a Recordset fairly expensive.

Unlike Find, however, filtering does makes sense in some circumstances, such as when you’re working with a large set of data on which you expect to make quite a few filter operations. In situations such as this, the overhead involved in creating multiple Recordsets retrieving small amounts of data may be less than that involved in one trip and multiple filters.

Restricting the results with SQL
Your last option would be to use some kind of a SQL SELECT statement to ensure that the Recordset contained only records with the values you are looking for. SQL processing is done by the data provider and is generally the fastest way to retrieve a restricted set of data because the only real overhead is the opening of the Recordset, which you have to do anyway. Most database systems interpret SQL with blinding speed, at least until you throw multiple joins or subqueries into the mix, so any extra processing time is negligible. Also, by retrieving only the records you want, you cut the waste and overhead of retrieving an entire table of data and filtering or using Find to locate the desired records.

I don’t have space in this column for a class in SQL, but every developer should have at least a basic understanding of it. I will explain, though, just for clarity, that assuming a table name of Customers, the SQL equivalent of the Filter we performed above would be:

rs.Open "SELECT * FROM Customers " _
& "WHERE LastName='Adams' AND " _
& "FirstName='Lamont';", cn

where cn is an open connection object.

A picture is worth 50 records
Throughout this column, I've been stressing that ADO’s Find and Filter methods are inefficient compared to restricting your result set with SQL. But how inefficient are they, exactly? I did a little informal benchmarking on the performance differences of locating multiple records using Find, Filter, and a SQL statement. The results, shown in Figure A, provide a little concrete data to back me up.

Figure A
SQL outperformed Find and Filter in my informal benchmarking.


The time results represent the average number of seconds needed to use a client-side, static cursor to open the Customers table in SQL Server's sample Northwind catalog and locate all records belonging to customers in London. I ran 50 trials using ADO version 2.6. These results will be a bit faster than real-world performance, since the data provider cached the same Recordsets that were opened, closed, and reopened in rapid succession for this rough test. But the differences in speed are still telling, particularly when operating over a network. Keep these results in mind the next time you need to find a few records and you're working with ADO.

Sunday, August 31, 2008

[messanger] Ten Must-Have Plug-ins to Power Up Pidgin

Cross-platform, multi-protocol instant messaging application Pidgin is very functional, but by default it's not what most people would call sexy. But like many great software projects, from Firefox to foobar2000, the looks and extra functionality are there—you just have to know where to find them. Today we're taking a look at 10 of the best Pidgin plug-ins for taking Pidgin from drab to fab.

NOTE: Unless linked elsewhere, the plug-ins listed are included with the default installation of Pidgin—it's just a matter of turning them on in Tools -> Plugins and, in some cases, configuring them.

Stay Smart Over IM with Text Replacement


text-replacements.pngIt's easy to get sloppy on the grammar when you're chatting, but the Text Replacement plug-in's autocorrection macros seamlessly correct common typos on-the-fly. If you've got a particular grammar offense that's not included in Text Replacement by default, you can add your own corrections to the list. Wish you had this functionality in every program? Check out Texter (Windows), TextExpander (Mac), or Snippits (Linux).

Encrypt Chat Sessions with Pidgin-Encrypt

pidgin-encrypt.pngKeep your IM sessions secure without hassle with the Pidgin-Encrypt plug-in. Back when Pidgin was still called GAIM, we showed you how easy it is to set up a secure chat session with this extension. To use Pidgin-Encrypt, download the installer from the Pidgin-Encrypt homepage, enable it in Tools -> Plugins, and then start a secure chat by clicking the padlock icon. UPDATE: A lot of readers are pointing out the Pidgin Off-the-Record plug-in, a more popular peer-reviewed encryption plug-in. The OTR homepage appears to be down at the moment so I haven't been able to try it out, but if you're looking for IM encryption, you may want to consider OTF rather than Pidgin-Encrypt.

Start BitTorrent Downloads through Pidgin with Autoaccept

autoaccept1.pngThe Autoaccept plug-in allows you to set rules for automatically accepting file transfers from specific users. As I pointed out in the top 10 BitTorrent tools and tricks, this feature can, among other things, be especially handy for remotely starting up BitTorrent downloads.

Keep Notified of Chats with Guifications

guifications.pngThe Guifications plug-in notifies you of user status and incoming messages with a small pop-up window in the corner of your screen, similar to how Growl works with Adium on the Mac. You can customize the look, location, and what events will trigger a Guification alert so you're only distracted by a Guification pop-up when you really want to be.

Get SMS Alerts of IMs While You're Away

If instant messaging is an extremely important part of what you do, the gSMS plug-in can keep you abreast of IMs even when you're away from your computer by sending an SMS message to your phone through Google Calendar, of all places. You'll need to download gSMS from the link above to add this plug-in to Pidgin.

Remember Where You Left Off with Enhanced History

history.pngPidgin comes with a built-in History plug-in that automatically inserts your last conversation with the current contact in your new conversation—especially handy if you're rejoining a conversation and want to pick up where you left off (after restarting your computer, for example). If that's enough for your needs, just flip the switch on it and you're set with the default plug-in. If you want more functionality, like the ability to display anywhere from zero to 255 previous conversations or limit chat histories by how long ago they occurred, the non-default Enhanced History plug-in is for you.

Save Your Chat Sessions with SessionSave

This plug-in remembers the state of your Pidgin chat windows when you quit and re-opens your conversation windows just as you left them—sort of like the session saving function built into the Tab Mix Plus Firefox extension. Coupled with one of the History plug-ins above, you won't miss a beat if you have to quickly quit Pidgin or restart your computer.

Add Now Playing Status to Pidgin

musictrac.pngIf you're terrible at setting your status or remembering to change it, let the MusicTracker plug-in update your status for you. The plug-in sets your status to whatever music you're listening to, works with tons of digital music players from iTunes and Winamp on Windows to Amarok and XMMS on Linux, and exactly what it displays is very customizable.

Get Updates on New Releases with Release Notification

If you're a Pidgin die-hard, you'll definitely want to enable the Release Notification plug-in, which periodically checks for updates to Pidgin and alerts you whenever one is available.

Theme Pidgin with the GTK Theme Selector or GTK+ Theme Control

themes-pidgin.pngPidgin runs on both Windows and Linux using the GTK graphical environment, so theming Pidgin is as easy as using GTK's built-in theming tools. Here's how it works on Windows.

First, you probably want to find a good-looking theme. A good place to start is at the Gnome Application Themes, where you can browse tons of GTK themes (like this one I'm using).

Found one? Download it and extract the contents to C:\Program Files\Common Files\GTK\2.0\share\themes (there should already be a few themes in there to give you an idea of what the results should look like). Now you just need to fire up the Gnome Theme Selector (it should be in your Start menu) and pick the theme you just installed.

Alternately, you could install the Pidgin GTK+ Theme Control plug-in, which allows you to adjust Pidgin's look directly from Pidgin.

Tuesday, August 26, 2008

Stone Age Cartoon



[vb6] Pause() function for Wait/Sleep

The best way to pause code execution is to provide a mechanism that gives the parent application such as Excel or Word opportunities to handle events as well as other operating system tasks. The routine below provides both and allows a pause of as little as a hundredth of a second.

Note that the declaration of the Sleep API function has to be placed above all other routines in the module.

[Begin Code Segment]

Public Declare Sub Sleep Lib "kernel32" (ByVal Milliseconds As Long)

Public Sub Pause( _
ByVal Seconds As Single, _
Optional ByVal PreventVBEvents As Boolean)

' Pauses for the number of seconds specified. Seconds can be specified down to
' 1/100 of a second. The Windows Sleep routine is called during each cycle to
' give other applications time because, while DoEvents does the same, it does
' not wait and hence the VB loop code consumes more CPU cycles.

Const MaxSystemSleepInterval = 25 ' milliseconds
Const MinSystemSleepInterval = 1 ' milliseconds

Dim ResumeTime As Double
Dim Factor As Long
Dim SleepDuration As Double

Factor = CLng(24) * 60 * 60

ResumeTime = Int(Now) + (Timer + Seconds) / Factor

Do
SleepDuration = (ResumeTime - (Int(Now) + Timer / Factor)) * Factor * 1000
If SleepDuration > MaxSystemSleepInterval Then SleepDuration = MaxSystemSleepInterval
If SleepDuration < MinSystemSleepInterval Then SleepDuration = MinSystemSleepInterval
Sleep SleepDuration
If Not PreventVBEvents Then DoEvents
Loop Until Int(Now) + Timer / Factor >= ResumeTime

End Sub

[End Code Segment]

The DoEvents call is used to give the managed environment such as Excel or Word opportunities to handle events and do other work. But DoEvents only works within the managed environment and can still consume a considerable amount of resources without some other throttling mechanism. By also using the Windows Sleep API call the Windows operating system is given an opportunity to let other processes run. And, since the code is doing nothing but waiting, it is the appropriate thing to do.

Often the task involves waiting for an asynchronous task to complete such as a web query. To use the above routine while waiting for such a task to compete, two time durations are needed: the total amount of time to wait until it can be assumed that a failure has occurred in the other task, and the amount of time to wait between checks that the other task has completed. Determining how long to wait until giving up requires consideration of the longest possible time that the task could reasonably take and how long the user is willing to wait for that task to complete - wait too long and the user gets frustrated, don't wait long enough and the risk increases of falsely assuming an error occurred when it didn't. This duration is the more difficult to determine of the two. The second time, the duration between checks for completion, is easier to determine. This duration should be long enough to not consume unnecessary CPU cycles doing the check, but short enough to respond quickly when the status of the asynchronous task changes. A duration of between a quarter of a second and one second is usually reasonable. The sample code below illustrates how to wait for an asynchronous task to complete that usually finishes in less than 10 seconds.

  Dim TimeoutTime As Date
TimeoutTime = Now() + TimeSerial(0, 0, 30) ' Allow 30 seconds for the asynchronous task to complete before assuming it failed
Do While Now() < TimeoutTime And Not IsTaskComplete
Pause 0.5 ' Pause half a second to allow the ashyncronous task (and the rest of the environment) to do work
Loop

The above example uses a function named IsTaskComplete to determine if the asynchronous task completed. The function can do anything such as checking if a cell changed, checking if a control's property is set, or checking if a file exists.

Bad Software Developement Cycle Illustration







Monday, July 28, 2008

Oracle connection strings

Oracle ODBC connection strings

Open connection to Oracle database using ODBC
"Driver= {Microsoft ODBCforOracle};Server=Your_Oracle_Server.world;Uid=Your_Username;Pwd=Your_Password;" Oracle OLE DB & OleDbConnection (.NET framework) connection strings Open connection to Oracle database with standard security:
1. "Provider=MSDAORA;Data Source= Your_Oracle_Database;UserId=Your_Username;Password=Your_Password;"
2. "Provider= OraOLEDB.Oracle;Your_Oracle_Database;UserId=Your_Username;Password=Your_Password;"

Open trusted connection to Oracle database
"Provider= OraOLEDB.Oracle;DataSource=Your_Oracle_Database;OSAuthent=1;"

MySQL connection strings

MySQL ODBC connection strings

Open connection to local MySQL database using MySQL ODBC 3.51 Driver
"Provider=MSDASQL; DRIVER={MySQL ODBC 3.51Driver}; SERVER= localhost; DATABASE=Your_MySQL_Database; UID= Your_Username; PASSWORD=Your_Password; OPTION=3"

MySQL OLE DB & OleDbConnection (.NET framework) connection strings

Open connection to MySQL database:
"Provider=MySQLProv;Data Source=Your_MySQL_Database;User Id=Your_Username; Password=Your_Password;"