OK, we were in Iceland and then Florida for our Accidental DBA workshops and both went really well. People agree that there are quite a few involuntary/accidental DBAs out there and overall, we helped quite a few to see a lot of options for better performance, availability, recovery, and/or just manageability.

So, this is our "resources post". We waited until after the SQL Connections delivery to post these as we figured we might add a few more to the list (as is typical when you deliver content more than once - it's really never the same twice!).

Also, I used a few "interactive" (or build) slides in my presentation - specifically on transaction log backups and the concepts of "clearing the log" which really only clears the inactive portion of the log. To help you visualize this, I've added these slides here: TrippRandal_ClearingTheLog-BuildSlides.zip (647.2 KB).

Finally, we've taken all of the scripts that we demo'ed and placed them on SQLskills on our Past Events page here: http://www.sqlskills.com/pastConferences.asp.

And, if you were there and you think we missed something, feel free to ping me (or Paul!) with an email and we'll make sure to update this resources post (and/or [at least] help you find it what you're looking for!!).

Next stop - Microsoft TechEd ITPro in June (we're back in Orlando again)!
kt

Hey there everyone! I know I still owe you a few Q&A entries (for sessions 7, 8 and 9) but I wanted to get this blog entry out there so that you can play a bit with some of the resources. This series was targeted at developers but really helps to "bridge the gap" between development and administration by always remembering the impacts of what you implement (and techniques to help you to see if you do). More specifically, everything you do and/or design, has the potential for a negative impact to something else - there's no free lunch, eh?

So, this series focused more on Scalability but always remembered the impact to availability and/or reliability. This last session brought together the three primary areas to remember while developing a scalable and reliable architecture:

  1. Know your data
    • Design for Performance - Sessions 1, 2, 3, 6, and 8
  2. Know your users
    • Indexing for Performance - Sessions 4, 5 and 9
    • Optimizing Procedural Code - Session 7
    • Controlling Mixed Workloads and Concurrency - Session 6
  3. Users lie
    • Profile - to make sure that you're tuning what's really happening as opposed to what you think was going to happen! - Session 9

This last session had some great questions and as a result, a few new resources were used. Here are a few of the things we talked about:

Event Notifications and DDL Triggers

DMVs

Webcast links for the entire series!

Part 1: Creating a Recoverable Database
For the MSDN Download for Part 1, click here.
For the SQLskills Blog Entry for Part 1, click here.

Part 2: Creating a Reliable and Automated Backup Strategy
For the MSDN Download for Part 2, click here.
For the SQLskills Blog Entry for Part 2, click here.

Part 3: Designing Tables that Scale, Best Practices in Data Types and Initial Table Structures
For the MSDN Download for Part 3, click here.
For the SQLskills Blog Entry for Part 3, click here.

Part 4: SQL Server Indexing Best Practices
For the MSDN Download for Part 4, click here.
For the SQLskills Blog Entries for Part 4
Resource links blog entry, click here.
Q&A blog entry, click
here.

Part 5: SQL Server Index Defrag Best Practices
For the MSDN Download for Part 5, click here.
For the SQLskills Blog entry, click here.

Part 6: Mixed Workloads, Secondary Databases, Locking and Isolation
For the MSDN Download for Part 6, click here.
For the SQLskills Blog Entry for Part 6, click here.

Part 7: Understanding Plan Caching and Optimizing Procedure Performance
For the MSDN Download for Part 7, click here.

Part 8: Data Loading and Aging Strategies - Table and Index Partitioning
For the MSDN Download for Part 8, click here.

Part 9: Profiling for Better Performance
For the MSDN Download for Part 9, click here.

Part 10: Session Summary - Common Roadblocks to Scalability
For the MSDN Download for Part 10, click here.
Transcript can be found here.

So, the series comes to an end (even though I still have more work to do). I have to say that it was a lot of fun and I enjoyed everyone's questions. And /start shameless plug here/ starting in March, SQLskills will begin a 10-12 part series on TechNet. The series will include sessions from my colleague Bob Beauchemin as well as me. This will definitely be more Operations and DBA focused but for all of you developers - it may help you better understand the system, High Availability and a myriad of New Features in SQL Server 2005.

I hope to see you there - or at least your DBA... ;-)

Thanks again everyone,

Kimberly

This is a much needed and much overdue blog entry... In 8 Steps to Better Transaction Log throughput, I mentioned a customer that was helped by TWO typical optimization problems I see. In that blog entry, I said I would write two blog entries - that one on transaction log optimization and another on common tempdb optimizations. Well, I forgot...until I was reminded with an email this morning (thanks Marcus!).

