622 Chapter14•ImplementingObjects user-defined data type to represent people’s names. This UDF can be based on nvarchar(50) and cannot contain nulls. This UDF can now be bound to any column that is to contain people’s names and will be consistent throughout. Create your user-defined data types in the Model system database, so that it is automatically inherited by all new databases you create. User-defined data types are created using the CREATE TYPE statement. The syntax is shown in Example 14.5. Example14.5 CREATE TYPE Statement—Syntax CREATE TYPE [schema_name.]type_name { FROM base_type([precision],[scale]) [NULL | NOT NULL] } Example14.6 Using the CREATE TYPE Statement CREATE TYPE PersonName { FROM varchar(50) NOT NULL }; GO CREATE TABLE TeamMembers (MemberId int PRIMARY KEY, MemberName PersonName, ManagerName PersonName); GO Example 14.6 shows the syntax used to create a user-defined data type named PersonName and to create a table that contains two columns of type PersonName. Use the ALTER TYPE statement to change the definition of your user-defined types. The DROP TYPE statement should be used to remove the user-defined data types you no longer need in the database. You cannot remove user-defined types from the database while there are tables with columns based on these types. If you attempt to use the DROP TYPE statement to remove a data type that is in use, you will get an error message similar to: “Msg 3732, Level 16, State 1, Line 1. Cannot drop type ‘PersonName’ because it is being referenced by object ‘TeamMembers’. There may be other objects that reference this type.” ImplementingObjects•Chapter14 623 Working with Constraints Constraints are data validation rules that are bound to a column or a set of columns in a table. Constraints can also be used to enforce a relationship between two entities represented as two tables. The available types of constraints are as follows: Check Constraints These constraints validate the integrity of data in a column by checking it against a valid comparison. For example, you can use a CHECK constraint to ensure that no one in your Employees table has a Birth Date earlier than 01/01/1880. You can also use a CHECK constraint to validate that an e-mail address is always at least seven characters long. Primary Key Constraints PRIMARY KEY constraints represent the unique identifier column that will enforce the uniqueness of each row. For example, you can designate the CustomerID column as the PRIMARY KEY for the Customers table. If you get two customers that have the same values in the Name column and other columns, but repre- sent different people, you will use the primary key to distinguish between them. It is a best practice to always have a primary key in each table and to use surrogate primary keys that have no meaning to the application. Unique Constraints These constraints are similar to PRIMARY KEY constraints, except that you can have more than one unique constraint per table. For example, you can designate that the combination of FirstName, LastName and TelephoneNumber is unique in the Customers table and that the EmailAddress column can only contain unique values. Foreign Key Constraints These constraints enforce a relationship between two tables. For example, you can use a FOREIGN KEY con- straint to specify that any row in the Orders table must have a corre- sponding row in the Customers table, and that the tables are linked through the CustomerID column, which is included in both tables. Once this FOREIGN KEY constraint is enforced, you cannot delete a row from the Customers table that has related rows in the Orders table. Default Constraints Also known as “defaults,” the DEFAULT constraints specify a default value to be inserted into a column if no value is inserted. Defaults can be bound to a column that is defined as NULL or NOT NULL. An example of a default is to use the value “Not Applicable” for the ProductColour every time someone adds a product to the Products table without specifying a color. 624 Chapter14•ImplementingObjects When you attempt to insert, delete, or modify data in a table that will result in a constraint violation, the statement will roll back. DML statements, like INSERT, UPDATE, DELETE, or MERGE, always succeed or fail as a whole. For example, if you were inserting 1000 records into a table, but one violated a PRIMARY KEY or UNIQUE constraint, all 1000 rows would roll back and nothing would be inserted. If a DELETE statement violates a FOREIGN KEY constraint, even on one row, the entire DELETE statement would fail and nothing would be deleted. You will never receive a partial result set from a DML statement. Example 14.7 shows the syntax used for working with constraints. Te s T Da y Ti p Remember that DML statements commit as a whole or not at all. A constraint violation will cause the entire statement to fail and roll back. Example14.7 Working with Constraints CREATE TABLE Stars (StarID int PRIMARY KEY, StarName varchar(50) Unique, SolarMass decimal(10,2) CHECK(SolarMass > 0), StarType varchar(50) DEFAULT 'Orange Giant'); GO INSERT Stars(StarID, StarName, SolarMass) VALUES (1, 'Pollux', 1.86); INSERT Stars(StarID, StarName, SolarMass, StarType) VALUES (2, 'Sun', 1, 'Yellow dwarf'); SELECT * FROM Stars Results: StarID StarName SolarMass StarType 1 Pollux 1.86 Orange Giant 2 Sun 1.00 Yellow dwarf INSERT Stars(StarID, StarName, SolarMass, StarType) VALUES (2, 'Deneb', 6, 'White supergiant'); ImplementingObjects•Chapter14 625 Results: Msg 2627, Level 14, State 1, Line 1 Violation of PRIMARY KEY constraint 'PK__Stars__06ABC647542C7691'. Cannot insert duplicate key in object 'dbo.Stars'. The statement has been terminated. INSERT Stars(StarID, StarName, SolarMass, StarType) VALUES (3, 'Deneb', -6, 'White supergiant'); Results: Msg 547, Level 16, State 0, Line 1 The INSERT statement conflicted with the CHECK constraint "CK__ Stars__SolarMass__58F12BAE". The conflict occurred in database "AdventureWorks", table "dbo.Stars", column 'SolarMass'. The statement has been terminated. INSERT Stars(StarID, StarName, SolarMass, StarType) VALUES (3, 'Deneb', 6, 'White supergiant'); SELECT * FROM Stars Results: DROP TABLE Stars StarID StarName SolarMass StarType 1 Pollux 1.86 Orange Giant 2 Sun 1.00 Yellow dwarf 3 Deneb 6.00 White supergiant Creating Indexes An index is a lookup structure created on a table to optimize sort and query performance. Indexes are created on a particular column or columns, and store the data values for this column or columns in order. When raw underlying table data is stored in no particular order, this situation is referred to as a heap. The heap is composed of multiple pages, with each page containing multiple table rows. When raw underlying data is stored in order, sorted by a column or columns, this situation is referred to as a clustered index. For example, if you have a table named Customer, with a clustered index on the FullName column, the rows in this table will be stored in order, sorted by the full name. This means that when you are searching for 626 Chapter14•ImplementingObjects a particular full name, the query optimizer component can execute the query more efficiently by performing an index lookup rather than a table scan. Only one clustered index is allowed per table; usually this is created on the column designated as the PRIMARY KEY. You can also create additional nonclustered indexes on a table that is stored either as a heap or as a clustered index. A nonclustered index is a separate lookup structure that stores index values in order, and with each index value, it stores a pointer to the data page containing the row with this index value. Nonclustered indexes speed up data retrieval. It makes sense to create nonclustered indexes on all frequently searched on fields in a table. The trade-off with indexes is write performance. Every time a new row is inserted, the index must also be updated. When writing data to a table with nonclustered indexes, sometimes the pages within the table have to be rearranged to make room for the new values. In addition, indexes are storage structures that take up disk space. Indexes are created using the CREATE INDEX statement. Example 14.8 shows the syntax for creating an index. Example14.8 CREATE INDEX Statement—Syntax CREATE [ UNIQUE ] [ CLUSTERED | NONCLUSTERED ] INDEX index_name ON table_or_view ( column1 [ ASC | DESC ], column2, . . .n) [ INCLUDE (additional_column_name, …n) ] [ WHERE filter_clause] [ WITH OPTIONS] The CREATE INDEX statement creates a clustered or nonclustered index on a specified column or columns. You can choose to create the index as UNIQUE, which will enforce a unique constraint on the index columns. A filter_clause can be specified to create indexes only on a subset of data that meets specific criteria. This is useful for a very large table, where creating an index on all values of a particular column will be impractical. Table 14.2 summarizes index options that can be used with the CREATE INDEX statement. . constraints represent the unique identifier column that will enforce the uniqueness of each row. For example, you can designate the CustomerID column as the PRIMARY KEY for the Customers table two customers that have the same values in the Name column and other columns, but repre- sent different people, you will use the primary key to distinguish between them. It is a best practice. For example, you can use a FOREIGN KEY con- straint to specify that any row in the Orders table must have a corre- sponding row in the Customers table, and that the tables are linked through the