<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	>

<channel>
	<title>Certain Extent</title>
	<atom:link href="http://davidtate.org/blog/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://davidtate.org/blog</link>
	<description>Maybe its something</description>
	<pubDate>Sat, 31 Jul 2010 01:01:18 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Why I&#8217;m quitting twitter/facebook</title>
		<link>http://davidtate.org/blog/?p=351</link>
		<comments>http://davidtate.org/blog/?p=351#comments</comments>
		<pubDate>Sat, 31 Jul 2010 00:59:06 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[peopleware]]></category>

		<guid isPermaLink="false">http://davidtate.org/blog/?p=351</guid>
		<description><![CDATA[Tonight, I’m quitting twitter/facebook, et al as a reader.  Please do not assume I have seen any of your updates, links, or lolcat pictures from this point forward.  I might post links to blog posts, but consider my account inactive.
As an experiment I recently loaded up everything I was planning on doing this summer into [...]]]></description>
			<content:encoded><![CDATA[<div><span id="internal-source-marker_0.29587558936327696">Tonight, I’m quitting twitter/facebook, et al as a reader.  Please do not assume I have seen any of your updates, links, or lolcat pictures from this point forward.  I might post links to blog posts, but consider my account inactive.</span></p>
<p><span>As an experiment I recently loaded up everything I was planning on doing this summer into a todo list application.  And I mean everything - I created recurring tasks for driving to work, going to church on Sundays, and eating food.  In addition I added the traditional things as well - work tasks, ideas I had for work, ideas I had for random stories, people I needed to contact every few months, etc.  I did this to free my mind from the constant interrupt-based thinking that happens with these things.  As I created these I was very honest with myself - I went to extremes to try to look outside myself and see what I was actually doing. </span></p>
<p><span>Driving home and during other idle times my mind is a constant state of poking around ideas like:</span></p>
<ul>
<li><span>Maybe it should be an interface instead of a base implementation, that would be more generic.</span></li>
<li><span>I need to call John about eating lunch</span>
<ul>
<li><span>Maybe we should go to Moe’s, but not the one on Old Milton, the one on Windward</span></li>
</ul>
</li>
<li><span>The emissions on the car still need to be done before August</span></li>
<li><span>I need another set of eyebrows to catch the sweat from my other eyebrows</span></li>
</ul>
<p><span>What prompted this inventory was the fact that I had some pretty big goals for this summer - move to a new team, learn some new technologies, train for two large cycling events, go on a big trip. </span></p>
<p><span>After doing this “open valve” for 2 weeks I went back and looked at what all I was doing and noticed two tasks:</span></p>
<ul>
<li><span>Check Facebook x3 daily</span></li>
<li><span>Check Twitter x20 daily</span></li>
</ul>
<p><span>I also noticed that during the time that is the most important in the day [the time in which my part cannot be played by anyone else] I was still thinking like this and doing some of the recurring tasks like checking twitter on my phone..  There have been many times where instead of focusing on what my wife is saying or what my kids are doing I was thinking about something that I couldn’t be doing right then or checking twitter/facebook.  In addition during times of true idleness I was filling it with unfocused internet browsing and other wastes.</span></p>
<p><span>That sounds a bit like an addiction - so I tried another experiment.  What would I lose if I cut it all off?  Turns out the list is appealing, but not like I thought it would be.  I would miss:</span></p>
<ul>
<li><span>Some jokes at work</span></li>
<li><span>Some information on stuff that I like (such as information on cycling)</span></li>
<li><span>Some information on stuff that I need (such as road construction)</span></li>
<li><span>Fast meme tracking</span></li>
</ul>
<p><span>None of these things are as important as what they were taking away:</span></p>
<ul>
<li><span>Being 100% present with my kids and wife</span></li>
<li><span>Being 100% present during idle times to think about things and create things</span></li>
</ul>
<p><span>So I’m turning it all off as much as I can - rather than being a massive consumer of information I’m pulling my shades and am going to do more writing and less reading, and maybe just less reading in total.  This feels very right/true to me - do you remember when people used to walk down the street and not listen to music?  I bet they heard some cool stuff and were more present in their world, and a lot of good ideas probably came to them during that time.  But now Katy Perry has ruined it all.  Anyway, cya.</span></div>
]]></content:encoded>
			<wfw:commentRss>http://davidtate.org/blog/?feed=rss2&amp;p=351</wfw:commentRss>
		</item>
		<item>
		<title>Key points to look for in your next job if you are a developer</title>
		<link>http://davidtate.org/blog/?p=332</link>
		<comments>http://davidtate.org/blog/?p=332#comments</comments>
		<pubDate>Fri, 21 May 2010 12:44:23 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[peopleware]]></category>

		<category><![CDATA[peopleware career]]></category>

		<guid isPermaLink="false">http://davidtate.org/blog/?p=332</guid>
		<description><![CDATA[As a developer, your basic job is to create things.  Since the world needs software in every industry you might think that one is the same as the next, and you&#8217;d be shamefully wrong.  Outside of the obvious questions you should ask yourself when looking for your next gig - how sharp are [...]]]></description>
			<content:encoded><![CDATA[<p>As a developer, your basic job is to create things.  Since the world needs software in every industry you might think that one is the same as the next, and you&#8217;d be shamefully wrong.  Outside of the obvious questions you should ask yourself when looking for your next gig - how sharp are the coworkers, how good is the tech, how hard are the problems, how good are the tools - you should also ask: &#8220;Who are we working for?&#8221;</p>
<p>It turns out that who you produce for can more directly impact how much you like your job than you might think.</p>
<p><strong>Internal vs. External client</strong></p>
<p>Internal clients don&#8217;t matter as much as external ones.  Yeah I said it, wanna fight about it?  Working for a bank on their internal accounting software is not the same as working on banking software that is sold to banks.  Period, close bracket, EOF.  The reason for this is that one is a profit center with real financial pressure, and the other is a cost center, with pressure to simply exist cheaply.  A profit center has direct competitors that you sometimes have to react to, but a cost center rarely implements new product due to hearing that an internal customer at another company is happy.</p>
<p>The internal vs. external switch plays itself out in multiple subtle ways*:</p>
<p><em>Rate of Change</em><br />
A profit center tries multiple things, watches competitors to match features, explores new lines of business, etc.  A cost center does not take much risk, and thus is more setup for small improvement or the support of company growth.</p>
<p><em>Rate of spending</em><br />
Another way this plays out is in lack of budget flexibility - since a cost center is under pressure to lower costs their budgets are smaller and less innovative.  The type of managers that run these organizations are the special type of demon that is good at finding ways to save money - like on hardware or crappy coffee.</p>
<p><em>Rate of Respect</em><br />
In a profit center the business leaders interact with the technical leaders and producers enough that they begin to understand their importance.  Over time a mutual respect grows and is a healthy team behavior. In a cost center at times the cost center is in a servant position and the IT functions are not held in the same level of respect.</p>
<p><strong>Producer vs. Maintainer</strong></p>
<p>There is another subtle difference in &#8220;software developers&#8221; at times that can play out in affecting you position.  Some people build tools and processes and some people build deliverable product.  In the software realm the tools can include continuous integration modules, deployment tools, operational helper tools, code generators, etc.  Product includes things that directly sell outside your organization.  If you are a developer working on the toolset *primarily* you are secondary to those working on the end product - you are in effect serving an internal customer.</p>
<p><strong>Deadline driven vs Shame-driven</strong></p>
<p>When we interview someone we always ask how the end game of projects work - &#8220;Do you work off of deadlines?&#8221;, &#8220;Who sets these deadlines?&#8221;.  Many internal customers have false deadlines because there is no competition - are they going to go use another internal accounting department?  The same fire does not always exist in those working in internally-facing companies.  Just because your coworkers are smart  this doesn&#8217;t mean they have any hustle.</p>
<p><strong>Industry and Economy</strong></p>
<p>The industry that you are building solutions for can matter because the level of tolerated innovation differs across industries and product categories.  My first full-time programming job was working on an audio-dispatching call center product that was sold to air traffic controllers and 911 call centers.  The sales cycle for this product was very different than other industries - if a client saw a demo and a single thing went wrong they would typically say: &#8220;We will reevaluate changing our product in 5 years, get back to us then&#8221;.  This is obviously different than the change cycle for a web-based project management tool that might receive changes every two weeks.</p>
<p><strong>Customer distance</strong></p>
<p>How &#8220;close&#8221; you are to the customer matters as well whether that customer is internal or external.  While only some developers are interested in directly speaking to customers, the dev team&#8217;s distance to the customer can affect whether or not what they are building matters.  As a producer you should care very deeply about whether you are building the right thing.</p>
<p>* Internal clients means full-time - if you are working on a project for an internal client the effects are more minor.  Most of what is mentioned above is when you wake up everyday to a world of internal client demands only.</p>
]]></content:encoded>
			<wfw:commentRss>http://davidtate.org/blog/?feed=rss2&amp;p=332</wfw:commentRss>
		</item>
		<item>
		<title>Complexity</title>
		<link>http://davidtate.org/blog/?p=315</link>
		<comments>http://davidtate.org/blog/?p=315#comments</comments>
		<pubDate>Sun, 02 May 2010 15:13:02 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[peopleware]]></category>

		<guid isPermaLink="false">http://davidtate.org/blog/?p=315</guid>
		<description><![CDATA[In software development there are three levels of complexity that are in play.
Primary complexity in software is taking a complex business problem within its native domain and designing a technical solution.  Examples of primary complexity issues are designing a strategy-based plugin architecture for mortgage calculations, designing a star schema to later use for predictive [...]]]></description>
			<content:encoded><![CDATA[<p>In software development there are three levels of complexity that are in play.</p>
<p>Primary complexity in software is taking a complex business problem within its native domain and designing a technical solution.  Examples of primary complexity issues are designing a strategy-based plugin architecture for mortgage calculations, designing a star schema to later use for predictive analysis of snack cart Skittle consumption on college campuses  (spoiler alert: finals week and wild berry are a solid marriage).</p>
<p>Secondary complexity in software include items that the folks in the domain don&#8217;t understand but the technical folks need to do their work quickly.  The QA team, the fact that you need to upgrade Visual Studio every two years in a Microsoft shop, your check-in policy and coding standards - these are typical examples of secondary complexity.</p>
<p>Tertiary complexity are items that the domain folks and the technical folks don&#8217;t fully understand.  The fact that Steve and Ralph freaking hate each other, the morale impact of low raises on a team, the fact that communication can kill a team over a certain size, poor meeting practice, etc. are all examples of tertiary complexity.  This type of complexity is colloquially referred to as &#8220;bullshit&#8221; (in some provinces &#8220;horseshit&#8221; or more rarely &#8220;dogshit&#8221;)</p>
<p><strong>Consequences</strong><br />
The levels affect each other predictably.  If the problems of one level are not solved, they affect levels above them.  If half of your team are French and the other half hate French people, that&#8217;s a tertiary complexity problem that will lead to awkward code reviews, poor rework throughput and affect your ability to ship software that solves domain problems.  If your QA and UAT databases &#8220;timeout&#8221; more than a cranky 2 year old, you can&#8217;t ship software.</p>
<p><strong>Talent Effect</strong><br />
By &#8220;effect&#8221; I don&#8217;t mean slow down only.  If you have too many secondary and tertiary problems, the people that work in the primary domain will grow frustrated.  In software development these are creators - developers and those that feed them information (BAs, User Interface folks, reedit).  This is a dangerous situation.  If you hire a talented pizza chef but all the pizza shows up two hours late (or not at all) you have secondary problems (failed delivery channel - broken down Civic) or tertiary problems (your delivery guy likes pot more than tips) eventually the chef will feel that their work doesn&#8217;t matter and leave.</p>
<p><strong>Management</strong><br />
If you make the move from developer to manager, you are effectively saying that your team will handle the primary work while you work on secondary work (processes) with their input, but try as much as possible to shield the tertiary problems (bullshit) from them.  Where this gets more complex is when tertiary complexity is high in an organization.  The most striking example of tertiary complexity in some organizations are middle managers jockeying for promotion position - this activity does not improve processes or help with production, but creates a situation in which these things are harder.</p>
<p>The balance of tertiary vs. secondary complexity is indicative of where a company is and whether it can heal its key problems.  If the tertiary complexity is low, then there is enough management capacity to solve secondary issues and allow producers to create great things.</p>
]]></content:encoded>
			<wfw:commentRss>http://davidtate.org/blog/?feed=rss2&amp;p=315</wfw:commentRss>
		</item>
		<item>
		<title>Fixing problems Part 1: Attitude Adjustment required</title>
		<link>http://davidtate.org/blog/?p=10</link>
		<comments>http://davidtate.org/blog/?p=10#comments</comments>
		<pubDate>Fri, 17 Apr 2009 06:41:21 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[peopleware]]></category>

		<guid isPermaLink="false">http://davidtate.org/blog/?p=10</guid>
		<description><![CDATA[
As DBAs, software developers, Homo Sapiens, and lovers we have to solve problems.  There is a common misconception that support is for the more junior folks on a team and thus being good at it is a sign of &#8220;being a little baby&#8221;.  While support is a great way to learn a software ecosystem and [...]]]></description>
			<content:encoded><![CDATA[<p></p>
<p>As DBAs, software developers, Homo Sapiens, and lovers we have to solve problems.  There is a common misconception that support is for the more junior folks on a team and thus being good at it is a sign of &#8220;being a little baby&#8221;.  While support is a great way to learn a software ecosystem and organization and thus &#8220;grow&#8221; a junior person into a senior one a lot of problem-solving falls to the rock star programmers or wizard DBAs in most organizations.</p>
<p>Yet some of these people aren&#8217;t any good at it.  In fact, they are awful.  I&#8217;ve seen people that are very good at doing very hard technical things - creating something from nothing, thinking of all the things that could go wrong, refactoring and integrating a large subsystem, etc. - fail at simply fixing a problem with an existing system. You can take your rock star and send them off to fix a support issue and they will return with a confused look, eight hours of wasted effort, and an STD.  Working with some truly gifted problem solvers I&#8217;ve witnessed some differences in Attitude, Practices, and Skills that separate the junior and senior problem solvers.  Let&#8217;s start with Attitude.</p>
<h5>First, there IS a problem</h5>
<p>Never deny that there is a problem.  If someone is at your desk, on the phone, flooding your email with red exclamation points, or outside your house knocking on the window there is clearly a problem.  The problem might be that they don&#8217;t understand something and the problem might not be your fault, but the issue should be treated with respect as a real problem if they took the time to contact you.  Don&#8217;t deny it or argue.  Why would you deny it anyway, because you see support as negative.</p>
<h5>Don&#8217;t see support as a negative shameful thing or a junior task</h5>
<p>As long as things keep changing, there will always be problems.  (This next part is hard to put down in writing) If you aren&#8217;t writing bugs you aren&#8217;t writing software.  If you aren&#8217;t changing systems you aren&#8217;t working.  A support issue is not an insult, a bug is not a breakup.  Yes, you should make sure that you don&#8217;t write infinite loops, and yes you should make sure that you test the latest SQL Server upgrade before you install it in production at 10 am on the last day of the month.  But in most organizations there is a constant to and fro of creating new things and then fixing issues that crop up with them, so don&#8217;t pretend as if a problem is an anomaly.  True root cause and issue prevention are topics for another post, but don&#8217;t act surprised that software systems don&#8217;t always work as expected.</p>
<h5>Confidence</h5>
<p>In my home office I have a fortune cookie taped to one of my monitors that says: &#8220;You have the ability to analyze and solve any problem&#8221;, and over time I have started to believe it (by looking at it 27 times a minute).  The fact is that most people that are good at support are good because they believe that given enough time they could fix any issue.  ANY issue.  Given 20 years and enough coffee they could learn C/C++, reverse engineer SQL Server, learn about cross-platform multi-threading, and fix <a href ="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=328811">that bug</a>.</p>
<h5>The ability to not freak out and lose it</h5>
<p>Something is broken but don&#8217;t be scared (its just moving electrons for the highest bidder).  The fact that someone is at your desk and not someone else&#8217;s is a good thing, don&#8217;t panic and freak out and yell things that you&#8217;ll regret later (Aside: yelling is always regretted - only thing I&#8217;ve not regretted yelling &#8220;OMG ITS MILEY&#8221;). Don&#8217;t blame people or come off as condescending; assume that they are your desk because they know you can fix it, not because they think you caused it.  Figuring out root cause and a long-term solution are separate things; as are fixing and blaming.  You are in charge of fixing for now, so just focus on that.  Besides, over time a calm person is going to be relied upon more while those who freakout will only end up on reality shows. (They don&#8217;t make reality TV shows about guys sitting at keyboards fixing complex technical issues - YET)</p>
<p>Next post - Practices that improve your ability to fix complex technical issues.</p>
]]></content:encoded>
			<wfw:commentRss>http://davidtate.org/blog/?feed=rss2&amp;p=10</wfw:commentRss>
		</item>
		<item>
		<title>Review of Yammer, a private corporate Twitter application</title>
		<link>http://davidtate.org/blog/?p=266</link>
		<comments>http://davidtate.org/blog/?p=266#comments</comments>
		<pubDate>Thu, 16 Apr 2009 19:54:28 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://davidtate.org/blog/?p=266</guid>
		<description><![CDATA[So a few months ago a bunch of folks at work (lead by cutting-edge Antonio Yon) installed Yammer and now nobody is using it anymore.  Why?
First, Yammer is pretty much a private Twitter for use in a corporate environment; its basic features are:

Basic twitter bits - follow people, short messages, etc.
Org chart build-out features [...]]]></description>
			<content:encoded><![CDATA[<p>So a few months ago a bunch of folks at work (lead by cutting-edge <a href="http://twitter.com/oldmantone">Antonio Yon) </a>installed <a href="http://yammer.com">Yammer</a> and now nobody is using it anymore.  Why?</p>
<p>First, Yammer is pretty much a private Twitter for use in a corporate environment; its basic features are:</p>
<ul>
<li>Basic twitter bits - follow people, short messages, etc.</li>
<li>Org chart build-out features (you tell Yammer your boss and who works for you and it invites them and builds out your corporate structure)</li>
<li>Can easily form groups such as QA, Development, The Party for the Overthrow of QA, Party Planning Committee, Party Pooping Committee</li>
<li>TweetDeck-like UI or web interface, also BlackBerry, etc.</li>
<li>More features here: <a href="https://www.yammer.com/company/features">Yammer Features</a></li>
</ul>
<p>Good things</p>
<ul>
<li>You can say things you can&#8217;t over Twitter such as &#8220;Lunch is here&#8221; or &#8220;Client XYZ makes me want to cut off my fingers&#8221; or even &#8220;I wish I was a lumberjack&#8221;</li>
<li>You can ask very very context-heavy questions - if you create a group for your team, it is basically an open IRC channel in which you can ask questions like &#8220;in this stored procedure, what does this mean?&#8221;</li>
</ul>
<p>Problems</p>
<ul>
<li>Yammer now can export their data to recruiters if/when they go out of business</li>
<li>Yammer is an immature software implementation of a good idea.  The 2nd day that 15 people signed up for it we all got 15&#215;15 emails notifying us that each person was following us - henious shameful bug (Cartesian FAIL).</li>
<li>The moment a &#8220;Vice President of Anything&#8221; joins the conversation cleans up and gets more useless or starts getting directed at that person (A VP of anything is more likely to join Yammer than Twitter given its org chart viral features)</li>
<li>The verb of Yammer is &#8220;Yam&#8221; which let&#8217;s admit it sounds gross</li>
</ul>
<p>Why it didn&#8217;t work here</p>
<ul>
<li>People used it to say stupid things &#8220;Holy crap look at that bird&#8221;</li>
<li>Given that some people are on Twitter and other social networking sites, the fact that it is yet another client makes people get very frustrated if the context isn&#8217;t rich</li>
<li>It spammed us</li>
<li>The client crashed occasionally</li>
<li>I&#8217;m not sure, but I think it got somebody pregnant</li>
</ul>
<p>If Twitter or some other service supported and could be trusted with this functionality I think that having a private one integrated would be a good idea given:</p>
<ul>
<li>Twitter isn&#8217;t bought by Google (At this point Google knows everything but my safe word: &#8220;Knight Rider&#8221;)</li>
<li>It is obvious that you are talking in a private channel</li>
<li>You could easily install it locally like various other social bits - most companies don&#8217;t run a wiki externally, they install Sharepoint or MediaWiki on a spare server</li>
<li>The &#8220;corporate twitter&#8221; is either supported by management (and your boss sends out updates and posts stuff there) or is completely outside the corporate realm (just your dev/DBA team uses it as a private IM group).</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://davidtate.org/blog/?feed=rss2&amp;p=266</wfw:commentRss>
		</item>
		<item>
		<title>Exception (Mis)Handling</title>
		<link>http://davidtate.org/blog/?p=140</link>
		<comments>http://davidtate.org/blog/?p=140#comments</comments>
		<pubDate>Tue, 24 Mar 2009 13:56:28 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[.net]]></category>

		<category><![CDATA[antipattern]]></category>

		<category><![CDATA[c#]]></category>

		<guid isPermaLink="false">http://davidtate.org.new-mn.sabren.com/blog/?p=140</guid>
		<description><![CDATA[Exception handling was originally created to try to give developers a way to separate out error handling so that it wouldn’t clutter up and distract from the core functionality trying to be accomplished.  When done well, exception handling can provide a clean way to instrument and separate truly exceptional conditions from the core flow [...]]]></description>
			<content:encoded><![CDATA[<p>Exception handling was originally created to try to give developers a way to separate out error handling so that it wouldn’t clutter up and distract from the core functionality trying to be accomplished.  When done well, exception handling can provide a clean way to instrument and separate truly exceptional conditions from the core flow of your methods as well as a way to prevent nasty crashes and untraceable bugs.  When done badly, exception handling can be misused for decision making, masking errors and bugs, and distracting developers from doing real work.</p>
<p>Go read these when you have time, and you have time if you are reading this – admit it.<br />
<a href="http://www.amazon.com/CLR-via-Second-Pro-Developer/dp/0735621632/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1237900532&amp;sr=8-1Framework Design ">Chapter 19 of CLR via C#</a> and <a href="http://msdn.microsoft.com/en-us/library/ms229014.aspx">Guidelines: Exception Handling.</a></p>
<p>There is not a lot that can be said that isn’t said here about .NET exception handling that isn’t in the resources above, but I’ve picked up on a common anti-pattern that stems from a misunderstanding of what exception handling is.  I call it the “safety-net catch”, and it goes a little something like this:</p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">bool</span> Import()
{
    <span class="kwrd">bool</span> isSuccess = <span class="kwrd">false</span>;

    <span class="kwrd">try</span>
    {
    <span class="kwrd">    if</span> (SomethingIsWrong())
        <span class="kwrd">    throw</span> <span class="kwrd">new</span> <span class="cobj">ApplicationException</span>(<span class="str">"Start panic sequence now."</span>);

        bSuccess = DoSomething(WithThis, AndThis);

    <span class="kwrd">    foreach</span> (<span class="kwrd">string</span> databaseThing <span class="kwrd">in</span> theDatabase)
        {
            <span class="kwrd">if</span> (bSuccess) bSuccess = DoSomethingUseful(databaseThing);
        }
    }
    <span class="kwrd">catch</span> (<span class="cobj">Exception</span> ex)
    {
        PublishException(ex);
        isSuccess = <span class="kwrd">false</span>;
    }
    <span class="kwrd">return</span> isSuccess;
}</pre>
<p>In this example, DoSomethingUseful,  DoSomething, and most likely PublishException all have this same pattern of a ‘catch all’ at the end.  We aren’t getting much benefit from this style of exception handling as it merely serves to make sure that this method always returns bSuccess so that execution should continue with any failure.</p>
<p>I think it would be easier on everyone if this code was changed to simply not handle exceptions that it can’t actually handle.  The top level threads of your application should have a “catch all” that publishes the exception, and then displays a nice message for the user in the case of a web application.  So if you want to fail, just let it fail if you can’t recover from it.  The contract of the method Import above doesn’t say that it doesn’t throw exceptions – if it can’t do its job it should throw or allow an exception to bubble up.</p>
<p>There are cases where you don’t want the code to break out in the case of any exception, but these are rare.  In the case of row level handling where we are parsing a file and you don’t want row to ruin the whole file you can simply move exception handling down to that import piece.  In this case non- exception based mechanisms can be used or you can throw a custom exception type or an exception with well understood semantics like InvalidOperationException or ArgumentException.</p>
<p>Please do:</p>
<ul>
<li> Throw an exception in a method if you can’t do the method’s job.  If you don’t know what the method should do because it does 23 things, 18 of which can happen if it isn’t given a valid value for AccountNumber, refactor until you have 23 methods, one of which throws InvalidArgumentException when it gets an invalid AccountNumber.</li>
<li>Only do a ‘catch’ if you are going to handle it (maybe the syntax should say handle).  Just publishing or setting a variable doesn’t count in the pattern above.</li>
<li> If you for some reason catch and rethrow (like if you want to add to the exception object), do a throw and not a throw origEx to keep the call stack.</li>
<li>Remember that your most common ‘exceptional condition’ might be a database timeout or other database exception (System.Data.SqlClient.SqlException) and is not an ApplicationException but most likely can’t be recovered from anyway.</li>
</ul>
<p>Please don’t do this:</p>
<pre class="csharpcode"><span class="kwrd">try</span>
{
    bc.Save(o);
    DB.UpdateProcessFlag(conn, Util.GetInt32(dr, <span class="str">"Key"</span>), 1);
}
<span class="kwrd">catch</span>
{
    <span class="rem">//Error on save, so mark this one as errored.</span>
    DB.UpdateProcessFlag(conn, Util.GetInt32(dr, <span class="str">"Key"</span>), -1);
}</pre>
<p>There are many reasons that this .Save() could fail, and a SqlException or (ObjectReferenceException on dr) are going to be badly mishandled here.  If you find yourself feeling like you need to use the try/catch mechanism for this sort of stuff perhaps take a step back and think about how to handle it in terms of design.</p>
<p>And please don’t do this unless you like hearing the sound of me dying inside. (Use the <a href="http://msdn.microsoft.com/en-us/library/f02979c7.aspx">TryParse </a>pattern instead)</p>
<pre class="csharpcode"><span class="kwrd">try</span> { o.Something = -aNumba; }
<span class="kwrd">catch</span> { o.Something = 0;  }
<span class="kwrd">try</span> { amt = Util.GetDecimal(dr, <span class="str">"FieldName"</span>); }
<span class="kwrd">catch</span> { amt = 0; }

<span class="kwrd">if</span> (amt == 0) <span class="kwrd">return</span>;</pre>
]]></content:encoded>
			<wfw:commentRss>http://davidtate.org/blog/?feed=rss2&amp;p=140</wfw:commentRss>
		</item>
		<item>
		<title>Triggers Part 3: FAQ and FOP continued</title>
		<link>http://davidtate.org/blog/?p=188</link>
		<comments>http://davidtate.org/blog/?p=188#comments</comments>
		<pubDate>Fri, 13 Mar 2009 03:25:07 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[sqlserver]]></category>

		<guid isPermaLink="false">http://davidtate.org/blog/?p=188</guid>
		<description><![CDATA[How many triggers should you have per table?
Ideally zero.  If you have any then there should be one.  There is no guarantee on the ordering of trigger firings, they normally fire based on their age – newly-added triggers fire last.  So if you have two triggers that both run on update, you [...]]]></description>
			<content:encoded><![CDATA[<p><strong>How many triggers should you have per table?</strong></p>
<p>Ideally zero.  If you have any then there should be one.  There is no guarantee on the ordering of trigger firings, they normally fire based on their age – newly-added triggers fire last.  So if you have two triggers that both run on update, you could get into a recursion situation.<br />
If you do have more than one, you have to set the trigger order via a call to sp_settriggerorder to avoid this recursion, or rewrite the trigger.</p>
<pre class="csharpcode">

<span class="rem">-- Test trigger recursion</span>

<span class="kwrd">if</span> <span class="preproc">object_id</span>(<span class="str">'TestTriggerRecursion'</span>, N<span class="str">'U'</span>) &gt; 0
    <span class="kwrd">drop</span> <span class="kwrd">table</span> TestTriggerRecursion

<span class="kwrd">if</span> <span class="preproc">object_id</span>(<span class="str">'tr_TestTriggerRecursionUpdatedDate'</span>, N<span class="str">'TR'</span>) &gt; 0
    <span class="kwrd">drop</span> <span class="preproc">trigger</span> dbo.tr_TestTriggerRecursionUpdatedDate

<span class="kwrd">if</span> <span class="preproc">object_id</span>(<span class="str">'tr_TestTriggerRecursionUpdatedBy'</span>, N<span class="str">'TR'</span>) &gt; 0
    <span class="kwrd">drop</span> <span class="preproc">trigger</span> dbo.tr_TestTriggerRecursionUpdatedBy
<span class="preproc">go</span>

<span class="kwrd">create</span> <span class="kwrd">table</span> TestTriggerRecursion
(
    KeyID <span class="kwrd">int</span> <span class="kwrd">primary</span> <span class="kwrd">key</span> <span class="kwrd">clustered</span>
    , Payload <span class="kwrd">varchar</span>(<span class="preproc">max</span>) <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    , LastUpdatedDate <span class="preproc">datetime</span> <span class="kwrd">NULL</span>
    , LastUpdatedBy sysname <span class="kwrd">NULL</span>
)
<span class="preproc">go</span>

<span class="kwrd">create</span> <span class="preproc">trigger</span> dbo.tr_TestTriggerRecursionUpdatedDate <span class="kwrd">on</span> dbo.TestTriggerRecursion
<span class="kwrd">for</span> <span class="kwrd">insert</span>, <span class="kwrd">update</span>
<span class="kwrd">as</span>

<span class="kwrd">print</span> <span class="str">'tr_TestTriggerRecursionUpdatedDate ran'</span>
<span class="kwrd">update</span> t <span class="kwrd">set</span> LastUpdatedDate = <span class="preproc">getdate</span>()
<span class="kwrd">from</span> INSERTED i
<span class="kwrd">join</span> TestTriggerRecursion t <span class="kwrd">on</span> t.KeyID = i.KeyID

<span class="preproc">go</span>

<span class="kwrd">create</span> <span class="preproc">trigger</span> dbo.tr_TestTriggerRecursionMaintainAuditField <span class="kwrd">on</span> dbo.TestTriggerRecursion
<span class="kwrd">for</span> <span class="kwrd">insert</span>, <span class="kwrd">update</span>
<span class="kwrd">as</span>

<span class="kwrd">print</span> <span class="str">'tr_TestTriggerRecursionUpdatedBy ran'</span>
<span class="kwrd">update</span> t <span class="kwrd">set</span> LastUpdatedBy = <span class="preproc">SUSER_NAME</span>()
<span class="kwrd">from</span> INSERTED i
<span class="kwrd">join</span> TestTriggerRecursion t <span class="kwrd">on</span> t.KeyID = i.KeyID

<span class="preproc">go</span>

<span class="rem">-- delete from TestTriggerRecursion</span>
<span class="kwrd">insert</span> <span class="kwrd">into</span> TestTriggerRecursion <span class="kwrd">values</span> (1, <span class="str">'test1'</span>, <span class="kwrd">null</span>, <span class="kwrd">null</span>)
<span class="kwrd">insert</span> <span class="kwrd">into</span> TestTriggerRecursion <span class="kwrd">values</span> (2, <span class="str">'test2'</span>, <span class="kwrd">null</span>, <span class="kwrd">null</span>)
<span class="kwrd">insert</span> <span class="kwrd">into</span> TestTriggerRecursion <span class="kwrd">values</span> (3, <span class="str">'test3'</span>, <span class="kwrd">null</span>, <span class="kwrd">null</span>)

<span class="kwrd">select</span> * <span class="kwrd">from</span> TestTriggerRecursion

<span class="rem">-- fails with</span>
/*

(0 <span class="kwrd">row</span>(s) affected)
Msg 217, <span class="kwrd">Level</span> 16, <span class="kwrd">State</span> 1, <span class="kwrd">Procedure</span> tr_TestTriggerRecursionUpdatedBy, Line 6
Maximum stored <span class="kwrd">procedure</span>, <span class="kwrd">function</span>, <span class="preproc">trigger</span>, <span class="kwrd">or</span> <span class="preproc">view</span> nesting <span class="kwrd">level</span> exceeded (<span class="kwrd">limit</span> 32).

*/

-- <span class="kwrd">exec</span> <span class="sqlkwrd">sp_settriggerorder</span> @triggername = <span class="str">'tr_TestTriggerRecursionUpdatedBy'</span>, @<span class="kwrd">order</span>=<span class="str">'First'</span></pre>
<p><strong>How can I get a list of triggers and which tables they apply to?</strong></p>
<pre class="csharpcode">
<span class="kwrd">select</span> so.name <span class="kwrd">as</span> TableName
, st.name <span class="kwrd">as</span> TriggerName
<span class="kwrd">from</span> sys.triggers st
<span class="kwrd">join</span> <span class="sqlkwrd">sys.objects</span> so <span class="kwrd">on</span> so.<span class="preproc">object_id</span> = st.parent_id
</pre>
<p><strong>What order do constraints and triggers run?</strong></p>
<p>Constraints, then triggers.  In addition it should be noted that the DML operation and the trigger code have separate execution plans, poor code that causes recompiles inside a trigger does not cause outside stored procedures to change.  With statement-level compilation/caching in 2005 this is even more true.  In addition it should be noted that constraint code is independent of DML operations as well.</p>
<p><strong>Can you say “only fire this trigger if THIS column is updated”?</strong>  </p>
<pre class="csharpcode">
<span class="kwrd">IF</span> <span class="kwrd">UPDATE</span>(Field1) <span class="kwrd">OR</span> <span class="kwrd">UPDATE</span>(Field2)
<span class="kwrd">BEGIN</span>

<span class="kwrd">END</span>
</pre>
<p><strong>Do triggers try to run ON UPDATE even if now rows have been affected?</strong></p>
<p>Yes, which is why you should always bail if no rows are affected like so:</p>
<pre class="csharpcode">
<span class="kwrd">IF</span> <span class="preproc">@@ROWCOUNT</span> = 0
RETURN</pre>
<p>As you see below, your entire trigger will fire even on a failed update.</p>
<pre class="csharpcode">

<span class="rem">-- Test trigger update behavior with zero rows</span>
<span class="kwrd">if</span> <span class="preproc">object_id</span>(<span class="str">'TestTriggerUpdateBehavior'</span>, N<span class="str">'U'</span>) &gt; 0
    <span class="kwrd">drop</span> <span class="kwrd">table</span> TestTriggerUpdateBehavior

<span class="kwrd">if</span> <span class="preproc">object_id</span>(<span class="str">'tr_TestTriggerUpdateBehaviorUpdatedDate'</span>, N<span class="str">'TR'</span>) &gt; 0
    <span class="kwrd">drop</span> <span class="preproc">trigger</span> dbo.tr_TestTriggerUpdateBehaviorUpdatedDate

<span class="kwrd">create</span> <span class="kwrd">table</span> TestTriggerUpdateBehavior
(
    KeyID <span class="kwrd">int</span> <span class="kwrd">primary</span> <span class="kwrd">key</span> <span class="kwrd">clustered</span>
    , Payload <span class="kwrd">varchar</span>(<span class="preproc">max</span>) <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    , LastUpdatedDate <span class="preproc">datetime</span> <span class="kwrd">NULL</span>
    , LastUpdatedBy sysname <span class="kwrd">NULL</span>
)
<span class="preproc">go</span>

<span class="kwrd">create</span> <span class="preproc">trigger</span> dbo.tr_TestTriggerUpdateBehaviorUpdatedDate <span class="kwrd">on</span> dbo.TestTriggerUpdateBehavior
<span class="kwrd">for</span> <span class="kwrd">update</span>
<span class="kwrd">as</span>

<span class="kwrd">print</span> <span class="str">'tr_TestTriggerUpdateBehaviorUpdatedDate ran'</span>
<span class="kwrd">update</span> t <span class="kwrd">set</span> LastUpdatedDate = <span class="preproc">getdate</span>()
<span class="kwrd">from</span> INSERTED i
<span class="kwrd">join</span> TestTriggerUpdateBehavior t <span class="kwrd">on</span> t.KeyID = i.KeyID

<span class="preproc">go</span>

<span class="kwrd">insert</span> <span class="kwrd">into</span> TestTriggerUpdateBehavior <span class="kwrd">values</span> (1, <span class="str">'test1'</span>, <span class="kwrd">null</span>, <span class="kwrd">null</span>)

<span class="kwrd">update</span> TestTriggerUpdateBehavior <span class="kwrd">set</span> Payload = <span class="str">'testImpossibleUpdate'</span>
<span class="kwrd">where</span> 1 = 2

<span class="kwrd">select</span> * <span class="kwrd">from</span> TestTriggerUpdateBehavior
</pre>
<p><strong>So, when should you use triggers?</strong></p>
<p>You should use triggers:</p>
<ul>
<li>When you have a clear understanding of how they work</li>
<li>You have no other option</li>
<li>You have performance tested your code thoroughly</li>
<li>You have informed your storage folks, DBA folks, and your mother</li>
<li>You have prayed about it</li>
</ul>
<p>In all honesty I&#8217;ve only seen a few clean uses for triggers:</p>
<ol>
<li>&#8220;audit trigger&#8221;: Audit mechanism for straight up insert/delete/update calls.  You have tableA, and you want to log all changes to auditTableA - a &#8220;copy trigger&#8221; does this quite well.</li>
<li>&#8220;refactor trigger&#8221;: A temporary bridge between two phases of a database refactor project.  You are migrating data from schema A to schema B, but that last pesty bit of code hasn&#8217;t been changed.  This release changed 80% of the code and put in a trigger to maintain the data or log to new tables for later testing the remaining 20%.  The discipline required to push through the 20% and remove the trigger is rare, so this is sometimes dangerous.</li>
<li>&#8220;trap trigger&#8221;: In a crisis, log where updates are coming from to a specific table.  Remove trigger quickly thereafter.</li>
<li>&#8220;evil trigger&#8221;: A trigger created for evil.</li>
</ol>
<p>Where can I find more information about triggers?</p>
<p><a href="http://msdn.microsoft.com/en-us/library/aa258254.aspx">Books online: Triggers.</a></p>
]]></content:encoded>
			<wfw:commentRss>http://davidtate.org/blog/?feed=rss2&amp;p=188</wfw:commentRss>
		</item>
		<item>
		<title>Triggers Part 2: Facts and Frequently-Occuring-Problems (FOP)</title>
		<link>http://davidtate.org/blog/?p=175</link>
		<comments>http://davidtate.org/blog/?p=175#comments</comments>
		<pubDate>Fri, 13 Mar 2009 03:07:16 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[sqlserver]]></category>

		<guid isPermaLink="false">http://davidtate.org/blog/?p=175</guid>
		<description><![CDATA[When are triggers fired, and how many times?
For your standard missionary position trigger, they are fired once per batch.  So if you have an update statement that affects 57 rows, then the DML AFTER trigger fires *once* but the deleted and inserted magic tables have 57 rows in them.  One common anti-pattern is [...]]]></description>
			<content:encoded><![CDATA[<p><strong>When are triggers fired, and how many times?</strong></p>
<p>For your standard missionary position trigger, they are fired once per batch.  So if you have an update statement that affects 57 rows, then the DML AFTER trigger fires *once* but the deleted and inserted magic tables have 57 rows in them.  One common anti-pattern is not handling multiple values in the inserted/deleted tables.  Code to prove the firing behavior of triggers:</p>
<pre class="csharpcode">
<span class="rem">-- Test trigger firing cardinality</span>
<span class="kwrd">if</span> <span class="preproc">object_id</span>(<span class="str">'TestTriggerFiring'</span>, N<span class="str">'U'</span>) &gt; 0
    <span class="kwrd">drop</span> <span class="kwrd">table</span> TestTriggerFiring

<span class="kwrd">if</span> <span class="preproc">object_id</span>(<span class="str">'TestTriggerAudit'</span>, N<span class="str">'U'</span>) &gt; 0
    <span class="kwrd">drop</span> <span class="kwrd">table</span> TestTriggerAudit

<span class="kwrd">if</span> <span class="preproc">object_id</span>(<span class="str">'tr_TestTriggerFiring'</span>, N<span class="str">'TR'</span>) &gt; 0
    <span class="kwrd">drop</span> <span class="preproc">trigger</span> dbo.tr_TestTriggerFiring

<span class="kwrd">create</span> <span class="kwrd">table</span> TestTriggerFiring
(
    KeyID <span class="kwrd">int</span> <span class="kwrd">primary</span> <span class="kwrd">key</span> <span class="kwrd">clustered</span>
    , Payload <span class="kwrd">varchar</span>(<span class="preproc">max</span>) <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    , LastUpdatedDate <span class="preproc">datetime</span> <span class="kwrd">NULL</span>
    , LastUpdatedBy sysname <span class="kwrd">NULL</span>
)

<span class="kwrd">create</span> <span class="kwrd">table</span> TestTriggerAudit
(
    TriggerName sysname
    , DateFired <span class="preproc">datetime</span> <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    , ActionType <span class="kwrd">varchar</span>(10) <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    , KeyID <span class="kwrd">int</span> <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    , Stamp uniqueidentifier
)

<span class="preproc">go</span>

<span class="kwrd">create</span> <span class="preproc">trigger</span> dbo.tr_TestTriggerFiring <span class="kwrd">on</span> dbo.TestTriggerFiring
<span class="kwrd">for</span> <span class="kwrd">insert</span>, <span class="kwrd">update</span>
<span class="kwrd">as</span>

<span class="kwrd">declare</span> @MyGuid uniqueidentifier
<span class="kwrd">set</span> @MyGuid = <span class="preproc">newid</span>()

<span class="kwrd">insert</span> <span class="kwrd">into</span> TestTriggerAudit (TriggerName, DateFired, ActionType, KeyID, Stamp)
<span class="kwrd">select</span> <span class="str">'tr_TestTriggerFiring'</span>, <span class="preproc">getdate</span>(), <span class="str">'inserted'</span>, i.KeyID, @MyGuid
<span class="kwrd">from</span> INSERTED i

<span class="preproc">go</span>

<span class="kwrd">insert</span> <span class="kwrd">into</span> TestTriggerFiring
<span class="kwrd">select</span> <span class="kwrd">top</span> 3 <span class="preproc">object_id</span>, name, <span class="kwrd">null</span>, <span class="kwrd">null</span>
<span class="kwrd">from</span> <span class="sqlkwrd">sys.objects</span>

<span class="preproc">go</span>

<span class="kwrd">insert</span> <span class="kwrd">into</span> TestTriggerFiring <span class="kwrd">values</span> (-1, <span class="str">'test4'</span>, <span class="kwrd">null</span>, <span class="kwrd">null</span>)
<span class="kwrd">waitfor</span> delay <span class="str">'00:00:001'</span>
<span class="preproc">go</span>
<span class="kwrd">insert</span> <span class="kwrd">into</span> TestTriggerFiring <span class="kwrd">values</span> (-2, <span class="str">'test5'</span>, <span class="kwrd">null</span>, <span class="kwrd">null</span>)
<span class="kwrd">waitfor</span> delay <span class="str">'00:00:001'</span>
<span class="preproc">go</span>
<span class="kwrd">insert</span> <span class="kwrd">into</span> TestTriggerFiring <span class="kwrd">values</span> (-3, <span class="str">'test6'</span>, <span class="kwrd">null</span>, <span class="kwrd">null</span>)
<span class="kwrd">waitfor</span> delay <span class="str">'00:00:001'</span>
<span class="preproc">go</span>

<span class="kwrd">select</span> * <span class="kwrd">from</span> TestTriggerAudit</pre>
<p><strong>What are the inserted/deleted magic tables?  How do they work?</strong></p>
<p>The inserted and deleted tables hold the date being changed.  Depending on what you are doing they hold different data:</p>
<p><em>Insert</em><br />
	Inserted – holds the new data<br />
	Deleted – is empty</p>
<p><em>	Update</em><br />
	Inserted – holds new data<br />
	Deleted – holds old data</p>
<p><em>	Delete</em><br />
	Deleted – data being deleted<br />
	Inserted – is empty</p>
<p>The inserted and deleted tables are not indexed, so take care in querying them in the wrong way.</p>
<p>Given the logic above, if possible it is normally cleaner to not combine an insert/update trigger.  If you have to, the below if my template for doing so:</p>
<pre class="csharpcode"><span class="kwrd">IF</span> <span class="preproc">OBJECT_ID</span>(<span class="str">'tr_Example_Update'</span>, <span class="str">'TR'</span>) &gt; 0
    <span class="kwrd">DROP</span> <span class="preproc">TRIGGER</span> dbo.tr_Example_Update
<span class="preproc">GO</span>

<span class="kwrd">CREATE</span> <span class="preproc">TRIGGER</span> dbo.tr_Example_Update <span class="kwrd">ON</span> dbo.Test
<span class="kwrd">FOR</span> <span class="kwrd">UPDATE</span>, <span class="kwrd">INSERT</span>
<span class="kwrd">AS</span> 

<span class="kwrd">IF</span> <span class="preproc">@@ROWCOUNT</span> = 0
<span class="kwrd">RETURN</span>

<span class="kwrd">SET</span> NOCOUNT <span class="kwrd">ON</span>
<span class="kwrd">IF</span> <span class="kwrd">UPDATE</span>(Field1) <span class="kwrd">OR</span> <span class="kwrd">UPDATE</span>(Field2)
<span class="kwrd">BEGIN</span>
    <span class="rem">-- Update</span>
    <span class="kwrd">IF</span> (<span class="kwrd">SELECT</span> <span class="preproc">COUNT</span>(1) <span class="kwrd">FROM</span> DELETED) &gt; 0
    <span class="kwrd">BEGIN</span>
        <span class="kwrd">print</span> <span class="str">'Update logic'</span>
    <span class="kwrd">END</span>
    <span class="kwrd">ELSE</span>
    <span class="kwrd">BEGIN</span>
        <span class="kwrd">PRINT</span> <span class="str">'Insert logic'</span>
    <span class="kwrd">END</span>
<span class="kwrd">END</span>

<span class="kwrd">SET</span> NOCOUNT <span class="kwrd">OFF</span>
<span class="preproc">GO</span></pre>
<p><strong>Are triggers fired on bulk insert?</strong><br />
No by default, but you can turn them on.  <a href="http://msdn.microsoft.com/en-us/library/ms187640.aspx">Controlling Trigger Execution When Bulk Importing Data</a></p>
<p><a href="http://msdn.microsoft.com/en-us/library/ms187640.aspx">Tune in for </a><a href="http://davidtate.org/blog/?p=188">Triggers Part 3: FAQ and FOP continued</a></p>
]]></content:encoded>
			<wfw:commentRss>http://davidtate.org/blog/?feed=rss2&amp;p=175</wfw:commentRss>
		</item>
		<item>
		<title>Triggers Part 1: Introduction to madness, plus whores</title>
		<link>http://davidtate.org/blog/?p=112</link>
		<comments>http://davidtate.org/blog/?p=112#comments</comments>
		<pubDate>Thu, 12 Mar 2009 17:07:59 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[sqlserver]]></category>

		<guid isPermaLink="false">http://davidtate.org/blog/?p=112</guid>
		<description><![CDATA[According to the standard developer canon about databases you should avoid triggers like you avoid wearing your &#8220;I love goto statements&#8221; t-shirt at the company Christmas party (again).  Knowledge of triggers is verboten, almost as bad as saying you love cursors.  Why are they such a bad idea, and why does the DBA let the [...]]]></description>
			<content:encoded><![CDATA[<p>According to the standard developer canon about databases you should avoid triggers like you avoid wearing your &#8220;I love goto statements&#8221; t-shirt at the company Christmas party (again).  Knowledge of triggers is verboten, almost as bad as saying you love cursors.  Why are they such a bad idea, and why does the DBA let the air out of your Oldsmobile Firenza tires whenever you write one?  Let&#8217;s explore these ideas, and leave you being grown up and driving a Oldsmobile for another day.</p>
<p>First, a brief overview of triggers if you aren&#8217;t familiar with them at all (lucky):</p>
<blockquote><p>Triggers are blocks of T-SQL that are run upon the firing of certain events within SQL Server, the most common one being an insert, update, or delete to a rows in a table.  They can also fire upon login, the creation of tables, and other system events.  Triggers have certain constraints that they have to live with such as not being able to return results or perform certain operations such as CREATE DATABASE, and they are fed only certain input - @@ROWCOUNT, the inserted and deleted tables, the ambient transaction.  They are typically complained at and about by DBAs and developers as being slow and hard to deal with.</p></blockquote>
<p><strong>Where do triggers come from?</strong></p>
<p>When a whore and a demon love each other they make a baby, and that baby is a trigger with a cursor in it that handles it own transactions and savepoints whilst trying to send email via a call to the smtp service using xp_cmdshell.</p>
<p><strong>Are triggers in the original relational model?</strong></p>
<p>No, but neither are isolation levels, recovery models, or stored procedures.  But never fear, very little that you have heard of is in the original relational model unless you married your sister and eat German chocolate bars in which case you are familiar with joined relations and relvars.</p>
<p><strong>Why are triggers given such a bad name - aren&#8217;t they just a certain type of stored procedure that happens at a certain time?</strong></p>
<p>Triggers are given a bad name for a few reasons:</p>
<p><em>What they are typically used for</em>: They are quite powerful and it is easy to overuse them for things that are normally better supported by the DBMS in other places.  The classic example of this is constraint or foreign key enforcement - a trigger can stop the insert/update from occurring or check the updated data right before the modification.  Business logic can live in a trigger, so that it runs &#8220;just in time&#8221;.  Another common pattern is to use a trigger to keep two separate databases in sync – every update to one causes an insert into another across linked server, etc.  Triggers are dark-alleyed shortcuts and there are normally better paths to build the functionality you want.</p>
<p><em>How hard they are to work with</em>:  Most developers don&#8217;t know how to properly code a trigger -  there I said it.  In addition, they are added T-SQL surface area that doesn&#8217;t show up in most tracing mechanisms for laymen (Activity Monitor, syscomments, indirectly in execution plans, a lot of source control systems, etc).</p>
<p><em>They are slow</em>: Triggers are inside a transaction doing an insert, update, delete.  This is the essential core of the database system - very close to the DBMS kernel in terms of your application usage.  Adding a trigger that takes half a second can bring your system to a messy halt.  In addition, in the past triggers populated the inserted and deleted tables via building a view from reading the transaction log.  This lead many DBAs to see non sequential behavior in their storage mechanisms that were built for sequential writes leading to even greater slowdowns.  As of SQL Server 2005 triggers are implemented via row versioning which thus makes use of the tempdb instead of reading the log, but DBAs never forget.  In either event, the behavior of triggers is simply different than other types of statements, leading DBAs to be even more unhappy - and they are pretty much unhappy in DisneyWorld.</p>
<p>Tune in for <a href="http://davidtate.org/blog/?p=175">Triggers Part 2 Facts and Frequently-Occuring-Problems (FOP)</a></p>
]]></content:encoded>
			<wfw:commentRss>http://davidtate.org/blog/?feed=rss2&amp;p=112</wfw:commentRss>
		</item>
		<item>
		<title>Read / Write by server and database</title>
		<link>http://davidtate.org/blog/?p=157</link>
		<comments>http://davidtate.org/blog/?p=157#comments</comments>
		<pubDate>Wed, 11 Mar 2009 00:07:49 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[sqlserver]]></category>

		<guid isPermaLink="false">http://davidtate.org/blog/?p=157</guid>
		<description><![CDATA[Modified from Jason Massie&#8217;s post, here are queries to tell you your read/write ratio per server and database.  Interesting results if you are maintaining multiple databases/application and are acting under the assumption that they behave similarly:

SELECT
    CAST(SUM(user_seeks+user_scans+user_lookups) AS decimal)
    /
    CAST(SUM(user_updates) + SUM(user_seeks+user_scans+user_lookups) AS decimal)
AS ReadPercent
 [...]]]></description>
			<content:encoded><![CDATA[<p>Modified from<a title="Jason Massie's" href="http://statisticsio.com/Home/tabid/36/articleType/ArticleView/articleId/324/Is-8020-a-90rsquos-Estimate.aspx"> </a><a href="http://statisticsio.com/Home/tabid/36/articleType/ArticleView/articleId/324/Is-8020-a-90rsquos-Estimate.aspx">Jason Massie&#8217;s post</a>, here are queries to tell you your read/write ratio per server and database.  Interesting results if you are maintaining multiple databases/application and are acting under the assumption that they behave similarly:</p>
<pre class="csharpcode">
<span class="kwrd">SELECT</span>
    <span class="preproc">CAST</span>(<span class="preproc">SUM</span>(user_seeks+user_scans+user_lookups) <span class="kwrd">AS</span> <span class="kwrd">decimal</span>)
    /
    <span class="preproc">CAST</span>(<span class="preproc">SUM</span>(user_updates) + <span class="preproc">SUM</span>(user_seeks+user_scans+user_lookups) <span class="kwrd">AS</span> <span class="kwrd">decimal</span>)
<span class="kwrd">AS</span> ReadPercent
    , <span class="preproc">CAST</span>(<span class="preproc">SUM</span>(user_updates) <span class="kwrd">AS</span> <span class="kwrd">decimal</span>) /
    <span class="preproc">CAST</span>(<span class="preproc">SUM</span>(user_updates)
    + <span class="preproc">SUM</span>(user_seeks + user_scans + user_lookups) <span class="kwrd">AS</span> <span class="kwrd">decimal</span>)
<span class="kwrd">AS</span> WriteRatio
<span class="kwrd">FROM</span> sys.dm_db_index_usage_stats

<span class="kwrd">SELECT</span> db.name
, <span class="preproc">CAST</span>(<span class="preproc">SUM</span>(user_seeks+user_scans+user_lookups) <span class="kwrd">AS</span> <span class="kwrd">decimal</span>)
/ <span class="preproc">CAST</span>(<span class="preproc">SUM</span>(user_updates)+<span class="preproc">SUM</span>(user_seeks+user_scans+user_lookups) <span class="kwrd">AS</span> <span class="kwrd">decimal</span>)
* 100 <span class="kwrd">AS</span> ReadPercent
, <span class="preproc">CAST</span>(<span class="preproc">SUM</span>(user_updates) <span class="kwrd">AS</span> <span class="kwrd">decimal</span>)
/
<span class="preproc">CAST</span>(<span class="preproc">SUM</span>(user_updates)
    + <span class="preproc">SUM</span>(user_seeks + user_scans + user_lookups) <span class="kwrd">AS</span> <span class="kwrd">decimal</span>)
    * 100 <span class="kwrd">AS</span> WriteRatio
<span class="kwrd">FROM</span> sys.dm_db_index_usage_stats t
<span class="kwrd">JOIN</span> <span class="sqlkwrd">sys.databases</span> db <span class="kwrd">on</span> db.database_id = t.database_id
<span class="kwrd">WHERE</span> (user_updates + user_seeks + user_scans + user_lookups) &gt; 0
<span class="preproc">GROUP</span> <span class="preproc">BY</span> db.name
<span class="kwrd">ORDER</span> <span class="preproc">BY</span> db.name</pre>
]]></content:encoded>
			<wfw:commentRss>http://davidtate.org/blog/?feed=rss2&amp;p=157</wfw:commentRss>
		</item>
	</channel>
</rss>
?>?>