First - a bit of understanding of TempDB - what goes there?

  • Internal temporary objects needed by SQL Server in the midst of other complex operations. For example, worktables created by a hash aggregate will be stored in TempDB or interim tables uses in hash joins (almost anything that shows as "hash" something in your query plan output is likely to go to tempdb).
  • User objects created with either # (for local temporary objects), ## (globabl temporary objects) or @ (table variables)
    • # = Local temporary object
      Local temp objects are objects accessible ONLY in the session that created it. These objects are also removed automatically when the session that created it ends (unless manually dropped).
    • ## = Globabl temporary object
      Global temporary objects are objects that are accessible to ANYONE who can login to your SQL Server. They will only persist as long as the user that created it lasts (unless manually dropped) but anyone who logs in during that time can directly query, modify or drop these temporary objects. These objects are also removed automatically when the session that created it ends (unless manually dropped) OR if being used by another session when the session that created it ends, when the session using it finishes using it (and it's only as long as any locks are held). If other sessions need more permanent use of a temporary object you should consider creating a permanent objects and dropping it manually.
    • @ = User-defined Table Variable
      User-defined Table Variables were introduced in SQL Server 2000 (or, wow - was it 7.0?) and provide an alternative to temporary tables by allowing you to create a variable defined as type TABLE and then you can populate and use it in a variety of ways. There has been A LOT of debate over whether or not you should always use table variables or always use temp tables. My response is that I ALWAYS avoid the word always! My point is that table variables are NOT always better nor are temp tables always better. There are key uses to each. I tend to like temp tables in scenarios where the object is used over a longer period of time - I can create non-key indexes on it and it's more flexible to create to begin with (SELECT INTO can be used to create the temp table). I also have the ability to use the temporary table in nested subprocedures because it's not local to the procedure in which it was created. However, if you don't need any of those things then a table variable might be better. When it is likely to be better - when you have smaller objects that don't need to be accessed outside of the procedure in which it was created and when you only need KEY indexes (a table variable ONLY supports the indexes created by a create table statement - meaning PRIMARY KEY and UNIQUE KEY).
  • Objects created by client applications - this is possibly a large part of your problem... Profiling can help you to determine if there's a lot of TempDB usage from your client applications.

OK, so now that you know what goes there - how do you make it optimal?

First and foremost, TempDB is in cache just as any other database is in cache. TempDB does not spill to disk unless you are low on cache and/or if you have a lot of inflight transactions in TempDB. Although TempDB is not persisted from shutdown to restart - it still needs to do some logging and therefore you should consider its optimization a lot like other databases.

Things you should do for TempDB (that are a lot like what you should do for every database):

  1. Isolate the data and log portion of TempDB.
  2. Place them on clean, defragmented disks.
  3. Pre-allocate them so they don't need to do a lot of autogrowth.
  4. Make sure you have sufficient memory to support active objects (check for disk activity to the disks that contain TempDB files).
  5. Make sure that transactions are written efficiently so that there are no unusually long running transactions that are unnecessarily holding resources (and therefore locks and therefore log activity).

And - if you need to move TempDB, you should review this KB Article: Moving SQL Server databases to a new location with Detach/Attach

Things you should do SPECIFICALLY for TempDB (especially if you're running on a multiproc machine):

Before I say what... let me tell you why? TempDB has a large number of objects being created all the time. For an object to be created, space must be allocated to it. Space allocation is determined by looking at some of the internal system pages (the GAM, and SGAM). In the end, it is these pages that start to have significant contention (with just one file) in a VERY active TempDB. To minimize that contention you can create multiple files.

  1. Consider creating multiple files for TempDB (even if on the same physical disks) so that there is less of a bottleneck when objects are being allocated. Make sure to read associated KB.
  2. Consider setting a trace flag to have object allocation grab extents rather than pages. Make sure to read associated KB.

BOTH of these last two are described in detail by a KB article: FIX: Concurrency enhancements for the tempdb database.

OK - so that should really help! Moving forward (meaning SQL Server 2005), having multiple files can still help for TempDB.

Scott Stanfield, a Microsoft Regional Director, had a great idea for Tech*Ed US (well, it was a great idea before he knew exactly how much work was involved :)...

He decided that he would get other Microsoft Regional Directors together to discuss (and record) interesting topics for just 10 minutes (of course, some of us went over... SORRY!). We could pick anything we wanted and then he would - with the help of a few other RDs such as Scott Golightly, Patrick "Beach Master" Hynds, Kate Gregory, and J. Michael Palermo - record our sessions to later edit and post on the GrokTalk site.

So, after A LOT OF HARD WORK - they're done! All 35 of the GrokTalks are posted and you can find them on the GrokTalk site: http://www.groktalk.net/

Finally, since I delivered a GrokTalk on Stored Procedures, I'm going to make sure that you have a comprehensive set of resources related to stored procedure optimizations here:

Well, now that should keep you busy...

Enjoy!!

Many of you in the .NET Community are already familar with theServerSide.NET but many of you in the SQL Server community are not...well, here's your chance to bridge the gap over to .NET and hear a bunch of great .NET interviews! My interview is more SQL-centric but there are lots of great ones on Development in general and even one on SQL Server 2005 Reporting Services by Jason Carlson.

My interview was recorded with them back in February when I was speaking at VSLive in San Francisco and they've been saving them up, editing them and then posting an interview roughly each week (mine is the 29th posted!). The interview was great fun and Paul Ballard asked some really interesting questions about tuning, indexes, stored procedures and SQL Server 2005. Mostly it's an interview about optimization tips in both SQL Server 2000 and 2005 and just in general - things to look forward to in SQL Server 2005.

Here's a link to all of theServerSide.NET interviews: http://www.theserverside.net/talks/index.tss 
Here's a link specifically to mine: http://www.theserverside.net/talks/videos/KimberlyTripp/interview.tss?bandwidth=dsl
Here's the link to Jason Carlson's Reporting Services interview: http://www.theserverside.net/talks/videos/JasonCarlson/interview.tss?bandwidth=dsl and finally, here's a TechNet Webcast that Jason did titled: Authoring Reports in SQL Server 2000 Reporting Services (Level 200)

Enjoy!

Well... the performance ramifications are... not good!

Without seeing more of the proc I'd have to suggest that you avoid this like the plague! I think I can convince you with this simple example which shows why it's better to have multiple procedures and limit the flexibility a bit OR use DSE = dynamic string execution (and unfortunately even recompilation issues don't help - which I talked about here)...

And - if we go down the path of DSE I'm sure that will start a separate thread as far as "sql injection" issues/attacks and security (the user executing the procedure will need permission to directly execute the statement which is in the DSE string). But - there are a few ways to protect parameters in a stored procedure. In this procedure, the parameters will not allow “injection” because of the addition of the QUOTENAME function. This function is INVALUABLE for protecting isolated input values (which is another trick: isolate parameters if you can).

In the following “multi-purpose” procedure it becomes obvious (after testing) that ONLY DSE solves the performance problem. I could still argue that more than one procedure could be beneficial especially as it wouldn't have the access requirements that this one has (remember - users would have to have permissions to the base table). BUT that's a lot more work to maintain/call, etc. Regardless, one procedure with “all purpose parameters” is definitely NOT a good choice (without DSE). (And fyi - a future release might help with some of the permissions issues - that's all I'll say for now! :)

OK - so see the ProcedureParameters.sql (4.3 KB) procedure to play with this within the CreditSampleDB.zip (55.79 KB). You will need to modify some data to get rows to return to these queries; use the updates in the .sql script.

CREATE PROC dbo.GetMemberInfoParam
(
 @Lastname varchar(30) = NULL,
 @Firstname varchar(30) = NULL,
 @member_no int = NULL
)
AS
SET NOCOUNT ON

SELECT m.*
FROM dbo.member AS m
WHERE (m.lastname LIKE @lastname OR @lastname IS NULL)
 AND (m.member_no = @member_no OR @member_no IS NULL)
 AND (m.firstname LIKE @firstname OR @firstname IS NULL)
go

This next procedure uses dynamic string execution to build the exact and needed string. You could have built this from the client as well... and that would introduce other sql-injection issues as well. But - this is a simpe idea and I hope this helps.

CREATE PROC dbo.GetMemberInfoParamDSE
(
 @Lastname varchar(30) = NULL,
 @Firstname varchar(30) = NULL,
 @member_no int = NULL
)
AS
SET NOCOUNT ON

IF @LastName IS NULL AND @FirstName IS NULL AND @Member_no IS NULL
 RAISERROR ('You must supply at least one parameter.', 16, -1)

DECLARE @ExecStr varchar(1000)
  , @MemberNoStr varchar(100)

SELECT @ExecStr = 'SELECT m.* FROM dbo.member AS m WHERE '

IF @LastName IS NOT NULL
 SELECT @Lastname = 'm.lastname LIKE ' + QUOTENAME(@lastname, '''')
IF @FirstName IS NOT NULL
 SELECT @Firstname = 'm.firstname LIKE ' + QUOTENAME(@firstname, '''')
IF @Member_no IS NOT NULL
 SELECT @MemberNoStr = 'm.member_no = ' + convert(varchar(5), @member_no)

SELECT @ExecStr = @ExecStr + ISNULL(@LastName, ' ')
 +
 CASE
  WHEN @LastName IS NOT NULL AND @FirstName IS NOT NULL
   THEN ' AND '
  ELSE ' '
 END
 +
 ISNULL(@FirstName, ' ')
 +
 CASE
  WHEN (@LastName IS NOT NULL OR @FirstName IS NOT NULL)
     AND @MemberNoStr IS NOT NULL
   THEN ' AND '
  ELSE ' '
 END
 +
 ISNULL(@MemberNoStr, ' ')

EXEC(@ExecStr)
go

The first procedure generates a plan based on the MOST selective criteria (there's more info in the script and you'll see this in the showplan/statistics io output that I describe - also in the script). The second procedure generates a good plan for each and every execution (as expected). I think this is a good example of what you can do!

I'm going to put together one more entry to bring all of these points together as well. Stay tuned!

Have fun!

After a group of RDs started chatting about stored procedures I finally felt like I could join in. Often they talk about .Net and client stuff and well, quite honestly, I don't care what you do with the data...only that I serve it up quickly. Ok, I'm really kidding here but my primary focus is backend server tuning and availability, so when the group started talking about stored procedures, I had to chime in. It all started because someone had blogged that stored procedures are not precompiled and are therefore of no use... which is COMPLETELY wrong. However, the irony is that I don't even believe that they should be compiled (and saved and therefore reused) ALL the time. In fact, a precompiled plan is also an optimized plan and that plan may or may not always be the best plan for every execution....... So, here's a bunch of stuff about sprocs. You definitely want to use them - but use them effectively!!!!
 
Benefits of stored procedures:
  • Centralized logic which can be changed with minimal client impact
  • Logic on the server so roundtrips are minimized
  • Compiled plans saved in cache. Not every plan is saved, not every plan should be saved. However, the default is that stored procedure plans are compiled and saved - on first exection. The plan is NOT created when the stored procedure is created (I've also seen this written up incorrectly before) NOR is this plan saved permanently. There are many reasons for why the procedure's plan may fall out of cache:
    • Server restart
    • Falls out of cache due to low re-use (and not enough cache to keep it around)
    • Specifically being removed from cache by:
      • Executing DBCC FREEPROCCACHE to kick ALL plans out of cache (fyi - you can see what's in cache by querying master.dbo.syscacheobects)
      • DATA on which the procedure depends changing enough to cause the statistics to be invalidated and therefore sql server proactively invalidate the plan
      • Executing an sp_recompile (on the object(s) on which the procedure depends. This last one is also nice and something I sometimes force (off hours when a lot of indexes are being rebuilt and/or added). You can execute sp_recompile tname and it will cause all plans which access this table to be invalidated.
Stored Procedures for Security:
  • Stored Procedures can be secure AND easier to manage - in terms of permissions.
  • If I am the owner of a table and I create a procedure based on my table - I can give people access to the procedure withOUT giving base table permissions. This will allow better security and minimize the accidental "oh darns" when someone forgets a where clause on a delete. Imagine a user who types in DELETE Sales WHERE invnum = 1234 BUT highlights ONLY the DELETE Sales...whose problem is that? Mine as the DBA. We can all argue that users should not have ad-hoc access to the server (and I agree) BUT stored procedures create a layer of abstraction which is ALWAYS good especially if you are using DYNAMIC SQL.
Dynamic sql has many connotations... There is "Dynamic String Execution" which can be WITHIN a stored procedure and there's Dynamic SQL which is what an application builds and sends off to the server. As for a client who sends an ad-hoc string to the server or for a dynamically built string within a stored procedure - BOTH REQUIRE that the user have the ability to execute the command directly - which means there's more room for error. However, if it doesn't need to be in a dynamic string (and many statements can be parameterized) then it could be in a stored procedure WITHOUT dynamic string exection withOUT the possible room for HUGE SQL injection problems/errors. Speaking of SQL Injection - IF you use stored procedures and mostly only allow for Identifiers as parameters (i.e. tablename, viewname column names, etc.) then you can use the QUOTENAME() function to significantly reduce SQL injection.
Caching (in General) for better Performance:
 
There are really three areas that need to be understood to really get the issues related to stored procedures:
  • Ad-hoc statement caching (that was new in SQL Server 7.0 and higher)
  • Forced statement caching (through sp_executesql)
  • Stored procedure caching (by creating stored procedures)
Ad-hoc Statement Caching
When a statement is deemed "safe" sql server will take EVEN ad-hoc statements and store a plan of execution in cache for subsequent users to use. For the plan to be re-used the statement has to be almost an identical match, the parameter has to be the EXACT same data type, the objects cannot be ambiguous and MOST statements won't really benefit from this. If you want to see an example of ad-hoc statement caching do the following:
-- 1) Clear Cache with
DBCC FREEPROCCACHE

-- 2) Look at what executable plans are in cache
SELECT sc.*
FROM master.dbo.syscacheobjects AS sc
WHERE sc.cacheobjtype = 'Executable Plan'

-- 3) execute the following statement
SELECT t.*
FROM pubs.dbo.titles AS t
WHERE t.price = 19.99

-- 4) Look again at what executable plans are in cache and you'll find that there's a
--     plan for a NUMERIC(4,2) (look at the "sql" column in output - far RIGHT)

SELECT sc.*
FROM master.dbo.syscacheobjects AS sc
WHERE sc.cacheobjtype = 'Executable Plan'
 
-- 5) If you execute the EXACT same statement with a 4,2 then you will get THAT plan
--     but if you execute with a 5,2 you'll get a new plan (the plan is not safe). Execute this:

SELECT t.*
FROM pubs.dbo.titles AS t
WHERE price = 199.99

-- 6) Look again at what executable plans are in cache...

SELECT sc.*
FROM master.dbo.syscacheobjects AS sc
WHERE sc.cacheobjtype = 'Executable Plan'
OK - so the fact that SQL Server can cache the plan is good... How often is something actually deemed safe - well, it's not that likely. A better way of getting better plan re-use IS either of the two following BUT in these two cases it's ALWAYS cached - which might not ALWAYS be good....
 
Forced Statement Caching (through sp_executesql)
This is good IF you know the plans are consistent (more on this coming up) and IF you want to type the data more strictly. Remember how SQL Server had to type the data above (numeric(4,2) or numeric(5,2), etc.). Well here you can type the parameters and force the plan to be cached. All subsequent uses will get the same plan:
DECLARE @ExecStr    nvarchar(4000)
SELECT @ExecStr = N'SELECT t.* FROM dbo.titles AS t WHERE t.price = @price'
EXEC sp_executesql @ExecStr, N'@price money', 19.99
BUT this falls into the category of being hard to know and there are better ways with stored procedures. Generally, I recommend stored procedures over statement caching...
 
Stored procedure caching (by creating stored procedures)
OK - I could go on for hours here and I'll make it sufficient to say that NOT all procedures should be cached but MANY should. The ONLY way to get the security and consistency desired from stored procedures is to know when to save them and when not to. It also requires that you typically have more smaller stored procedures rather than fewer larger procedures (i.e. modular code is a KEY component to better performance). Anyway, having said this... IF the first person who executes the procedure (and there isn't a plan for the procedure already in cache (and just to make this even more clear - stored procedure plans - when saved - are ONLY in cache they are NEVER saved to disk)) then a plan will be generated - and used for ALL subsequent users (unless a recompile is forced or occurs due to issues in third bullet in first section :). So now that there's a plan - will that plan be perfect...not necessarily. I have a great script that shows this and it's hard to explain over a short blog but the key point is this:  (I took this small section from yet another of my emails so forgive the duplication):
I would say that forced statement caching and stored procedure caching have the same problem(s) and that's that NOT all statements should be cached. There are certain strategies that can help to minimize costly overhead and there are certainly tips/tricks that we use to see if a plan should be saved or not. I think the number one thing I'd recommend is more modular procedures where the data returned SIGNIFICANTLY varies. The key reason is that smaller procedures tend to be more controllable. And - when a complex procedure is separately out then you can set that smaller more modular proc to be recompiled on each execution. That's probably the easiest way. There are certainly other things you can do to more effectively determine the cause of the problem (and that would be better) but I'd say that forcing recompilation on a smaller piece of code that generates widely varying sizes/sets of data is a good and generic solution.
So - having gotten through all of this... If you really want to play with this A LOT more below are a couple of scripts that I think are VERY useful. I use these in my lectures on Optimizing Stored Procedure performance. Regardless, this is the correct order to read/review/learn these scripts:
 
Create the Credit database (you can use CreditSampleDB.zip (55.79 KB) to create it)
Use "RecompilationIssues.sql (3.67 KB)" to get insight into bad plans being created/saved/re-used
Use "ModularProcedures.SQL (4.28 KB)" to get insight into what happens even with procedures that use conditional logic - not that there's anything wrong with that?!

Theme design by Nukeation based on Jelle Druyts