8 – Finding data corruption 210 Figure 8.4 shows the output from rerunning DBCC PAGE, with this trace flag turned on. Figure 8.4: DBCC PAGE with trace flag 3604 turned on. At the bottom of the output I can see that pages 1:172 – 1:383 are not allocated, and all pages are 0% full. Recall, this is a database with no tables or any other objects created and with no data inserted. So, let's now create a simple table and insert some data into it. The script to do this in is shown in Listing 8.1. It creates a table in the NEO database, called ONE, and inserts into it 1000 records (well, 999 really). Simple stuff, but the important point in the context of this example is that this data load will cause additional pages to be allocated to the database and be filled with data, and I'll be able to home in on these new pages. 8 – Finding data corruption 211 USE [NEO] GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ONE]') AND type in (N'U')) DROP TABLE [dbo].[ONE] GO CREATE TABLE [dbo].[ONE]( [NEOID] [int] NULL, [NEOTEXT] [nchar](50) NULL ) ON [PRIMARY] GO BEGIN Tran T_Time DECLARE @SQL_Alphabet varchar(26) SET @SQL_Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' DECLARE @rnd_seed int SET @rnd_seed = 26 DECLARE @counter int = 1 WHILE @counter < 1000 BEGIN Insert Into ONE Values ( @counter, (select SUBSTRING (@SQl_alphabet, Cast(RAND() * @rnd_seed as int) + 1, CAST(RAND() * @rnd_seed as int) + 1) ) ) SET @counter = @counter + 1 END Commit Tran T_Time Listing 8.1. Creating and populating the ONE table. Figure 8.5 shows the sample data that was inserted. 8 – Finding data corruption 212 Figure 8.5: Sample data in the ONE table. From Figure 8.4, I already know that, for our empty database, pages 1:172 – 1:383 were unallocated. Re-running DBCC PAGE should reveal that more pages have been allocated to accommodate this data, and that those pages have different percentages of fullness. Figure 8.6 shows the new results. 8 – Finding data corruption 213 Figure 8.6: New Pages added to NEO database after loading data. I can see that pages 1:184 – 1:189, for example, are now allocated and are 100 percent full. Having identified one of the new pages (1:184) that contains the data that I just loaded, I can run DBCC PAGE again for that specific page and return a basket full of information, as shown in Figure 8.7. 8 – Finding data corruption 214 Figure 8.7 Individual records from page 1:184. I can see, for example, that it returns the actual value for both NEOID and NEOTEXT, 553 and UVWXYZ respectively. It also returns a hex dump (10006c00 29020000…) that specifies the specific location in the data file where the record with NEOID 533 is stored. If you are not an expert in reading hexadecimal then fear not; neither am I at this point. I do know, however, that using this information I will be able to find this exact same record and modify it outside of SQL Server, which will really wreak some havoc. For that however, I will need my trusty hexadecimal editor, which I will discuss shortly. . [NEOTEXT] [nchar](50) NULL ) ON [PRIMARY] GO BEGIN Tran T_Time DECLARE @SQL_ Alphabet varchar(26) SET @SQL_ Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' DECLARE @rnd_seed int SET. using this information I will be able to find this exact same record and modify it outside of SQL Server, which will really wreak some havoc. For that however, I will need my trusty hexadecimal. WHILE @counter < 1000 BEGIN Insert Into ONE Values ( @counter, (select SUBSTRING ( @SQl_ alphabet, Cast(RAND() * @rnd_seed as int) + 1, CAST(RAND() * @rnd_seed as int) + 1) )