| Sam Allen's profileDOT NET PERLSBlogListsSkyDrive | Help |
|
|
Numbers to DateTime, to Database EfficiencyI spent the afternoon reading the books "Expert SQL Server 2005 Development" and the "SQL Cookbook" by O'Reilly. I got my books and a drawing board and a piece of paper and a pen and did some old fashioned studying. It is nice to think about programming and not be at the computer. I don't like laptops due to (lack of) screen size and I much prefer mice, so I can't take a laptop around and use that. What I wanted to do today was learn the ins and outs of the DateTime data type in SQL Server 2005/SQL Compact Edition databases. Now, I had designed a content management system that used a bunch of integers for dates: Month, Day, and Year. This was okay at first but I could do better. SQL Server has a very handy DateTime data type to use—the hard part is knowing exactly how to use it. The syntax, the nitty-gritty details, etc. Well, that's what I tackled and I have gotten a good start on it now. Here's the first thing to know: SQL DateTime fields always contain both a date and a time. So if you try to compare a SQL cell to DateTime.Now, you will fail. The trick is to use the greater than, less than, and >= and <= operators. To find a DateTime on a specific date, your WHERE clause can look like this: WHERE DateTimeColumn >= @dateTimeToday AND DateTimeColumn < @dateTimeTodayPlusOne Define the value of @dateTimeToday to be DateTime.Today and @dateTimeTodayPlusOne to be DateTimeToday.AddDays(1). I will include an example code snippet at the end. In my program, I wanted "Go To Next Date" and "Go To Previous Date" functionality. Those can be expressed this way (starting with getting the date in the database that follows the current date): SELECT DateTimeColumn FROM JournalTable WHERE DateTimeColumn > @dateTimeCurrent ORDER BY DateTimeColumn ASC Define @dateTimeCurrent as the date you are currently viewing or that is selected. The SELECT will return all dates greater than the current date, with the ones that are nearest at the top (ASC means sort by smallest to largest). To get the previous date, do this: SELECT DateTimeColumn FROM JournalTable WHERE DateTimeColumn < @dateTimeCurrent ORDER BY DateTimeColumn DESC That gets the biggest date that is smaller than the current date (the date previous to the current one). Unfortunately, I am using SQL Server Compact Edition right now, so TOP 1 doesn't work. If you are using a more powerful database, you can SELECT TOP 5 or whatever and the statement will only return the first 5 results. That is better. C Optimization Tip: Testing Against Zero--MOVEDAnother moved post: see the new document: Optimize C Loops Against Zero.DeBruijn Bit Positions, FFS: MOVEDThis wasn't a popular article, but I have moved it to a much better place, my new site (Dot Net Perls Dot Com). Visit the article here.Segoe/Tahoma Article MovedA much better article at Dot Net Perls Dot Com! With screenshot, etc. All about Segoe and Tahoma in C#Random Perls: Firefox Personas, Alphanumeric SortingUPDATE: I have a better alphanumeric sorting algorithm, and the source code is open source and easy to use! Check out my newer post, Alphanumeric Sorting Revisted: Some tidbits (snippets?) from what has been going on in my little programming microcosm:
This is a picture of my Highway Viewer program display some road conditions and also the sorted drop down items: Winforms C# .NET: Making It Faster By Making It SlowerI have two desktop Winforms applications that store 30+ MB of data in databases (originally SQLCE, now SQLite). One thing I like to do is page through large series of documents or images stored in these databases, to gain a clearer view of time and change between them. With one of my programs, ~700 documents are 8+ MB in total, and they are over 1 million words in length. The viewer/editor program I made in C#/.NET works well and I have been adding features to it, but I wanted to solve the problem with the application that would cause it to "starve" the UI thread by flooding it with rendering requests in rapid succession. This would cause the program to generally look bad, unsteady, and unreliable. I have a keyboard shortcut to open and display the previous day's document (Control-Left). On my computer, in the .NET runtime, the slowest operation is painting and rendering the controls. Next is reading from the hard disk. Well, the database structure I am using fixes the hard disk slowdown in the best way I know how, but the painting problem was still present. I implemented a paint suppression (throttling) algorithm. The idea is that the program will skip painting certain screens if the screen is expired/not current before it is painted. Here's the algorithm:
So this is what happens:
There are more bugs to work out, but so far I am really impressed with this "paint suppression," or "paint throttling." It really improves the user experience of my programs. Update: Okay, I have worked out more of the details of this algorithm. I have to say this approach really makes a Winforms application look professional and it decreases CPU time and enhances responsiveness. Here is the biggest issue I have had with it: You have to use a lock (or similar object) to ensure that the currently displayed form contents are in sync with the "document position" variable (a date, number, etc., that indicates the ID of the currently requested document). Note that the currently requested document (a cursor of sorts) is different from the currently displayed document.
I also implemented this in my Unabridged Dictionary program, and it really does enhance the user experience. Just keep your variables in sync and keep track of the time, and things will work out great.
Possibly the most important thing about this sort of enhancement is that it makes your users feel that your program is steady, reliable, and predictable. The program doesn't stutter and it does everything in specified intervals. It is even, and it feels faster even though it does less and basically puts itself to sleep over and over again.
What applications is this technique useful for? Any program that allows a user to scan over many documents/pieces of data/images quickly. A more sophisticated technique is used in web browsers and I am sure Microsoft Word uses a bunch of algorithms that are probably more complex and refined.
UPDATE: This code is now available in my public folder. Developers and Databases All Around the WorldWhat's going on in the computer world? Well, quite a lot, and most of it I don't know about.
SQL Compact Express and CompactnessI have been building things with SQL Compact Edition 3.5 databases, and these "things" are working pretty well. I still have to put forth more effort to really like my creations, but I make enhancements each day. I am going to share with the blog today my discoveries with Microsoft Compact Edition SQL Server. Here is how you set up a connection, and then read in the values from that connection into some objects in C#:
SqlCeCommand c = new SqlCeCommand("SELECT Something FROM TableName WHERE ColumnName=@input", com);
c.Parameters.AddWithValue("@var", input);
SqlCeReader reader = c.ExecuteReader();
if (reader.Read())
result = reader.GetString(0);
return result;
}
So that is the code. However, if you run that code, your nice, efficient C# program could end up taking 1.5 GBs of memory and take your brand new machine to its knees! Microsoft has some problems with memory here. It is extremely important that you call Dispose() on SqlCeCommand and SqlReader, and also Close on reader. Of course SqlCeConnection must also be closed. Let's make a new class with a constructor that sets up the SqlCe stuff:
class Setup // This class is allocated once
{ SqlCeConnection com;
SqlCeCommand c;
public Setup() // Initialize and allocate the objects once on class creation
{
com = new SqlCeConnection(@"Data Source=|DataDirectory|\data.sdf");
com.Open();
c = new SqlCeCommand("SELECT Something FROM TableName WHERE ColumnName=@input", com);
}
string SomethingExample(string input) // Called thousands of times
{
string result = String.Empty;
c.Parameters.AddWithValue("@var", input);
SqlCeReader reader = c.ExecuteReader();
if (reader.Read())
result = reader.GetString(0);
reader.Close();
reader.Dispose();
c.Parameters.Clear();
return result;
}
}
Wow! So let's go over what I changed. The SomethingExample() function is now using class-level member variables and not local variables. This means that it doesn't allocate those variables each time it is called and instead uses memory already present. We use the same SELECT SQL string, as that doesn't change. We use the input parameter to modify the SQL SELECT string (so that the lookup is for the input string). The new function also calls Clear() on the Parameters collection, so that when Example() is called next, the parameter list will be empty. Also note that I call Dispose() and Clear() on the reader. (I tried making the reader a class-level member variable, but it didn't change behavior or performance and seemed to make the code less clear.) This frees up all the resources. Note that in my first example, the code example I used isn't the exact same one in my program that I tested so it might not waste 1.5 GBs of memory. I am using some poetic license here. However, I can assure you that the second example is several times faster and also uses less memory. I have some stuff to say about implementing transactions in SQL Compact Edition, but I will save that for later. The code above wasn't compiled and was just typed manually (no copy/paste, either). Isn't it amazing how two pieces of code can do the exact same thing, but one can be 2x, 4x, 1000x more efficient than the other? Unabridged Programming Endeavors and OptimizationWell, today I have made some worthwhile enhancements to my Unabridged Dictionary program (based on the Project Gutenberg Webster's dictionary). I started in on converting the data storage in the program to an SQL Compact Edition database, and completed that a day ago or so. The improvement was not astounding but worthwhile, and the memory use and CPU usage are both decreased by a bit, the exact amount depending on the specific use case.
On the first 4 versions, I used a Dictionary<string, string> or similar structure to cache the words from a file, and in the last two I use a SQL Compact database to find the words and then cache the results for later accesses. The performance is generally better as memory goes down, although once the files are cached in versions 1-4, the program is faster. The CPU and disk hit is spread out more on versions 5-5.1. Version 5.1's database is about 7 MB smaller, because I eliminated an unneeded index. It is interesting that when I first got the Gutenberg.sdf database working, it took about 300 ms for a word lookup. I added an index and changed the file format slightly and then it took about 10-30 ms. So, the lesson here is that adding an index to a SQL database is critical and very beneficial if you do it right. I just use an index on the words themselves, and use that to find the definitions. Gutenberg Unabridged Dictionary Changes
Unabridged Dictionary Database Changes
To Do
Other I added a file to my public folder, called Benchmarker.cs. This program is a simple benchmarking program that indicates the various times that an operation might take in C# code. The same code using regular expressions might take 1.3 seconds to execute while more C-style code takes 250 milliseconds. This stuff is useful to know and will help me learn more best practices. SQL Compact Express: All My Base Are....I am going to suspend my feverish blogging for a bit while I explore and learn a new technology: SQL Express and Compact Express. I have a working database set up and I am going to make a new one and transition two of my personal projects to SQL Compact databases. I think performance, scalability, and simplicity will improve. I am excited. Here's a history of my computer programming activity and learning:
There is more to learn--but I am determined and progress will be made. I am going to post and interesting or confusing things I learn about SQL Express as I deal with them. It seems like databases are about simplicity, not complexity. There is a learning curve but once you are past that it seems a lot easier. It is harder to reinvent the database algorithm than to pass a few SQL queries to something someone else coded! Rogues, Minotaurs, and Seeker Algorithms MOVEDThis post is available in an improved version at DotNetPerls.com.How To Thread, or How Not To?Okay, I started learning C# two months ago, but almost every day I have spent several hours reading, writing, and researching code. So I am not an expert, but I do try to do my best. So I left a comment about an article about Python threads today--the article was good, and I just said something about a possible idea of mine to improve the code. After I did that, I searched for some C# threading articles to satiate my curiousity about optimal practices. I found this article.
No, no, no. Do not read that article. Well, you can read it but don't try to learn from it. I respect that Mr. Kanetkar may be a good programmer in many ways, but this article is bad.
First, he starts three threads with the ThreadStart being display1, display2, and display3. So he is having a function on another thread update the textboxes on the UI thread. Don't do this! Call Invoke() from the threaded function to change the textbox's value (the textboxes being num1, num2, and num3). That is the threadsafe way of doing it. Doing it the way in the article can cause awful bugs because of thread races, etc. It is bad.
Second, when the user clicks the button, he aborts all three threads. Abort() is bad! Visual C#'s IDE complains on Abort() for a reason--it can cause problems in more complex programs. So, basically, here is my take on Mr. Kanetkar's article:
I am not an expert, but I know this code is bad. I didn't start this blog to complain, but sometimes doing so is reasonable. |
|
|