Chapter 2: Developing with T-SQL 29 If you have created previous database projects or SQLCLR solutions that connect to SQL Server, you’ll have existing connections as shown in Figure 2-6. You can either choose an existing connection or click Add New Reference to create a new database reference. In the example shown in Figure 2-6 you can see that an existing connection to the AdventureWorks database on a SQL Server system named SQL2005-2 has been selected. Clicking OK creates a new Visual Studio solution. The Visual Studio 2005 Solution Explorer will be shown on the right side of the screen; it provides an overview of the connections, projects, and files inside a solution. To get an SSMS-like view of the SQL Server databases and their objects, you can open the Server Explorer by selecting the View | Server Explorer option from Visual Studio 2005’s IDE. A Visual Studio project will appear like the one shown in Figure 2-7. The Solution Explorer shown on the right-hand portion of Figure 2-7 is divided into four sections: Change Scripts, Create Scripts, Queries, and Database References. When a project is first created, all of these items will be empty, except for Database References, where you can see the database connection that you selected earlier. Figure 2-7 The New Visual Studio 2005 solution 30 Microsoft SQL Server 2005 Developer’s Guide The Server Explorer window enables you to browse through the objects in the SQL Server database. In addition, you can right-click the different objects shown in the Server Explorer to display a context menu that allows you to work with the objects. For instance, right-clicking a stored procedure will display a context menu that enables you to work with stored procedures. The available options are: Add New Stored Procedure, Open, Execute, Step Into Stored Procedure, Generate Create Script to Project, Copy, Delete, Refresh, and Properties. To create a new stored procedure, click Add New Item from the Project menu or right-click a stored procedure in the Server Explorer and select the Add New Stored Procedure option to display the Add New Item dialog you can see in Figure 2-8. Visual Studio 2005 has a number of different database project templates that you can see in Figure 2-8. These templates essentially supply you with the starter code for your project. The existing templates can be customized, or you can also add your own custom templates if you’ve developed your own set of starter code. To create a stored procedure, select the Stored Procedure Script template, name the script, and click OK. In Figure 2-8 you can see that the example stored procedure script will be named MyStoredProcedure.sql. After you click OK, Visual Studio 2005 will generate the stub code to drop and create a stored procedure. However, the generated code is just a shell. It is up to you to name the stored procedure and fill in the required logic. You can see the complete example stored procedure in Figure 2-9. Figure 2-8 Database Project: Add Item Chapter 2: Developing with T-SQL 31 NOTE Visual Studio supports the same Query Builder that was presented earlier in this chapter in the section “SQL Server Management Studio.” To open the Query Builder in Visual Studio 2005, right- click in the editing window and select the Insert SQL option from the context menu. In Figure 2-9 you can see that the stored procedure has been named uspRead PersonContactName. This example stored procedure reads through the Person .Contacts table on the AdventureWorks database. The complete code to create the uspReadPersonContactInfo stored procedure is shown in the following code listing: IF EXISTS (SELECT * FROM sysobjects WHERE type = 'P' AND name = 'uspReadEmpMgrs') BEGIN DROP Procedure 'uspReadEmpMgrs' END GO Figure 2-9 Editing a stored procedure in Visual Studio 2005 32 Microsoft SQL Server 2005 Developer’s Guide CREATE Procedure 'uspReadEmpMgrs'AS BEGIN DECLARE @ThisEmp int DECLARE EmpCursor CURSOR FOR SELECT EmployeeID FROM AdventureWorks.HumanResources.Employee OPEN EmpCursor WHILE @@FETCH_STATUS = 0 BEGIN FETCH NEXT FROM EmpCursor INTO @ThisEmp PRINT 'EmployeeID:' + RTRIM(CAST(@ThisEmp AS VARCHAR(10))) EXEC uspGetEmployeeManagers @ThisEmp END CLOSE EmpCursor DEALLOCATE EmpCursor END; At the top of this code listing, you can see where an IF EXISTS test is used to determine whether the stored procedure named uspReadEmpMgrs is present in the AdventureWorks database. If so, then the procedure is dropped so that the following create statement can proceed with no errors. This code and the following CREATE PROCEDURE statement were both generated by Visual Studio’s stored procedure template. The code within the uspReadEmpMgrs stored procedure declares a variable to hold the information read from the HumanResources.Employee table, and a cursor is declared that enables the stored procedure to read through the HumanResources .Employee table one row at a time. For each row read, the uspGetEmployeeManagers stored procedure is called, passing the value of EmployeeID from the current row. At the end of the routine, the cursor is closed and then released. NOTE More information on creating stored procedures and other T-SQL coding techniques is presented later in this chapter. In general, using cursors limits application scalability, and therefore, they should normally be avoided. However, in this case a cursor was used to make it easier to illustrate the debugging and code-stepping techniques in Visual Studio 2005’s T-SQL debugger. To create the stored procedure, save your script and then select the Run option from Visual Studio 2005’s Project menu. This will delete and re-create the stored procedure name uspReadEmpMgrs in the AdventureWorks database. Visual Studio 2005’s Output window will show the result of the DROP and CREATE PROCEDURE statements. To see the new stored procedure, go to the Server Explorer window and expand the Data Connections node. Then expand the connection you are using, right-click the Stored Procedure node, and select Refresh. The stored procedure you have created should now be visible in the list of procedures. Chapter 2: Developing with T-SQL 33 Executing and Debugging T-SQL with Visual Studio 2005 To execute a stored procedure using Visual Studio 2005, first open up Server Explorer and expand the Data Connections node for the desired database connection. The example that this chapter has used is sql2005-2.AdventureWorks. Next open the Stored Procedures node, right-click the stored procedure that you want to run, and select Execute from the pop-up menu. Visual Studio 2005 will execute the selected stored procedure, and the results will be shown in the Output pane at the bottom of the Visual Studio IDE. Debugging a stored procedure from Visual Studio is very similar. You can debug a stored procedure from the Server Explorer. To debug a T-SQL stored procedure using the Server Explorer, first open the Server Explorer, expand the desired Data Connections node, expand the Stored Procedures node, and right-click the stored procedure that you want to debug. This will display the context menu. From the context menu, select the Step Into Stored Procedure option. If the stored procedure uses input parameters, Visual Studio 2005 will display a Run Stored Procedure dialog that allows you to pass in the required parameter values. In the case of the uspReadEmpMgrs stored procedure, no input parameters are required and Visual Studio 2005 opens up into the stored procedure debugger that you can see in Figure 2-10. Figure 2-10 Debugging T-SQL stored procedures 34 Microsoft SQL Server 2005 Developer’s Guide Visual Studio loads the stored procedure source code into the IDE, and execution of the stored procedure stops at the first line of code. You can step through the T-SQL source code by clicking the Step Into, Step Over, or Step Out icon on the toolbar or by pressing f 10.You can inspect the contents of a variable using Visual Studio 2005’s DataTips feature by moving the mouse over the variable, causing the DataTips windows to automatically display. This is shown in Figure 2-10, where the contents of the @ThisEmp variable are displayed in the DataTips window. You can also use one of the Visual Studio debugging windows to see the contents of a T-SQL variable. The Visual Studio 2005 debugging windows are displayed using the Debug | Windows option, and the following windows are available: ᭤ Immediate Allows you to enter commands and change variables. ᭤ Locals Displays variables within the current scope. ᭤ Breakpoints Displays the set breakpoints. ᭤ Output Displays the output of the executing code. ᭤ Autos Displays variables used in the current statement. ᭤ Call Stack Displays the code call stack. ᭤ Threads Displays the ID of the current thread. ᭤ Watch Displays a watch window for watch variables that you defi ne. As you step through the code, the output will be displayed in the Output window that you can see at the bottom of Figure 2-10. If the stored procedure you are debugging calls another stored procedure, you can press f 11 when stepping over the line of code that calls the other procedure. This will automatically load the called stored procedure into the debugger. You can then step through the code in that procedure by pressing f 10 or clicking the Step Into or Step Over icon in the Visual Studio 2005 toolbar. Clicking the Step Out icon will return the debugger to the caller. Creating Database Objects Using T-SQL DDL This part of the chapter covers the basic features of the Data Definition Language (DDL) parts of SQL. You see how to create several kinds of SQL objects, such as databases, tables, views, and indexes. Chapter 2: Developing with T-SQL 35 Databases A database is the main container for tables, views, indexes, stored procedures, and other database objects. Using the CREATE DATABASE statement, you can create a new database along with the files used to store the database, you can create a database snapshot, or you can attach a database using the detached files of a previously created database. You can create 32,767 databases on an instance of SQL Server. The following statement creates a database: CREATE DATABASE MyNewDatabase When a database is created, two files are also created: a primary file ( an .mdf file) and a transaction log file (an .ldf file). It is recommended that you keep these files in different drives from each other to simplify recovering your database in case your database becomes corrupted. You can also specify multiple data and transaction log files. The next code listing shows designating the .mdf and .ldf file locations in the CREATE DATABASE statement: CREATE DATABASE MyNewDatabase ON PRIMARY (Name ='MyDB_Data', FileName= 'C:\DBData\MyDB_Data.Mdf', Size=100MB, MaxSize=200MB, FILEGROWTH=10%) LOG ON (Name = 'MyDB_Log', FileName= 'D:\DBLogs\MyDB_Log.Ldf', Size=30MB, MaxSize=50MB , FILEGROWTH=10%) You can also use the CREATE DATABASE statement to create a database snapshot. A database snapshot is a read-only, static view of an existing database at the time the snapshot was created and does not create a log file. Database snapshots are a good way to create backup copies of your database. The following code creates a database snapshot: CREATE DATABASE MyDBSnapshot ON (NAME = MyDatabase_data, FILENAME = 'C:\temp\MyDatabase_data.ss') AS SNAPSHOT OF MyNewDatabase 36 Microsoft SQL Server 2005 Developer’s Guide Tables In your database, tables are objects that actually contain the data. In SQL Server 2005, you can create up to two billion tables per database and 1024 columns per table. The total size of the table and the number of rows are restricted only by the available storage, and the maximum number of bytes per rows is 8060. However, the row restriction has been adapted for tables with column types of varchar, nvarchar, varbinary, or sql_variant, or CLR user-defined types where the total combined table width can possibly exceed 8060 bytes. Each of the individual columns must stay within the 8060 byte limit, but the database engine moves the record column with the largest width to another page in the ROW_OVERFLOW_DATA allocation unit and maintains a 24-byte pointer on the original page. The CREATE TABLE statement creates a database table. In the CREATE TABLE statement you must specify the table name and the column names and column definitions for the table. You can optionally specify other table creation options, such as the database name, schema name, filegroup, and setup constraints. The following code listing shows a basic CREATE TABLE statement for a new Warehouse table: CREATE TABLE Sales.Warehouse (HouseID INT PRIMARY KEY, HouseName Char(50)) When this statement executes, a Warehouse table is created in the current database, in the Sales schema. It contains two columns, a HouseID column that is defined as an integer type and a HouseName column that is defined as a character type with a length of 50. The HouseID column is also set as a primary key. Constraints Constraints let you define the rules regarding the values that go into columns in your tables and help enforce the integrity of your database. The following list shows the constraint options: ᭤ NOT NULL specifi es that the column cannot accept NULL values. ᭤ CHECK constraints limit the values that can be put in a column by evaluating a search condition that is applied to the values that are entered for the column, and returning True, False, or unknown. ᭤ UNIQUE constraints do not allow two rows in the table to have the same value for the columns. Chapter 2: Developing with T-SQL 37 ᭤ PRIMARY KEY constraints identify the column or set of columns that have values that uniquely identify a row in a table. The value of NULL cannot be entered into a primary key column. ᭤ FOREIGN KEY constraints reference relationships between tables. There are two types of constraints: column constraints and table constraints. A column constraint is defined as part of a column definition in the CREATE TABLE statement and applies to only that column. A table constraint is declared using the CONSTRAINT keyword in the CREATE TABLE statement and can apply to more than one column in a table. Temporary Tables You can create two types of temporary tables: local and global. Local temporary tables are only visible in the current session, but global temporary tables are visible to all sessions. Temporary tables are useful when you need to create a specific index on them in your session, and they are automatically dropped when they go out of scope. Local temporary table names are designated with single number sign (#table_name) prefix, and global temporary table names are designated with a double number sign (##table_name) prefix. Temporary table names have a limit of 116 characters. The next listing shows how to create a temporary table: CREATE TABLE #tempWarehouse (HouseCode Char(5) PRIMARY KEY, HouseID INT) Data Types With SQL Server 2005, not only will the CREATE TYPE statement allow you to create an alias data type that is based on a SQL Server native data type, but you can also create a user-defined data type (UDT) that is implemented through a class of an assembly in the Microsoft .NET Framework common language runtime (CLR). Creating aliases of native SQL Server data types gives more meaningful names to data types that have specific characteristics for your users. This example shows creating an alias type based on the native varchar type: CREATE TYPE EMAILADDRESS FROM varchar(128) NOT NULL 38 Microsoft SQL Server 2005 Developer’s Guide The following table shows the native SQL Server data types on which you can base your alias data type: bigint binary(n) bit char(n) datetime decimal float image int money nchar(n) ntext numeric nvarchar(n | max) real smalldatetime smallint smallmoney sql_variant text tinyint uniqueidentifier varbinary(n | max) varchar(n | max) To create a UDT from a CLR assembly, you must first register the assembly in SQL Server using the CREATE ASSEMBLY statement. You can then use the CREATE TYPE statement to create the UDT. The following listing shows creating an assembly and then creating a UDT based on that assembly: CREATE ASSEMBLY EmailAddress FROM 'C:\temp\EmailAddress.dll' CREATE TYPE EMAILADDRESS EXTERNAL NAME EmailAddress.[EmailNameSpace.EmailClass] A full description of creating an assembly for a UDT and deploying it to the server is covered in Chapter 3. Indexes Creating indexes on your database objects can effectively save on I/O operations and quicken processing time. Indexes can be created on tables, views, and temporary tables, or an XML index can be given on a table. An index can even be created before there is data in the table. The common types of indexes are NONCLUSTERED, CLUSTERED, and UNIQUE. The maximum size for an index key is 900 bytes. Indexes provide an ordered lookup of information for your queries and are generally placed on key fields in your tables. However, a new feature of SQL Server 2005 allows you to include nonkey columns in your nonclustered indexes. The following example shows a common CREATE INDEX statement. This statement creates a nonunique, nonclustered index on the TerritoryID column of the Sales.SalesPerson table: CREATE INDEX IdxTerritoryID ON Sales.SalesPerson (TerritoryID) . 2-7 The New Visual Studio 2005 solution 30 Microsoft SQL Server 2005 Developer’s Guide The Server Explorer window enables you to browse through the objects in the SQL Server database. In addition,. SNAPSHOT OF MyNewDatabase 36 Microsoft SQL Server 2005 Developer’s Guide Tables In your database, tables are objects that actually contain the data. In SQL Server 2005, you can create up to two. and Visual Studio 2005 opens up into the stored procedure debugger that you can see in Figure 2-10. Figure 2-10 Debugging T -SQL stored procedures 34 Microsoft SQL Server 2005 Developer’s Guide Visual