ptg 754 When you look at the definition of the CodeTable table in Object Explorer, you see the TableDesc column displayed with the ShortDescription data type as well as the underly- ing data type varchar(20). You can use the Object Explorer to create user-defined data types as well. To do so, you right-click the User-Defined Data Types node, then select Programmability, and then select Types. Then you choose the New User-Defined Data Type option, and you can create a new user-defined data type through a friendly GUI screen. If you create a user- defined data type in the model database, this user-defined data type is created in any newly created database. CLR User-Defined Types SQL Server 2008 continues support for user-defined types (UDTs) implemented with the Microsoft .NET Framework common language runtime (CLR). CLR UDTs enable you to extend the type system of the database and also enable you to define complex struc- tured types. A UDT may be simple or structured and of any degree of complexity. A UDT can encapsu- late complex, user-defined behaviors. You can use CLR UDTs in all contexts where you can use a system type in SQL Server, including in columns in tables, in variables in batches, in functions or stored procedures, as arguments of functions or stored procedures, or as return values from functions. A UDT must first be implemented as a managed class or structure in any one of the CLR languages and compiled into a .NET Framework assembly. You can then register it with SQL Server by using the CREATE ASSEMBLY command, as in the following example: CREATE ASSEMBLY latlong FROM ‘c:\samplepath\latlong.dll’ After registering the assembly, you can create the CLR UDTs by using a variation of the CREATE TYPE command shown previously: CREATE TYPE latitude EXTERNAL NAME latlong.latitude CREATE TYPE longitude EXTERNAL NAME latlong.longitude When a CLR UDT is created, you can use it in the definition of tables. The following example shows a table created with the new latitude and longitude UDTs: CREATE TABLE [dbo].StoreLocation (StoreID int NOT NULL, StoreLatitude latitude, StoreLongitude longitude) For more details on programming and defining CLR UDTs, see Chapter 45, “SQL Server and the .NET Framework.” CHAPTER 24 Creating and Managing Tables Download from www.wowebook.com ptg 755 Defining Columns 24 Column Properties Name and data type are the most basic properties of a column, but many other properties can be defined for a column. You do not have to specify these properties to be able to create the columns, but you can use them to further refine the type of data that can be stored within a column. Note that many of the available column properties relate to indexes and constraints that are beyond the scope of this section. The following sections describe some of the column properties you are most likely to encounter. The NULL and NOT NULL Keywords When you are defining tables, it is always good idea to explicitly state whether a column should or should not contain nulls. You do this by specifying the NULL or NOT NULL keywords after the column data type. If the nullability option is not specified, the SQL Server default is to allow nulls unless the ANSI_NULL_DFLT_OFF option is enabled for the session or no setting is specified for the session, and the ANSI_NULL_DEFAULT option for the database is set to OFF. Because of this uncertainty, it is best to always explicitly specify the desired nullability option for each column. Listing 24.4 creates a new table named PrinterCartridge that has the NULL or NOT NULL property specified for each column. LISTING 24.4 Defining Column NULL Properties by Using CREATE TABLE CREATE TABLE dbo.PrinterCartridge ( CartridgeId int NOT NULL, PrinterID int NOT NULL, CartridgeName varchar(50) NOT NULL, CartridgeColor varchar(50) NOT NULL, CartrideDescription varchar(255) NULL, InstallDate datetime NOT NULL ) GO NOTE It is beyond the scope of this section to debate whether columns should ever allow nulls. In some organizations, nulls are heavily used, and in others they are not allowed. There is no right answer, but it is important for a development team to be aware of the existence of nulls so that it can create appropriate code to handle them. Identity Columns A property commonly specified when creating tables is IDENTITY. This property automati- cally generates a unique sequential value when it is assigned to a column. It can be assigned only to columns that are of the following types: Download from www.wowebook.com ptg 756 . decimal . int . numeric . smallint . bigint . tinyint Only one identity column can exist for each table, and that column cannot allow nulls. When implementing the IDENTITY property, you supply a seed and an increment. The seed is the starting value for the numeric count, and the increment is the amount by which it grows. A seed of 10 and an increment of 10 would produce values of 10, 20, 30, 40, and so on. If not specified, the default seed value is 1, and the increment is 1. Listing 24.5 adds an IDENTITY value to the PrinterCartridge table used in the previous example. LISTING 24.5 Defining an Identity Column by Using CREATE TABLE IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N’dbo.PrinterCartridge’) AND OBJECTPROPERTY(id, N’IsUserTable’) = 1) DROP TABLE dbo.PrinterCartridge CREATE TABLE dbo.PrinterCartridge ( CartridgeId int IDENTITY (1000, 1) NOT NULL, PrinterID int NOT NULL, CartridgeName varchar(50) NOT NULL, CartridgeColor varchar(50) NOT NULL, CartrideDescription varchar(255) NULL, InstallDate datetime NOT NULL ) GO insert PrinterCartridge (PrinterID, CartridgeName, CartridgeColor, CartrideDescription, InstallDate) values (1, ‘inkjet’, ‘black’,’laser printer cartridge’, ‘8/1/09’) select CartridgeId, PrinterID, CartridgeName from PrinterCartridge /* results from previous SELECT statement CartridgeId PrinterID CartridgeName 1000 1 inkjet */ CHAPTER 24 Creating and Managing Tables Download from www.wowebook.com ptg 757 Defining Columns 24 In this listing, the seed value has been set to 1000, and the increment has been set to 1. An insert into the PrinterCartridge table and a subsequent SELECT from that table follows the CREATE TABLE statement in the listing. Notice that the results of the SELECT show a value of 1000 for the identity column CartridgeID. This is the seed or starting point that is defined. ROWGUIDCOL Columns An alternative to an identity column is a column defined with the ROWGUIDCOL property. Like the IDENTITY property, the ROWGUIDCOL property is autogenerating and unique. The difference is that the ROWGUIDCOL option generates column values that will be unique on any networked database anywhere in the world. The identity column generates values that are unique only within the table that contains the column. You can have only one ROWGUIDCOL column per table. You must create this ROWGUIDCOL column with the uniqueidentifier data type, and you must assign a default of NEWID()to the column to generate the unique value. Keep in mind that users can manually insert values directly into columns defined as ROWGUIDCOL. These manual inserts could cause duplicates in the column, so a UNIQUE constraint should be added to the column as well to ensure uniqueness. Listing 24.6 shows the creation of a table with a ROWGUIDCOL column. Several rows are inserted into the newly created table, and those rows are selected at the end of the listing. LISTING 24.6 Defining a ROWGUIDCOL Column CREATE TABLE SomeUniqueTable (UniqueID UNIQUEIDENTIFIER DEFAULT NEWID(), EffectiveDate datetime ) GO INSERT INTO SomeUniqueTable (EffectiveDate) VALUES (‘7/1/09’) INSERT INTO SomeUniqueTable (EffectiveDate) VALUES (‘8/1/09’) GO select * from SomeUniqueTable /* Results from previous select statement UniqueID EffectiveDate 614181BC-D7B9-4108-B2BD-C2F39E999424 2009-07-01 00:00:00.000 62368A2D-3557-4727-9DD3-FBCA38705B1B 2009-08-01 00:00:00.000 */ You can see that the ROWGUIDCOL values are fairly large. They are 16-byte binary values that are significantly larger than most of the data types used for identity columns. For example, an identity column defined as data type int occupies only 4 bytes. You need to consider the storage requirements for ROWGUIDCOL when you select this data type. Download from www.wowebook.com ptg 758 Computed Columns A computed column is a column whose value is calculated based on other columns. Generally speaking, the column is a virtual column because it is calculated on the fly, and no value is stored in the database table. With SQL Server 2008, you have an option of actually storing the calculated value in the database. You do so by marking the column as persisted. If the computed column is persisted, you can create an index on this column as well. Listing 24.7 includes several statements that relate to the creation of a computed column. It starts with an ALTER TABLE statement that adds a new computed column named SetRate to the Sales.CurrencyRate table in the AdventureWorks2008 database. The new rate column is based on an average of two other rate columns in the table. A SELECT state- ment is executed after that; it returns several columns, including the new SetRate computed column. The results are shown after the SELECT. Finally, an ALTER TABLE state- ment is used to change the newly added column so that its values are stored in the data- base. This is accomplished with the ADD PERSISTED option. LISTING 24.7 Defining a Computed Column Add a computed column to the Sales.CurrencyRate Table named SetRate ALTER TABLE Sales.CurrencyRate ADD SetRate AS ( (AverageRate + EndOfDayRate) / 2) go Select several columns including the new computed column select top 5 AverageRate, EndOfDayRate , SetRate from sales.currencyrate /*Results from previous SELECT statement AverageRate EndOfDayRate SetRate 1.00 1.0002 1.0001 1.5491 1.55 1.5495 1.9379 1.9419 1.9399 1.4641 1.4683 1.4662 8.2781 8.2784 8.2782 */ Alter the computed SetRate column to be PERSISTED ALTER TABLE Sales.CurrencyRate alter column SetRate ADD PERSISTED CHAPTER 24 Creating and Managing Tables Download from www.wowebook.com ptg 759 Defining Columns 24 NOTE You c an use the sp_spaceused stored procedure to check the space allocated to the Sales.CurrencyRate table. You need to check the size before the column is persist- ed, and then you need to check the space allocated to the table after the column is persisted. As you would expect, the space allocated to the table is increased only after the column is persisted. FILESTREAM Storage SQL Server 2008 introduces FILESTREAM storage for storing unstructured data, such as documents, images, and videos. In previous versions of SQL Server, there were two ways of storing unstructured data. One method was to store it in the database as a binary large object (BLOB) in an image or varbinary(max) column. The other method was to store the data outside the database, separate from the structured relational data, storing a reference or pathname to the unstructured data in a varchar column in a table. Neither of these methods is ideal for unstructured data. FILESTREAM storage helps to solve the issues with using unstructured data by integrating the SQL Server Database Engine with the NTFS file system for storing the unstructured data, such as documents and images, on the file system with the database storing a pointer to the data. Although the actual data resides outside the database in the NTFS file system, you can still use T-SQL statements to insert, update, query, and back up FILESTREAM data, while maintaining transactional consistency between the unstructured data and corre- sponding structured data with same level of security. To specify that a column should store data on the file system when creating or altering a table, you specify the FILESTREAM attribute on a varbinary(max) column. This causes the Database Engine to store all data for that column on the file system, but not in the data- base file. After you complete these tasks, you can use Transact-SQL and Win32 to manage the FILESTREAM data. NOTE To us e FILESTREAM storage, you must first enable FILESTREAM storage at the Windows level as well as at the SQL Server Instance level. You can enable FILESTREAM at the Windows level during installation of SQL Server 2008 or at any time using SQL Server Configuration Manager. After you enable FILESTREAM at the Windows level, you next need to enable FILESTREAM for the SQL Server Instance. You can do this either through SQL Server Management Studio or via T-SQL. For more information on enabling and using FILESTREAM storage, see Chapter 42. Sparse Columns and Column Sets SQL Server 2008 provides a new space-saving storage option referred to as sparse columns. Sparse columns are ordinary columns that provide optimized storage for null values. If the value of a column defined as a sparse column is NULL, it doesn’t consume any space at all. Download from www.wowebook.com ptg 760 You can define a column as a sparse column by specifying the SPARSE keyword after the data type in the CREATE TABLE or ALTER TABLE statement, as shown in Listing 24.8. LISTING 24.8 Specifying a Sparse Column in a Create Table Statement CREATE TABLE DBO.SPARSE_TABLE (ID INT IDENTITY(1,1), FIRST_NAME VARCHAR (50), MIDDLE_NAME VARCHAR (50) SPARSE NULL, LASTNAME VARCHAR (50) ) The space savings of sparse columns come with a trade-off, however, requiring extra space for storing non-null values in the sparse column. Fixed-length and precision data types require 4 extra bytes, and variable-length data types require 2 extra bytes. For this reason, you should consider using sparse columns only when the space saved is at least 20% to 40%. SQL Server stores sparse columns in a single XML column that appears to external applica- tions and end users as a normal column. Storing the sparse columns in a single XML column allows up to 30,000 sparse columns in a single table, exceeding the limitation of 1,024 columns if sparse columns are not used. In addition, because sparse columns have many null-valued rows, they are good candidates for filtered indexes. A filtered index on a sparse column can index only the rows that have non-null values stored in the column. This creates smaller and more efficient indexes. (For more information on filtered indexes, see Chapters 25, “Creating and Managing Indexes,” and 34, “Data Structures, Indexes, and Performance.”) Sparse columns can be of any SQL Server data type and behave like any other column with the following restrictions: . A sparse column must be nullable and cannot have the ROWGUIDCOL or IDENTITY properties. A sparse column cannot be of the following data types—text, ntext, image, timestamp, user-defined data type, geometry, or geography—or have the FILESTREAM attribute. . A sparse column cannot have a default value. . A sparse column cannot be bound to a rule. . A computed column cannot be marked as sparse. . A sparse column cannot be part of a clustered index or a unique primary key index. When the number of sparse columns in a table is large, and operating on them individu- ally is cumbersome, you may want to define a column set. A column set is an untyped XML representation that combines all the sparse columns of a table into a structured set. A column set is like a calculated column in that the column set is not physically stored in the table, but the column set is directly updatable. Applications may see some perfor- CHAPTER 24 Creating and Managing Tables Download from www.wowebook.com ptg 761 Defining Table Location 24 mance improvement when they select and insert data by using column sets on tables that have lots of columns. To define a column set, use the <column_set_name> FOR ALL_SPARSE_COLUMNS keywords in the CREATE TABLE or ALTER TABLE statements, as shown in Listing 24.9. LISTING 24.9 Defining a Column Set CREATE TABLE emp_info (ID INT IDENTITY(1,1), FIRST_NAME VARCHAR (50), MIDDLE_NAME VARCHAR (50) SPARSE NULL , LASTNAME VARCHAR (50), HOMEPHONE VARCHAR(10) SPARSE NULL, BUSPHONE VARCHAR(10) SPARSE NULL, CELLPHONE VARCHAR(10) SPARSE NULL, FAX VARCHAR(10) SPARSE NULL, EMAIL VARCHAR(30) SPARSE NULL, WEBSITE VARCHAR(30) SPARSE NULL, CSet XML COLUMN_SET FOR ALL_SPARSE_COLUMNS ) A column set is created as an untyped XML column and is treated as any other XML column with a maximum XML data size limit of 2GB. Only one column set per table is allowed. You cannot add a column set to a table if the table already contains sparse columns. To specify a column as a sparse column using SQL Server Management Studio (SSMS), set the Is Sparse property to Yes in the column properties for the selected column (see Figure 24.3). Similarly, if a column needs to be declared as column set, set the Is Columnset property to Yes in the column properties. For more information on using and working with sparse columns and column sets, see Chapter 42. Defining Table Location As databases scale in size, the physical location of database objects, particularly tables and indexes, becomes crucial. Consider two tables, Authors and Titles, that are always queried together. If they are located on the same physical disk, contention for hardware resources may slow performance. SQL Server addresses this issue by enabling you to specify where a table (or an index) is stored. The mechanism for specifying the physical table location is the filegroup. Filegroups are aligned to physical data files. By default, each database has a primary filegroup and a data file that matches the name of the database. You can create additional filegroups and align them to other data files. When these filegroups are created, SQL Server enables you to create your database tables on a specific filegroup. Download from www.wowebook.com ptg 762 FIGURE 24.3 Setting a column as a sparse column. NOTE Using partitioned tables is a way to specify table location. This SQL Server 2008 fea- ture allows you to divide a table into partitions and align those partitions with file- groups. This concept is discussed in detail in the “Using Partitioned Tables” section, later in this chapter. The placement of tables on separate filegroups has some distinct advantages, including performance benefits. You can achieve performance improvements by storing filegroups on different disks. You can also achieve some manageability improvements by using file- groups because you can back up and manipulate filegroups separately. This capability is particularly important for large tables. You specify the location of a table by using the ON clause during table creation. Listing 24.10 shows an example of creating two filegroups in the BigPubs2008 database, followed by the creation of two new tables on those filegroups. Note that the filegroups must exist before the tables are created. For more information on filegroups, see Chapter 23, “Creating and Managing Databases.” LISTING 24.10 An Example of Creating Tables on Specific Filegroups Add the filegroups ALTER DATABASE BigPubs2008 ADD FILEGROUP FG1 ALTER DATABASE BigPubs2008 ADD FILEGROUP FG2 GO Add files to the filegroups ALTER DATABASE BigPubs2008 ADD FILE CHAPTER 24 Creating and Managing Tables Download from www.wowebook.com ptg 763 Defining Table Constraints 24 ( NAME = FG1_File, FILENAME = ‘c:\BigPubs2008FG1.ndf’, SIZE = 2MB) TO FILEGROUP FG1 go ALTER DATABASE BigPubs2008 ADD FILE ( NAME = FG2_File, FILENAME = ‘c:\BigPubs2008FG2.ndf’, SIZE = 2MB) TO FILEGROUP FG2 go CREATE TABLE [dbo].[authors_NEW]( [au_id] [dbo].[id] NOT NULL, [au_lname] [varchar](40) , [au_fname] [varchar](20) , [phone] [char](12), [address] [varchar](40) NULL, [city] [varchar](20) NULL, [state] [char](2) NULL, [zip] [char](5) NULL, [contract] [bit] NOT NULL, ) ON FG1 go CREATE TABLE [dbo].[titles_NEW]( [title_id] [dbo].[tid] NOT NULL, [title] [varchar](80) NOT NULL, [type] [char](12) NOT NULL, [pub_id] [char](4) NULL, [price] [money] NULL, [advance] [money] NULL, [royalty] [int] NULL, [ytd_sales] [int] NULL, [notes] [varchar](400) NULL, [pubdate] [datetime] NOT NULL, ) ON FG2 Defining Table Constraints Constraints provide a means to enforce data integrity. In addition to NULL/NOT NULL, discussed earlier in this chapter, SQL Server provides five constraint types: PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK, and DEFAULT. These constraints help further define the type of data you can store in tables. Download from www.wowebook.com . installation of SQL Server 2008 or at any time using SQL Server Configuration Manager. After you enable FILESTREAM at the Windows level, you next need to enable FILESTREAM for the SQL Server Instance persisted. FILESTREAM Storage SQL Server 2008 introduces FILESTREAM storage for storing unstructured data, such as documents, images, and videos. In previous versions of SQL Server, there were two. either through SQL Server Management Studio or via T -SQL. For more information on enabling and using FILESTREAM storage, see Chapter 42. Sparse Columns and Column Sets SQL Server 2008 provides