 




<?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/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SQL Server with Mr. Denny &#187; Tables</title>
	<atom:link href="http://itknowledgeexchange.techtarget.com/sql-server/tag/tables/feed/" rel="self" type="application/rss+xml" />
	<link>http://itknowledgeexchange.techtarget.com/sql-server</link>
	<description></description>
	<lastBuildDate>Fri, 17 May 2013 17:04:01 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Deleting LOB Data and Shrinking the Database</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/deleting-lob-data-and-shrinking-the-database/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/deleting-lob-data-and-shrinking-the-database/#comments</comments>
		<pubDate>Wed, 13 Mar 2013 14:00:52 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[Andre Kamman]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[Database Administration]]></category>
		<category><![CDATA[DBCC PAGE]]></category>
		<category><![CDATA[Mladen Prajdić]]></category>
		<category><![CDATA[Paul Randal]]></category>
		<category><![CDATA[SQL Saturday]]></category>
		<category><![CDATA[SQL Saturday 194]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[SQL Server 2005]]></category>
		<category><![CDATA[SQL Server 2008]]></category>
		<category><![CDATA[SQL Server 2008 R2]]></category>
		<category><![CDATA[SQL Server 2012]]></category>
		<category><![CDATA[System Objects]]></category>
		<category><![CDATA[Tables]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/?p=2564</guid>
		<description><![CDATA[While attending SQL Saturday 194 in Exeter over in England one of the attendees came to Mladen Prajdić, Andre Kamman and myself with an interesting problem.  She had a database table which was about 200 Gigs in size which she wanted to delete about half of the data from the table.  The catch was that the [...]]]></description>
				<content:encoded><![CDATA[<p>While attending <a href="http://www.sqlsaturday.com/194/eventhome.aspx">SQL Saturday 194</a> in Exeter over in England one of the attendees came to Mladen Prajdić, Andre Kamman and myself with an interesting problem.  She had a database table which was about 200 Gigs in size which she wanted to delete about half of the data from the table.  The catch was that the database table was full of LOB data where the rows were very large, with an average LOB data size of over a meg.  She also needed to shrink the database after the database was deleted so that she could reclaim the space from the database.  Oh and all this had to be done on SQL Server 2005 Standard Edition. (Everything here applies to SQL Server up through SQL Server 2012 as well.)</p>
<p>Deleting the data from the database is the easy part, a simple delete loop will handle that nicely.  The problem is when you delete rows from a table which contains LOB data the LOB pages aren&#8217;t cleared when they are deallocated.  We can see this by running the following code.</p>
<p><code>CREATE DATABASE Lobtest<br />
GO<br />
use Lobtest<br />
GO<br />
CREATE TABLE t1 (c1 int IDENTITY(1,1) PRIMARY KEY, c2 ntext)<br />
GO<br />
INSERT INTO T1 (c2) VALUES (replicate('a', 20000))<br />
GO<br />
DBCC IND ('LobTest', 't1', 1)<br />
GO<br />
DBCC TRACEON(5201, -1)<br />
GO<br />
DELETE FROM t1<br />
GO<br />
DBCC IND ('LobTest', 't1', 1)<br />
GO<br />
DECLARE @dbid as int = db_id('Lobtest')<br />
DBCC PAGE (@dbid, 1, 231, 3)<br />
GO</code></p>
<p>You can see that page 231 is a LOB page which is allocated to the table t1. When you look at the actual page using DBCC PAGE after the row has been deleted we can see that there is data in the page, and that the page header shows that the page is still allocated to the table t1. This can be seen by looking in the header of the page for the header value labeled &#8220;Metadata: ObjectId = 245575913&#8243;.</p>
<p>When you go to shrink the database the SQL Server engine will get to the LOB pages and it will need to figure out if the LOB row is a part of a row which still exists or not. In order to do this SQL Server will need to scan through the pages which make up the table looking for any rows which reference the page it is trying to delete.</p>
<p>When doing shrinks after deleing large amounts of LOB data SQL Server will generate large amounts of IO while figuring this out and the shrink operation will take an extremely long time. (Paul Randle talks more about <a href="http://www.sqlskills.com/blogs/paul/why-lob-data-makes-shrink-run-slooooowly-t-sql-tuesday-006/">it here</a>.)</p>
<p>So the question that this person at SQL Saturday had was, how can I reclaim the space from my database within a reasonable time.</p>
<p>The solution that we came up with was actually pretty simple.  Do the database deletion as normal.  Then backup and restore the database.  Then do the shrink, followed by rebuilding the clustered indexes in order to fix the fragmentation issue which the shrink will introduce.</p>
<p>This works for a pretty simple reason, because the PFS page shows that the LOB page isn&#8217;t allocated even though the page is full of data (you can verify this by looking at page 1 in file 1 in the sample database created by the script above).  When the database engine backups up the database the database engine looks at the PFS pages to figure out which pages to back up.  Because the PFS pages show that the pages are empty the database engine doesn&#8217;t bother to backup the pages, so when the pages are restored they are restored as blank pages.  This means that after the restore the shrink operation can run without an issue.</p>
<p>In the case of this application there was a maintenance window which could be taken advantage of which would allow the backup and the restore to happen.</p>
<p>Another option which we came up with which would require less downtime involved using database mirroring.  By configuring database mirroring (which is initialized via a backup and restore process giving us the same basic approach) and then failing over to the mirror we would end up in the same position.  We could then shrink the database without issue (probably pausing database mirroring so that we didn&#8217;t have to wait for the second server to process the shrink in real time) and then fail back the database to the original server.</p>
<p>As geeky as it was, Mladen, Andre and I had a great time figuring this out, and the attendee had a great time watching us go through all the possible options as we excluded them one by one.  And most importantly she got her problem solved.</p>
<p>So if you end up in this situation here&#8217;s a solution that will help you shrink the database so that you can reclaim the space that the LOB data pages are taking up without having to wait forever.</p>
<p>Denny</p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/deleting-lob-data-and-shrinking-the-database/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>NOLOCK is not a turbo button</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/nolock-is-not-a-turbo-button/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/nolock-is-not-a-turbo-button/#comments</comments>
		<pubDate>Wed, 23 Jan 2013 09:00:26 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[CREATE INDEX]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[Indexing]]></category>
		<category><![CDATA[SELECT statement]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[T/SQL]]></category>
		<category><![CDATA[Tables]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/?p=1574</guid>
		<description><![CDATA[All to often when talking to developers they put the WITH (NOLOCK) table hint in place to speed up queries without understanding what the table hint does.  I&#8217;ve even run across companies that have policies in place that every select statement must have the WITH (NOLOCK) table hint. The WITH (NOLOCK) table hint isn&#8217;t a [...]]]></description>
				<content:encoded><![CDATA[<p>All to often when talking to developers they put the WITH (NOLOCK) table hint in place to speed up queries without understanding what the table hint does.  I&#8217;ve even run across companies that have policies in place that every select statement must have the WITH (NOLOCK) table hint.</p>
<p>The WITH (NOLOCK) table hint isn&#8217;t a go faster button for SQL Server.  It has actual implications to the data which is being returned by the query.  The biggest of these implications is that the data might not be correct.  You see the WITH (NOLOCK) table hint uses dirty reads to return the data, so it basically ignores the locks which other queries have taken.  This is why the query appears to run faster, because the query isn&#8217;t being blocked any more.  The proper approach would be to find the query which is causing the extended blocking and figure out why it is taking so long to run, and fix the performance problems of that query.</p>
<p>The only go faster button that is available in SQL Server is the CREATE INDEX statement.  Anything else isn&#8217;t truly a go faster button, and has other side effects which must be understood before being implemented.</p>
<p>Denny</p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/nolock-is-not-a-turbo-button/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Databases With Large Tables Should Use Auto Stats Async Update Feature</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/databases-with-large-tables-should-use-auto-stats-async-update-feature/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/databases-with-large-tables-should-use-auto-stats-async-update-feature/#comments</comments>
		<pubDate>Wed, 05 Dec 2012 14:00:17 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[Auto Update Statistics Asynchronously]]></category>
		<category><![CDATA[Auto Update Stats]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[Database Administration]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Statistics]]></category>
		<category><![CDATA[Tables]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/?p=2264</guid>
		<description><![CDATA[A pretty cool feature that was introduced in SQL Server 2008 R2 SP1 is the ability to change the auto stats algorithm from the default of 20%+500 rows to a sliding scale.  This feature is only available by turning on trace flag 2371 so it won&#8217;t be turned on by default for you. When turning [...]]]></description>
				<content:encoded><![CDATA[<p>A pretty cool feature that was introduced in SQL Server 2008 R2 SP1 is the ability to change the auto stats algorithm from the default of 20%+500 rows to a <a href="http://itknowledgeexchange.techtarget.com/sql-server/new-sql-2008-r2-sp1-trace-flag-adjusts-autostats-threshold/">sliding scale</a>.  This feature is only available by turning on trace flag 2371 so it won&#8217;t be turned on by default for you.</p>
<p>When turning on this trace flag because you&#8217;ve got a large database the goal obviously is to use autostats so you&#8217;ll need to have auto stats turned on.  In addition you&#8217;ll want to turn on the &#8220;Auto Update Statistics Asynchronously&#8221; setting for the database or databases which hold the super large table.  The reason that you&#8217;ll want to turn on the auto stats async update feature is that if you don&#8217;t you may see queries time out when auto stats starts to kick in.</p>
<p>Auto stats as we all know update the statistics when the correct number of rows in the table have changed.  When you&#8217;ve got very large tables that then trigger auto stats to run if it takes more than 30 seconds for the update stats command to run the query that triggered the auto stats to time out, which causes that transaction to roll back, which means that the auto stats command will also roll back.  So the next query will then fire the auto stats update and the process will repeat over and over.</p>
<p>The symptoms that you&#8217;ll see on the SQL Server are queries which are timing out at random even though the execution plan looks totally normal.  You&#8217;ll also see a massive amount of IO being generated on the disks which are hosting the database as auto stats does a lot of querying of the table and as auto stats is running over and over you&#8217;ll be thrashing the hard drive pretty quickly.</p>
<p>When you turn on the async auto stats setting on the database when the auto stats is triggered by the SQL Server the query in question doesn&#8217;t have to wait for the update stats command to finish.  Instead the update statistics command runs in the background letting the query continue to run as normal.  Now the query will run using the old statistics which is probably OK in this case as they were ok 2 seconds earlier so if they are used for a few more seconds it probably isn&#8217;t all that big of a deal.</p>
<p>Now I don&#8217;t recommend turning this setting on for every database on the server.  All of the smaller databases will update statistics just fine within the timeout period.</p>
<p>So the table that I ran across in my case where I had to turn this setting on took over 2 minutes in order to manually run update statistics on the table so using synchronous statistics updates via auto stats was basically useless.</p>
<p>Denny</p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/databases-with-large-tables-should-use-auto-stats-async-update-feature/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Fixing a table that has overflowed its primary key</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/fixing-a-table-that-has-overflowed-its-primary-key/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/fixing-a-table-that-has-overflowed-its-primary-key/#comments</comments>
		<pubDate>Wed, 21 Nov 2012 09:00:38 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[Clustered Index]]></category>
		<category><![CDATA[Data Types]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[Database Administration]]></category>
		<category><![CDATA[Database Design]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Stored Procedures]]></category>
		<category><![CDATA[T/SQL]]></category>
		<category><![CDATA[Tables]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/?p=2179</guid>
		<description><![CDATA[During TechEd Europe I got a very scary phone call.  A very large table was throwing errors that the value being inserted into the primary key column was overflowing the data type that makes up that column.  In this case the data type in question was INT, so we were trying to stick the number [...]]]></description>
				<content:encoded><![CDATA[<p>During TechEd Europe I got a very scary phone call.  A very large table was throwing errors that the value being inserted into the primary key column was overflowing the data type that makes up that column.  In this case the data type in question was INT, so we were trying to stick the number 2,147,483,648 into the column and that number just doesn’t fit.  The error that we got looked something like this:</p>
<blockquote><p>System.Data.SqlClient.SqlException: Error 8115, Level 16, State 1, Procedure <em>SomeStoredProcedure</em>, Line 103, Message: Arithmetic overflow error converting IDENTITY to data type int.  Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.</p></blockquote>
<p>Ourshort term solution was quick and easy, to change identity seed of the column from 0 to the lowest possible number, in this case -2,147,483,648 which got us back up and running pretty quickly.  This however isn’t a very long term solution for this system.  The application is only about 3 or 4 years old, and has been growing very quickly over the last year or so.  We estimated based on the last few weeks worth of data that we would run out of negative numbers within about 12 months at the most.  We sent an estimate of 9 months to the business when we advised them that the system was back up and running.  We also told them that we wanted this fixed within 5-6 months to be save because if we didn’t get this fixed before running out of negative numbers there wouldn’t be any short term fix and that we’d be looking at a multi-day outage to fix the problem.</p>
<p>We couldn’t just rush into a database side fix, as the fix in this case is to change the data type from INT to BIGINT.  As the application does use this column in a couple of places the .NET application needed to be reviewed to ensure that anything that was looking for an INT was corrected to handle the BIGINT correctly.</p>
<p>Based on the amount of data within the table (about 300 Gigs) it was decided that taking an outage to make the change in place wasn’t really an option as doing the size change in place would require somewhere around a 5 day outage to remove and rebuild all the non-clustered indexes.  To make things a little more complex there is a second table which has a 1=1 relationship with this table, and the second table is even larger (about 800 Gigs), though thankfully the second table doesn’t have any non-clustered indexes.</p>
<p>The solution that was decided on was to move the data from the current table to a table with a BIGINT data type while the application was running.  To do this meant that we needed to get all the data copied over to the new table and in sync while the old table was being used.  It was decided that the easiest way to do this would be to use triggers.  In this case instead of one complex trigger to handle all insert, update and delete operations three separate triggers were used for each of the two tables.  First I created two new tables, with the exact same schema as the old tables, with the exception that the new tables used the bigint data type for the primary key instead of the int data type for the primary key and the new table was setup with the ident being the next available positive number.  Once that was done the triggers were setup.  The insert trigger is pretty basic.  Take the data that was just loaded and stick it into the new table.</p>
<blockquote><p>CREATE TRIGGER t_MyTable1_insert ON dbo.MyTable1<br />
FOR INSERT<br />
AS<br />
SET IDENTITY_INSERT MyTable1_bigint ON<br />
INSERT INTO MyTable1_bigint<br />
(Col1, Col2, Col3, Col4…)<br />
SELECT Col1, Col2, Col3, Col4<br />
FROM inserted<br />
SET IDENTITY_INSERT MyTable1_bigint OFF<br />
GO</p></blockquote>
<p><span style="color: #000000;">The update and delete triggers required a little more logic.  The trick with the triggers was that I needed to avoid doing massive implicit data conversions.  In order to ensure that SQL was doing what I wanted (which it should be doing anyway, but it made me feel better doing explicit conversions) I explicit conversions into place for the JOIN predicates as shown.  The update trigger is shown first, then the delete trigger.</span></p>
<blockquote><p>CREATE TRIGGER t_MyTable1_update ON dbo.MyTable1<br />
FOR UPDATE<br />
AS<br />
UPDATE MyTable1_bigint<br />
SET MyTable1_bigint.[Col2] = inserted.[Col2],<br />
MyTable1_bigint.[Col3] = inserted.[Col3],<br />
MyTable1_bigint.[Col4] = inserted.[Col4],<br />
…<br />
FROM inserted<br />
WHERE cast(inserted.Col1 as bigint) = MyTable1_bigint.Col1<br />
GO</p></blockquote>
<blockquote><p>CREATE TRIGGER t_MyTable1_delete ON dbo.MyTable1<br />
FOR DELETE<br />
AS<br />
DELETE FROM MyTable1_bigint<br />
WHERE Col1 in (SELECT cast(Col1 as bigint) FROM deleted)<br />
GO</p></blockquote>
<p><span style="color: #000000;">Once these tables were up and running all the new data changes were being loaded into the table.  At this point it was just a matter of coping the existing data into the table.  There are a few ways that this can be done.  In this case I opted for an SSIS package with a single data pump task, and two data transformations within the data pump task with one transformation for each table.</span></p>
<p><span style="color: #000000;">In order to make the load as fast as possible I used the fast load option and loaded 1000 rows at a time.  Within the data task if there was an error I redirected the rows to another data pump task which simply dropped the rows into the same table, but this time going row by row.  Any failures from that import were simply ignored.  While handling failed rows like this is time consuming it is easier than running T-SQL scripts to verify which rows are needed and which rows aren’t needed.  SSIS also gives an easy option to ignore the foreign key relationship between the two tables so if the child table gets rows first that isn’t a problem as we know that the parent table will catch up.  The SSIS package looked like this:</span></p>
<p><span style="color: #000000;"><a href="http://itknowledgeexchange.techtarget.com/sql-server/fixing-a-table-that-has-overflowed-its-primary-key/ssis/" rel="attachment wp-att-2186"><img class="alignnone size-full wp-image-2186" src="http://cdn.ttgtmedia.com/ITKE/uploads/blogs.dir/20/files/2012/08/ssis.jpg" alt="" width="480" height="365" /></a></span></p>
<p><span style="color: #000000;">When all is said and done and the data is in sync between the new and old tables, the current tables will be dropped and the new tables will be renamed and put into place so that the application can continue to run without issue, with just a few minutes of downtime.</span></p>
<p><span style="color: #000000;">So why did this happen?  When the applications database was being designed the developers didn’t think about how many rows the database was going to get over time, so they didn’t account for needing to support more than 2.1 billion rows over time.  If I (or another data architect) had been involved in the project at it’s start this hopefully would have been caught at design time.  However when the application was first being designed the company was brand new and didn’t have the funds for a data architect to help with the application design so this problem was missed.</span></p>
<p><span style="color: #000000;">Hopefully you never hit this problem, but if you do this helps you get out of it.</span></p>
<p><span style="color: #000000;">Denny</span></p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/fixing-a-table-that-has-overflowed-its-primary-key/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Encrypting data in the same column</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/encrypting-data-in-the-same-column/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/encrypting-data-in-the-same-column/#comments</comments>
		<pubDate>Wed, 10 Oct 2012 09:00:00 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[Data Encryption]]></category>
		<category><![CDATA[Data Loss]]></category>
		<category><![CDATA[Data Security]]></category>
		<category><![CDATA[Data Types]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[Database Administration]]></category>
		<category><![CDATA[Database Design]]></category>
		<category><![CDATA[Database security]]></category>
		<category><![CDATA[Encryption]]></category>
		<category><![CDATA[Identity theft]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[SQL Server stored procedures]]></category>
		<category><![CDATA[Stored Procedures]]></category>
		<category><![CDATA[T/SQL]]></category>
		<category><![CDATA[Tables]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/?p=2175</guid>
		<description><![CDATA[I wrote a little while ago about the fact that sensitive data needs to be encrypted within the database for all applications.  This is the first technique that is available to you to encrypt data in a database with as little outage as possible. In this technique we’ll encrypt the data using just a single [...]]]></description>
				<content:encoded><![CDATA[<p>I wrote a little while ago about the fact that sensitive <a href="http://itknowledgeexchange.techtarget.com/sql-server/sensitive-data-must-be-encrypted/">data needs to be encrypted</a> within the database for all applications.  This is the first technique that is available to you to encrypt data in a database with as little outage as possible.</p>
<p>In this technique we’ll encrypt the data using just a single column.  This technique requires butting some additional logic within the application to figure out if the value is encrypted or not, but other than that logic, which you can leave in and strip out later the changes to the application are pretty minimal as the column stays the same, so that means that the stored procedures don’t need to be changed.</p>
<p>The first thing to remember is that the encrypted data will be larger, possibly much larger than the plain text version of the data.  Because of this you’ll need to increase the size of the field which you’ll be putting the data into.  Now the good news is that if this column isn’t indexed this change should be pretty quick and easy as it should just be a meta change which tells the SQL Server that the column size can be bigger without having to actually change the pages.  You can see this by making some changes to the [HumanResources].[Employee] table within the AdventureWorks database.  By turning on STATISTICS IO and using the ALTER TABLE statement we see that there is no IO generated when we change the size of the LoginID column from nvarchar(256) to nvarchar(512).</p>
<blockquote><p>set statistics io on<br />
alter table MyTable<br />
alter column LoginID nvarchar(512)</p></blockquote>
<p>Once the column is made larger the .NET code needs to be modified to see if the data is compressed for not.  Now there is no sure fire way to check to see if a value has been encrypted or not, but a pretty good test is to look at the last two characters of the value.  If they are both an equal sign (==) then it is probably safe to assume that the value is encrypted.  To don’t want to just attempt to decrypt the data and look for an error message, and if there is an error assume that the encrypted value is in plain text, throwing and catching error messages in .NET is very expensive, especially compared to simply checking to see if the last two characters are an equal sign.  This isn’t to say that you shouldn’t have TRY/CATCH logic around the code that decrypts the values as someone could easily enough put two equal signs at the end of their password.</p>
<p>At this point either a .NET app or a T-SQL script can loop through the values in the table which aren’t encrypted and then encrypt them, updating the rows which aren’t already encrypted.</p>
<p>Look for more blog posts in this series on how to encrypt data which already exists within your applications database.</p>
<p>Denny</p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/encrypting-data-in-the-same-column/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Changing the default owner when creating objects</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/changing-the-default-owner-when-creating-objects/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/changing-the-default-owner-when-creating-objects/#comments</comments>
		<pubDate>Mon, 20 Jul 2009 11:00:00 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[Permissions]]></category>
		<category><![CDATA[SQL Server 2005]]></category>
		<category><![CDATA[SQL Server 2008]]></category>
		<category><![CDATA[T/SQL]]></category>
		<category><![CDATA[Tables]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/changing-the-default-owner-when-creating-objects/</guid>
		<description><![CDATA[When a user that doesn&#8217;t have sysadmin rights creates objects by default they will be created in the schema that is the users default schema.  Now the catch to this is that if you grant the user rights into the database via a domain group that domain group then the user doesn&#8217;t have a default [...]]]></description>
				<content:encoded><![CDATA[<p>When a user that doesn&#8217;t have sysadmin rights creates objects by default they will be created in the schema that is the users default schema.  Now the catch to this is that if you grant the user rights into the database via a domain group that domain group then the user doesn&#8217;t have a default schema.</p>
<p>So, now how do you fix this?  Unfortunately the only fix to this is to grant the users Windows login as a separate login, then grant this login rights into the database.  You can then grant the user which is mapped directly to the users Windows login a default schema of dbo.</p>
<p>Because of this the user should specify the schema when creating objects.</p>
<p>The downside to this is that they won&#8217;t be able to use the object editor to create new tables.  All new tables will need to be created in T/SQL directly.</p>
<p>Denny</p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/changing-the-default-owner-when-creating-objects/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Non-sysadmins create tables under own schema</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/non-sysadmins-create-tables-under-own-schema/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/non-sysadmins-create-tables-under-own-schema/#comments</comments>
		<pubDate>Thu, 16 Jul 2009 11:00:00 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[Enterprise Manager]]></category>
		<category><![CDATA[SQL Server 2000]]></category>
		<category><![CDATA[T/SQL]]></category>
		<category><![CDATA[Tables]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/non-sysadmins-create-tables-under-own-schema/</guid>
		<description><![CDATA[When using SQL Server 2000 and the user doesn&#8217;t have sysadmin rights, and their login isn&#8217;t mapped to the dbo user within the database all objects created will be, by default created under the user schema. This is the normal behavior of SQL Server 2000.  In order to allow users who are not members of [...]]]></description>
				<content:encoded><![CDATA[<p>When using SQL Server 2000 and the user doesn&#8217;t have sysadmin rights, and their login isn&#8217;t mapped to the dbo user within the database all objects created will be, by default created under the user schema.</p>
<p>This is the normal behavior of SQL Server 2000.  In order to allow users who are not members of the sysadmin fixed server role to create objects under the dbo schema by default you have to map their login to the dbo user, even if they are a member of the dbo fixed database role.</p>
<p>To work around this, in the T/SQL code specify the owner of the database object.  If your developer is using Enterprise Manager to create the new tables before saving the table, click on the properties button in the upper left hand corner of the Enterprise Manager window (second from the left).  Then change the owner drop down from their username to dbo.  There is no way to default this setting to dbo so it will need to be changed for each new table being created.</p>
<p>Denny</p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/non-sysadmins-create-tables-under-own-schema/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A better way to index text data</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/a-better-way-to-index-text-data/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/a-better-way-to-index-text-data/#comments</comments>
		<pubDate>Mon, 21 Jul 2008 11:00:15 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[Index Performance]]></category>
		<category><![CDATA[Query tuning]]></category>
		<category><![CDATA[SELECT statement]]></category>
		<category><![CDATA[T/SQL]]></category>
		<category><![CDATA[Tables]]></category>
		<category><![CDATA[Uni-code]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/a-better-way-to-index-text-data/</guid>
		<description><![CDATA[Indexing text data (varchar, nvarchar, char, etc) is a good way to make it faster to find the data you are looking for.  However these indexes can end up being very hard on the disks behind the index, as well as the memory of the server.  This post shows a couple of techniques which can be used to increase index performance which decreasing the storage requirement.]]></description>
				<content:encoded><![CDATA[<p>Indexing text data (varchar, nvarchar, char, etc) is a good way to make it faster to find the data you are looking for.  However these indexes can end up being very hard on the disks behind the index, as well as the memory of the server.  This is because of the large amount of data being put in the index.</p>
<p>As an example, let&#8217;s say that we have a table like this.</p>
<p><code>CREATE TABLE Employee<br />
(EmployeeID INT,<br />
FirstName VARCHAR(50),<br />
LastName VARCHAR(50),<br />
EmailAddress VARCHAR(255))</code></p>
<p>Now assume that you want to be able to search by the EmailAddress field.  We will then want to index the EmailAddress field with a non-clustered index.  If we work for a company like AMD, then our email addresses will be pretty short (<a href="mailto:f.lastname@amd.com">f.lastname@amd.com</a>).  However if we work for a company like I work for then the email addresses are a bit longer (<a href="mailto:flastname@awarenesstechnologies.com">flastname@awarenesstechnologies.com</a>).  Now when we index this column we will be putting the entire email address into the index, taking up a lot of space within the index; especially compared to a numeric value such as an integer.  This becomes doubly true if you are using a uni-code data type as each character requires two bytes of storage instead of the usual one.</p>
<p>This also becomes a problem if you are working on a system with URLs in the field to be indexes.  Depending on the length of the URL, the values may be longer than is allowed in an index which could then give you sorting problems on the indexes.</p>
<p>There are a couple of variations on this technique which I&#8217;ve seen.  The one I&#8217;ve used the most is to use the CHECKSUM function as part of a calculated column, and then index the calculated column.  This way you simply get the CHECKSUM of the value you want to find, and search the calculated column.  As we are now have an index made up of integers the index can fit a lot more data on each physical data page reducing the IO cost of the index seek as well as saving space on the disk.</p>
<p>So doing this turns our table into something like this.</p>
<p><code>CREATE TABLE Employee<br />
(EmployeeID INT,<br />
FirstName VARCHAR(50),<br />
LastName VARCHAR(50),<br />
EmailAddress VARCHAR(255),<br />
EmailAddressCheckSum AS CHECKSUM(EmailAddress))</code></p>
<p>Now I wouldn&#8217;t recommend using this technique for each table you create.   I usually only recommend a technique like this when the value to be indexes won&#8217;t fit within the bounds of the index, or the table will be very large and searched often so the memory saved is worth the extra CPU time of having to hash the values before doing the lookup.</p>
<p>Now there are a couple of gotchas with this technique.  If you are check summing domain names, some characters don&#8217;t check sum correctly.  Also check summing a Unicode version of a string will give you a different result than the non-unicode version of the same string.</p>
<p>You can see that with these three SELECT statements.</p>
<p>SELECT CHECKSUM(&#8216;google.com&#8217;), CHECKSUM(&#8216;g-oogle.com&#8217;)<br />
SELECT CHECKSUM(&#8216;google.com&#8217;), CHECKSUM(N&#8217;google.com&#8217;)<br />
SELECT CHECKSUM(N&#8217;google.com&#8217;), CHECKSUM(N&#8217;g-oogle.com&#8217;)</p>
<p>As you can see the first one you get two different values as you would expect ( 1560309903 and 1560342303 respectively).  With the second query you get two very different values between the Unicode and character strings (1560309903 and -1136321484 respectively).  Based on the first query you would expect to get two different values for the third query, but you don&#8217;t.  With the Unicode strings the &#8211; appears to not count as part of the CHECKSUM giving you the same CHECKSUM value for both strings (-1136321484).</p>
<p>Another version of this technique which <a target="_blank" href="http://sqlblog.com/blogs/kevin_kline/default.aspx" title="Kevin Kline">Kevin Kline</a> talked about recently uses the HASHBYTES function of SQL Server 2005 to get the hash of a column and use that.  In his <a target="_blank" href="http://sqlblog.com/blogs/kevin_kline/archive/2008/04/21/implementing-a-hash-partition-in-sql-server-2005.aspx" title="Implementing A Hash Partition in SQL Server 2005">blog</a> he&#8217;s talking about using it for partitioning a table, but that same technique can be used here as well.</p>
<p><code>CREATE TABLE Employee<br />
(EmployeeID INT,<br />
FirstName VARCHAR(50),<br />
LastName VARCHAR(50),<br />
EmailAddress VARCHAR(255),<br />
EmailAddressCheckSum AS HASHBYTES('SHA1', EmailAddress)</code></p>
<p>This will however give you a longer string, therefor taking up more space within the index. However if working with long Unicode strings this may be a better option for you to use.</p>
<p>Denny</p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/a-better-way-to-index-text-data/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Using batching to do large operations without filling the transaction log</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/using-batching-to-do-large-operations-without-filling-the-transaction-log/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/using-batching-to-do-large-operations-without-filling-the-transaction-log/#comments</comments>
		<pubDate>Mon, 14 Jul 2008 11:00:21 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[DELETE statement]]></category>
		<category><![CDATA[Query tuning]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Tables]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/using-batching-to-do-large-operations-without-filling-the-transaction-log/</guid>
		<description><![CDATA[Deleting large amounts of data from a table is usually an important task, but if you don&#8217;t have a maintenance window to work within then you can end up causing locking and blocking against the other processes which are accessing the database.  Not to mention you will cause a very large amount of data into [...]]]></description>
				<content:encoded><![CDATA[<p>Deleting large amounts of data from a table is usually an important task, but if you don&#8217;t have a maintenance window to work within then you can end up causing locking and blocking against the other processes which are accessing the database.  Not to mention you will cause a very large amount of data into the transaction log no matter what your transaction logging level is set to.</p>
<p>Say you have a table with a date column and you need to delete a million plus records.  Doing this in a single transaction will put all million transactions into your transaction log, plus cause any other processes which are trying to access the table to be blocked.</p>
<p> However if we batch the transaction into many smaller transactions our transaction log will not fill up as we can backup the log using our normal log backup methods throughout the process, or if we use SIMPLE recovery on our database then transactions will be removed from the log automatically.</p>
<p>In SQL 2000 and below you have to set the ROWCOUNT session variable to a number above 0, which would cause SQL to delete the first <em>n </em>records that it comes across.  In SQL 2005 we can use the TOP parameter as part of our DELETE command having the same effect, but without having to reset the session variable.</p>
<p>In SQL 2000 or below you can use a syntax like this one.</p>
<p><code>DECLARE @c BIT, @d DATETIME<br />
SET @d = dateadd(yy, -1, getdate())<br />
SET @c = 0<br />
SET ROWCOUNT = 1000<br />
WHILE @c = 0<br />
BEGIN<br />
DELETE FROM Table<br />
WHERE CreateDate  </p>
<p>If you are using SQL 2005 you can use this very similar syntax.</p>
<p><code>DECLARE @c BIT, @d datetime<br />
SET @d = dateadd(yy, -1, getdate())<br />
SET @c = 0<br />
WHILE @c = 0<br />
BEGIN<br />
DELETE TOP (1000) FROM Table<br />
WHERE CreateDate </p>
<p>Both pieces of code are very similar. Declare a variable which tells the loop when to exit. Then start deleting the data. If no records are deleted, then set the variable to 1 causing us to exit the loop. Now this will usually take a little bit longer to complete than a single delete statement, but the system will continue to be responsive during the process.</p>
<p>The number of records which you are deleting should be adjusted based on the width of your records, the load on the database at the time of deletion, and the speed of your hard drives which hold the data files and transaction logs. I usually start at 1000 records and see how the system responds. For tables which a just a few numbers I'll put it up as high as 50k or 100k records. For very wide tables I'll drop it down to 100 or 500 records if the system can't handle 1000 records.</p>
<p>Denny</p>
<p>Update:<br />
Sorry this post didn't look very good at first.  The blog site seams to have eaten it, and I didn't notice until just now.</p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/using-batching-to-do-large-operations-without-filling-the-transaction-log/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Back To Basics: Tables, without them we have nothing to do</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/back-to-basics-tables-without-them-we-have-nothing-to-do/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/back-to-basics-tables-without-them-we-have-nothing-to-do/#comments</comments>
		<pubDate>Thu, 10 Apr 2008 10:00:11 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[Back To Basics]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Tables]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/back-to-basics-tables-without-them-we-have-nothing-to-do/</guid>
		<description><![CDATA[Tables are the core of any database platform.  Without tables we would be able to process data, but we would have no way to store the data.  In the simplest terms tables look like Excel sheets.  They both have columns and rows.]]></description>
				<content:encoded><![CDATA[<p>Tables are the core of any database platform.  Without tables we would be able to process data, but we would have no way to store the data.  In the simplest terms tables look like Excel sheets.  They both have columns and rows.  When you view a table in the client tools it looks much like an Excel sheet does.  While the basic concept is the same tables are very, very different that Excel sheets.  SQL Server is optomized to store large quanties and data and search through that data as quickly as possible.</p>
<p>Unlike Excel, tables have indexes.  Indexes are copies of the column which comprise the index.  The reason that we use indexes is to speed up the processing of the query.  Whle the index is a copy of the column, the copy within the index in sorted in order while the table is not.  Because the data is sorted SQL Server can search through the data much easier and there for faster.</p>
<p>To make this easier to understand think of an Excel sheet with 1000 rows of data in it.  Each row has a single value in column A.  Each value is a random number between 1 and 1000.  Now try to find all the rows with the value of 25.  You need to search down the column looking for the data.  This is how your table work.  When you tell SQL Server to search the table for the value of 25 using this statement SQL has to look at every record in the table.</p>
<p><code>SELECT *<br />
FROM Table<br />
WHERE Column1 = 25</code></p>
<p>Now when you create an index on this column think of sorting the Excel sheet in order.  Now find the records with the value of 25.  You can simply scan down the column and find the records and not look any further.  This is the same thing that SQL Server does when you us the same command but with the index.  SQL Server uses the index automatically, so no changes to code are required when you create the index.</p>
<p>Denny</p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/back-to-basics-tables-without-them-we-have-nothing-to-do/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
