MSSQL Tips for Developers and Administrators

  • Fix corrupt preformance counter information with logman

    I'm a bit rusty on the operations side of things and this sometimes gets me into trouble. So, when I find a tool that can clean up the mess after I've already scrambled things beyond recognition, I waste no time using it. Sometimes I'm so anxious to use it, in fact, that I launch the tool without reading all the instructions first. I stumbled upon a tool today that fixes corrupt performance counters. And, by mistake, I neglected to supply all the suggested parameters. The result was an interesting accident, but one I am not likely to forget soon.

    So here's the situation. I was trying to set up some performance counters and alerts on a SQL Server (SQL2K5) by importing a template my friend Chad Boyd - http://blogs.mssqltips.com/blogs/chadboyd created. However, there were changes I should have made to the template to insure the counters would work on the new server. After installing from the template, I had no counters that could be recognized. I tried a number of things, eventually realizing that I had corrupted the counter information in my registry. I resigned myself to the probability that the only likely solution was to rebuild the box (I had not made any restore points either - it seems the only time you really need a restore point is when you don't have one...).

    I searched TechNet and other likely places for a solution, but found nothing that helped. Finally as sort of a last resort, while looking at one of the multitude of counter errors in the event viewer, I clicked the "More Info" Link (this was on windows server 2003). This took me to an article on TechNet that said I could use a builtin command "lodctr.exe" to fix the problem (see: http://technet.microsoft.com/en-us/library/bb490926(TechNet.10).aspx ).

    Lodctr.exe is supplied with every version of windows (I think) since windows 2003 server. The description of lodctr says "Lodctr Registers new Performance counter names and Explain text for a service or device driver, and saves and restores counter settings and Explain text."  What an understatement. I've looked around and don't see a lot of references to it. Those that do seem to imply that if you use the /r switch (to fix registry problems), you must also include the name of a file containing the correct entries. Luckily, as I said, I'm not known for patience or thorough reading, so I missed this.

    Here's what I did. From a command prompt on the affected machine I entered lodctr.exe /r  and hit enter. Lodctr started working and went away long enough for me to start worrying (now, I finished reading and realized that I should have used /r:filename but by the time I was through reading this, my cmd prompt was back and there was a message that said "Info: Successfully rebuilt performance counter setting from system backup store".

     I can't tell you how or why this works, but it does (I've tested it on a box running Windows 2003 Server and one running Vista). Perhaps someone more knowledgable than me will feel like filling in the details. But regardless of how or why it works, it does work. And, that's what I care about...

  • Checking for Object Existence

    I have become a little idiosyncratic over time, I know. And, many of my pet peeves  has to do with something that may seem pretty trivial to most. Regardless, every time I see it, I get a little irritated. It seems that even the best Developers are in too much of a hurry (or maybe just too lazy) to think about the little things…

    Here’s a little thing. Existence checking for objects. If you look at SQL Books on line for an example of how to create a new object (or look at any of the books at your local book store, or search the web for examples…) the examples will either begin without any existence check or will begin with something that looks like this:

    if not exists (select [name] from sys.objects where [name] = N'MyBogusTable1' and [type] = N'U' and schema_id = schema_id(N'dbo'))

    begin ...

    The problem with not checking at all is that your script (or procedure) is not reliable. Even if it works the first time you use it, it will fail the second time. It will fail the first time if, for some reason, there is already an object with the same name as the one you are getting ready to create in the database. You should always insure that when your script or procedure runs, you get exactly the results you intended (and none other). And, I’m pretty sure no one will argue that you shouldn’t at least check first…

    But, what about that existence check I used as an example above? It will work as advertised every time. But, does that mean it is the best you could do? I would argue that it isn’t.

    Here’s why:

    The good folks at Microsoft have gone to great lengths and worked long hours to provide you with functions that return useful information about the server, database, and objects contained in the database (or elsewhere on the server). One such function is OBJECT_ID().  For an entertaining exercise, compare the estimated query plan for each of the following statements.

    if not exists (select [name] from sys.objects where [name] = N'MyBogusTable1' and [type] = N'U' and schema_id = schema_id(N'dbo'))

        create table dbo.MyBogusTable1 (col1 int);

     

    if object_id(N'dbo.MyBogusTable2',N'U') is null

        create table dbo.MyBogusTable2 (col1 int);

    go

    Now, run them and compare the actual query plan.

    The second statement is a pure metadata operation. The first requires two index seeks, a constant scan and two nested loops. It may be small, but which do you think is really the more efficient? Also, which is easier to read and understand (at a glance)? Finally, remembering that every keystroke is a new opportunity for you to make a mistake, which requires the least typing?

    Like I said, it is a pretty small thing. But the small things do add up.

  • “Roll Your Own” Replication (and why I wish I never tried it…)

    OK, first let me say a couple of things in my defense.

    I did this in 1998 (I think. It might have been earlier. SQL 6.5 was the new version of MSSQL at the time…). Replication, the version that Microsoft created and supports, was not yet available. And, we had a deadline to meet. I had one afternoon to create and test some process for moving changes made in the Order Entry/Customer Service database on a SQL 6.0 server into the Accounting/Billing database on another (It was running MSSQL 6.5). Finally, I was still pretty inexperienced with SQL  (I had only been working with it for a couple of years at the time). I did the best I could at the time and within a couple of weeks I learned some of the most important lessons of my career…

    Conceptually, the system was simple enough.

    The publishing server had triggers on the tables that needed to be replicated. Every insert, update, or delete caused a SQL statement to be generated that would be inserted into a “Transaction Log”. The “Transaction Log” was just another table. It had two columns: TranSequence integer and CmdText varchar (500) -- I think. The Transaction Log had an insert trigger defined on it as well. Every time a row was inserted, it used xp_sendmail to send a mail message to the other box. The Subject of the email was a special phrase (so the other DB would recognize that it was not SPAM) and the TranSequence. The body of the email contained the command.

    Every minute the subscriber used xp_readmail to read any messages that arrived. If it found the correct phrase in the subject, it executed the command found in the message body then sent a message (using a similar process back to the publisher which informed it that the replication was complete. Every message, after it was read and acted on (or not, depending on the phrase) , was deleted from the exchange server using xp_deletemail.

    I created this process on a Friday afternoon. It was tested (the requirement was that it should be able to manage about 400 messages / per day and that the changes would migrate to the next machine within about 2 minutes. So, testing wasn’t all that rigorous). We had it in Production on the following Monday. It worked precisely as advertised. Everyone was happy…

    What Went Wrong

    After a week or so, the sent items folder on the Exchange server reached its’ size limit. This caused the Exchange server to lock. The company was now without email. Once Exchange stopped responding, xp_sendmail, xp_readmail, and xp_deletemail stopped working. They didn’t fail. Instead, they simply went into a wait.

    These Extended Stored Procedures were being executed from within a transaction. So, locks on the tables involved quickly escalated into Table Locks. System tables did the same thing. Within a few minutes, the server was down and there was not even a way to log on to it. We had to guess at what the problem might be. It took quite a while to extrapolate a cause/effect relationship between Exchange going down, then the SQL Server.

    Worse yet, by the time this went wrong, the company was quite dependent on my “Homegrown” replication. There was no way back. For the next week or so, I started every day by manually deleting the messages from the sent mail folder. Then several times each day, I did it again. The company was growing quickly and as it grew, so did the number of messages to delete. Soon, deleting messages consumed a large part of each day. Eventually, I managed to get an intern to do this for me (I still feel just a little guilty about that). When I left the company a year later, this was still going on.

    Lessons Learned

    As I promised, I will now highlight the most important things I learned from this experience. I hope you can generalize these lessons to be useful in the situations you encounter. I know I wasn’t the only one who needed to learn these things because I still see the same mistakes repeated (in only slightly different ways) on a regular basis.

    First, never make an external system (like Exchange) a critical part of your SQL process (if you can help it). You have no control over that system and any number of things external to your process can (and sooner or later will) cause it to quit working. Also, your process might affect that system. Others who depend on it will be affected. (Trust me. There is nothing as embarrassing as shutting down the entire company, order entry, accounting, and email. You only do it once…)

    Second, avoid using triggers to move data around, execute stored procedures, or perform important business functions. They are intended to help insure transaction integrity. Try to resist the temptation to use them for other things.

    Third, remember that everything you do has two sets of consequences, those that you intended and those that you did not intend. The unintended consequences are usually the ones hurt. Take longer than one afternoon to think about the problem you need to solve. I should have explained that Friday afternoon (two days before go live) was not the appropriate time to begin adding an important new piece to the system. Yes, it would probably have been painful to delay the launch. But, it cost the company a lot more money and pain to have email, accounting and order entry shut down for a day while we tried to figure out what went wrong.

    Finally, remember this. No matter what you do. Regardless of how stupid it is. Once it makes it into a production environment, it will probably stay there to haunt you for a long time. And, even when it is gone, if it was dumb, it won’t be forgotten…

  • Hi, and welcome to my Blog.

    You might be wondering what value there is in adding yet another SQL Blog to the web. I’ve thought about this for a long time myself. Here are the most salient points I’ve come up with.

    I’m a SQL Developer. I’ve been working with MSSQL almost exclusively for 13 years now. I currently work for Microsoft MSN in the adCenter group. The database I work with most of the time has now grown to over 100 TB. Some evenings, when I’m not at Microsoft, I can be found teaching SQL. I enjoy SQL and working with really big databases. I also enjoy teaching. And, I think you might benefit from some of my experience...

    As a DBA, then as a Developer, I’ve seen (and done) a lot of stupid things. For example, before MSSQL replication was available, I created a system for replicating data between a SQL 6.0 database on one server and a SQL 6.5 database on another. It used xp_sendmail, xp_readmail, and triggers that fired for every insert, update, or delete. It worked great for several days, then the sent mail exchange folder reached its size limit… This is not the sort of mistake a sane developer would make more than once. And, I can relate any number of equally stupid things I’ve tried that didn’t work quite like I had expected.

    I’ve also learned some really useful things. Some are fairly simple things that any SQL Dev. or Admin. with even moderate skill could use. Some are quite esoteric and, perhaps, only useful to the more advanced.

    Working with SQL and teaching SQL, I’ve noticed noticed that many of the things I know how to do (and teach) that my students find most useful are not even mentioned in books on line, most of the blogs, or in any of the SQL books you can find at the local book store. They are tribal knowledge, things I learned from other Developers, or tricks I stumbled on quite by accident. In addition, I’ve come to believe that because a thing can be done does not mean it should be done.

    With that in mind, I’d like to use this blog to relate some of the things I’ve learned along the way. From time to time, I’ll also tell you about a few of the dumb things I’ve tried and why they didn’t work. (In the next post, I'll tell you all about my home grown replication system and point out some of the more egregious mistakes I made there.)

    I hope you will find this Blog interesting, perhaps even a little entertaining from time to time. And if, along the way, I manage to help you with some problem you’ve been trying to solve? Well, that would be just dandy…

    charlie


© 2008 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker