Chapter 2: Developing with T-SQL 49 service is installed by default when you install SQL Server, but it runs only when full- text search is being used. MSFTESQL handles the actions of full-text searching, such as filtering and word breaking, as well as memory resources. Any indexes you build for full-text searching are kept in full-text catalogs and can be backed up and restored. Full-Text Catalogs Use the CREATE FULLTEXT CATALOG statement to create a full-text catalog for a database. Full-text catalog names are limited to 120 characters and cannot be created in the master, model, or tempdb databases. CREATE FULLTEXT CATALOG StoreSearch Once a catalog is created, you can define full-text indexing on a table in the database and associate it with the catalog. The following listing shows a full-text search on the Sales.Store table where the Name column contains the word “cycle”: SELECT Name FROM Sales.Store WHERE CONTAINS(Name, ' "*cycle*" ') Querying and Updating with T-SQL DML In the next section of this chapter you’ll see how T-SQL can be used to query and update SQL Server databases. A full explanation of using T-SQL is beyond the scope of this chapter. Writing SQL queries is a topic that’s big enough to warrant its own book, and in fact several books have been written on the topic. This chapter will present the core T-SQL concepts that you’ll need to get started writing T-SQL queries and to better understand how they work. Select and Joins The SELECT statement is undoubtedly the key building block for using T-SQL as a basis for queries from your data access applications and T-SQL scripts, stored procedures, and functions. This is true even for client and n-tiered applications that connect to SQL Server using ODBC, OLE DB, and ADO.NET. These data access frameworks provide an object-oriented data access framework that makes it easy for applications to work with the data retrieved from a relational database, but at their core they all still submit T-SQL commands to retrieve and update data from the SQL Server database. 50 Microsoft SQL Server 2005 Developer’s Guide Building Queries Using the SELECT Statement In its most basic form, the SELECT statement retrieves the rows and columns from a table. The following example illustrates using the SELECT statement to retrieve all of the rows and columns from the HumanResources.Department table in the sample AdventureWorks database: use adventureworks go SELECT * FROM HumanResources.Department The asterisk is a shorthand notation that indicates all of the columns will be retrieved. The FROM clause indicates that name of the table that will be accessed. This name can optionally be qualified with the full path to the database. For example, the query could have used the form AdventureWorks.HumanResources.Department. However, use of the AdventureWorks statement sets the current database to AdventureWorks, making it unnecessary to fully qualify the name. NOTE Using the SELECT * statement is fine for ad hoc queries. However, for most production applications, it is better to limit the data returned from the query by explicitly supplying just the desired columns in the SELECT statement as is shown in the following examples. You can see the results of this basic SELECT statement in the following listing: DepartmentID Name GroupName 1 Engineering Research and Development 2 Tool Design Research and Development 3 Sales Sales and Marketing 4 Marketing Sales and Marketing 5 Purchasing Inventory Management 6 Research and Development Research and Development 7 Production Manufacturing 8 Production Control Manufacturing 9 Human Resources Executive General and Administration 10 Finance Executive General and Administration 11 Information Services Executive General and Administration 12 Document Control Quality Assurance 13 Quality Assurance Quality Assurance 14 Facilities and Maintenance Executive General and Administration 15 Shipping and Receiving Inventory Management 16 Executive Executive General and Administration (16 row(s) affected) Chapter 2: Developing with T-SQL 51 NOTE The preceding is a partial listing of the complete result set. It was cut back to fit the publication page size. Filtering Results Using the WHERE Clause The WHERE clause is used to filter the rows that are returned by the SELECT statement. The following example illustrates using the WHERE clause to return a single row from the HumanResources.Department file: SELECT DepartmentID, Name FROM HumanResources.Department Where DepartmentID = 7 Here the SELECT statement is retrieving only the values in the DepartmentID and Name columns. The WHERE clause will return a single row because only one row meets the equal condition. In other words, there’s only one row in the HumanResources.Department table where the value of the DepartmentID column is equal to 7. The results are shown here: DepartmentID Name 7 Production (1 row(s) affected) While this example illustrates use of the equal expression, the WHERE clause is extremely flexible and supports a number of different expressions. The common expressions are listed in Table 2-1. A complete list can be found in Books On-Line. Renaming Columns with AS You can also use the AS keyword to rename the column headings that are returned by a SELECT statement. By default, the column headings from the source are used; however, AS lets you substitute new column headings as you can see here: SELECT DepartmentID As ID, Name As Title FROM HumanResources.Department Where DepartmentID BETWEEN 5 AND 10 Here again this query retrieves the DepartmentID and Name columns from the HumanResources.Department table. However, in this example, the column heading of ID is substituted for DepartmentID, and the heading of Title is substituted for Name. 52 Microsoft SQL Server 2005 Developer’s Guide In addition, the WHERE clause restricts the rows returned to just those rows where the value of the DepartmentID column is between 5 and 10. The result set with the new column heading is shown here: ID Title 5 Purchasing 6 Research and Development 7 Production 8 Production Control 9 Human Resources10 Finance (6 row(s) affected) Condition Description = Tests for an equal condition. <> Tests for a not-equal condition. != Tests for a not-equal condition. > Tests for a greater-than condition. >= Tests for a greater-than or equal-to condition. !> Tests for a not-greater-than condition. < Tests for a less-than condition. <= Tests for a less-than or equal-to condition. !< Tests for a not-less-then condition. [ NOT ] LIKE Tests for a matching pattern. ESCAPE ‘escape_character’ Allows a wildcard character to be searched for. [ NOT ] BETWEEN Tests for a between condition. The AND keyword separates the starting and ending values. IS [ NOT ] NULL Tests for a null or optionally a not-null condition. CONTAINS Tests for fuzzy matching or words or phrases. [ NOT ] IN Tests if a value is included or excluded from a list. The list can be a set of constants enclosed in parentheses or a subquery. Table 2-1 Common Expressions for a WHERE Clause Chapter 2: Developing with T-SQL 53 Ordering Results with ORDER BY In the preceding example the results were returned in the order of the DepartmentID column in the HumanResources.Department table. You can also use the SELECT statement’s ORDER BY clause to order the results in alternate sequences. The following listing shows how to use the ORDER BY clause to order the results according to the Name column. The default order is ascending, but you can also specify descending results: SELECT DepartmentID, Name FROM HumanResources.Department ORDER By Name Here the DepartmentID column is selected from the HumanResources.Department table, and the ORDER BY clause is used to order the result by the values contained in the Name column. The results are shown here: DepartmentID Name 12 Document Control 1 Engineering 16 Executive 14 Facilities and Maintenance 10 Finance 9 Human Resources 11 Information Services 4 Marketing 7 Production 8 Production Control 5 Purchasing 13 Quality Assurance 6 Research and Development 3 Sales 15 Shipping and Receiving 2 Tool Design (16 row(s) affected) Grouping Results with GROUP BY The GROUP BY clause enables you to group subgroups of the rows in a result set together. This is useful for applying aggregate functions to these groups. In the following listing the GROUP BY clause is used to group the results returned from the HumanResources.Department table according to the GroupName column. 54 Microsoft SQL Server 2005 Developer’s Guide In addition, the COUNT(*) operator is used to aggregate a count of all of the rows contained in each group: SELECT GroupName, Count(*) As Departments FROM HumanResources.Department GROUP BY GroupName Here the result set is created by selecting the GroupName column from the HumanResources.Department table, and the COUNT(*) operator is used to return the count of rows for each group. The GROUP BY clause specifies that the result set will be grouped according to the values in the GroupName column. You can see the results of using the GROUP BY clause in the following listing: GroupName Departments Executive General and Administration 5 Inventory Management 2 Manufacturing 2 Quality Assurance 2 Research and Development 3 Sales and Marketing 2 (6 row(s) affected) Eliminating Duplicate Rows with SELECT DISTINCT For cases where you want to eliminate duplicate values in the result set, you can use the SELECT DISTINCT statement. For example, as you may have noticed in some of the previous listings, multiple occurrences of some of the values in the GroupName column exist for several of the rows in the HumanResources.Department table. You can use SELECT DINSTINCT as shown in the following listing to create a query that eliminates the duplicate results. SELECT Distinct GroupName FROM HumanResources.Department In the example, the SELECT DISTINCT statement retrieves all of the rows from the HumanResources.Department table, with the DISTINCT clause eliminating the duplicate values. You can see the results of the SELECT DISTINCT statement in the following listing: Chapter 2: Developing with T-SQL 55 GroupName Executive General and Administration Inventory Management Manufacturing Quality Assurance Research and Development Sales and Marketing (6 row(s) affected) Creating Tables Using SELECT INTO Using SELECT INTO enables you to create tables using the results of a query. The data type of the columns used will all match the data type of the original columns. You can see an example of the SELECT INTO statement in the following listing: SELECT * INTO #TempDepartment FROM HumanResources.Department Where GroupName LIKE '%Ex%' SELECT * FROM #TempDepartment In this listing you can see where a SELECT * statement is used to retrieve all of the columns from the HumanResources.Department table. The INTO clause directs the results of the SELECT statement into the temporary table named #TempDepartment. The WHERE clause filters the rows to only those rows where the value in the GroupName column contains the characters ‘Ex’. After the #TempDepartment table is created, another SELECT statement is used to show the contents of the #TempDepartment table. (5 row(s) affected) DepartmentID Name GroupName 9 Human Resources Executive General and Administration 10 Finance Executive General and Administration 11 Information Services Executive General and Administration 14 Facilities and Maintenance Executive General and Administration 16 Executive Executive General and Administration (5 row(s) affected) 56 Microsoft SQL Server 2005 Developer’s Guide Using the TOP Clause The Top clause can be used to return a given percentage of the result set. In SQL Server 2000 you were forced to use a constant value in conjunction with the TOP clause. In other words, you could select the TOP 5 or TOP 10 rows, where the value of 5 or 10 was a constant. With SQL Server 2005 the TOP function now enables the use of an expression in conjunction with the TOP clause. An expression can be any allowed T-SQL expression, including a variable or a scalar subquery. The TOP clause is also supported in the INSERT, UPDATE, and DELETE statements. This gives the TOP clause a great deal more flexibility than ever before. An example of using the new TOP clause is shown here: DECLARE @MyTop INT SET @MyTop = 5 SELECT TOP (@MyTop) DepartmentID, Name FROM HumanResources.Department The example returns the top 5 results from the HumanResources.Department table. The results of using the TOP clause are shown in the following listing: DepartmentID Name 12 Document Control 1 Engineering 16 Executive 14 Facilities and Maintenance 10 Finance (5 row(s) affected) Retrieving Related Data Using Joins The previous examples illustrated the use of the basic SELECT statement that was working with a single table. The SELECT statement can also handle much more complex requirements by using the JOIN clause to join together rows from multiple tables, producing a single result set. Using joins is common in a relational database system like SQL Server, as the data composing the database tables is typically normalized to various degrees. Therefore related data is typically stored in several different tables that are intended to be joined together using columns from each table that contain common data. The following example illustrates using a three-table inner join to retrieve selected data from the HumanResources.Department table, the HumanResources.Employee table, and the HumanResources.Contact table: Chapter 2: Developing with T-SQL 57 SELECT e.EmployeeID, c.FirstName, c.LastName, e.Title, d.Name AS Department FROM HumanResources.Employee e INNER JOIN Person.Contact c ON c.ContactID = e.ContactID INNER JOIN HumanResources.EmployeeDepartmentHistory h ON e.EmployeeID = h.EmployeeID INNER JOIN HumanResources.Department d ON h.DepartmentID = d.DepartmentID Where h.EndDate IS NOT Null In this example the SELECT statement specifies that the returned result set will consist of the EmployeeID and Title columns from the HumanResources.Employee table, the FirstName and LastName columns from the Person.Contacts table, and the Name column from the HumanResources.Department table. To make it easier to work with the column names, short name aliases are used for each of the tables. For example, the HumanResources.Employee table uses an alias of e, the HumanResources.Contacts table uses an alias of c, and the HumanResources.Department column uses an alias of d. While the SELECT statement defines the result set that will be returned, the join conditions that tell SQL Server how to retrieve the data are specified in the FROM clause. In this example the HumanResources.Employee table is joined to the Person.Contact table on the Contact ID to retrieve the employee name information. Then the HumanResources.Employee table is joined to the HumanResources .EmployeeDepartmentHistory table on the EmployeeID column to retrieve the Department ID for the employee. Then the HumanResources.Employee table is joined to the HumanResources.Department table to retrieve the Department name. Finally, the Where clause indicates that only the rows where the EndDate in the EmployeeDepartmentHistory column are not null will be selected. In other words, the employee is still part of that department. You can see the results of this three-table join in the following listing: EmployeeID FirstName LastName Title Department 4 Rob Walters Senior Tool Designer Engineering 6 David Bradley Marketing Manager Purchasing 96 William Vong Scheduling Assistant Production 140 Laura Norman Chief Financial Officer Finance 274 Sheela Word Purchasing Manager Marketing 274 Sheela Word Purchasing Manager Quality Assurance (6 row(s) affected) 58 Microsoft SQL Server 2005 Developer’s Guide While this example illustrates the use of the inner join, SQL Server supports a number of additional join conditions. The common join types are listed in Table 2-2. A complete list can be found in Books On-Line. Combining Related Data Using UNIONs The UNION statement combines the results of multiple queries into a single result set. In order to perform a UNION, the data being combined must meet two conditions. First, the number and the order of the columns must be the same. Next, the data types must be compatible. SELECT * INTO dbo.FirstHalfDept FROM HumanResources.Department WHERE DepartmentID <= 8 GO SELECT * INTO dbo.SecondHalfDept FROM HumanResources.Department WHERE DepartmentID > 8 GO SELECT * FROM dbo.FirstHalfDept UNION SELECT * FROM dbo.SecondHalfDept ORDER BY DepartmentID; GO Join Type Description INNER All matching pairs of rows are returned. Unmatched rows from both tables are discarded. Inner is the default join type. FULL [ OUTER ] Rows from either the left or right table that do not meet the join condition are included in the result set. Any columns that correspond to the other table are set to NULL. LEFT [ OUTER ] Rows from the left table not meeting the join condition are included in the result set. Any columns from the other table are set to NULL. RIGHT [OUTER] Rows from the right table not meeting the join condition are included in the result set. Any columns that correspond to the other table are set to NULL. Table 2-2 Common Join Types . database, but at their core they all still submit T -SQL commands to retrieve and update data from the SQL Server database. 50 Microsoft SQL Server 2005 Developer’s Guide Building Queries Using the. Administration (5 row(s) affected) 56 Microsoft SQL Server 2005 Developer’s Guide Using the TOP Clause The Top clause can be used to return a given percentage of the result set. In SQL Server 2000 you were forced. Manager Quality Assurance (6 row(s) affected) 58 Microsoft SQL Server 2005 Developer’s Guide While this example illustrates the use of the inner join, SQL Server supports a number of additional join