 Microsoft  SQL Server - Learning TRANSACT-SQL







 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
             Microsoft (R) SQL Server - Learning TRANSACT-SQL(tm)

              The SYBASE (R) SQL Server database for PC networks
                                 VERSION 1.1
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


                     for the MS (R) OS/2 Operating System








 Microsoft Corporation

 Information in this document is subject to change without notice and does
 not represent a commitment on the part of Microsoft Corporation. The
 software described in this document is furnished under a license agreement
 or nondisclosure agreement. The software may be used or copied only in
 accordance with the terms of the agreement. It is against the law to copy
 the software on any medium except as specifically allowed in the license or
 nondisclosure agreement. No part of this manual may be reproduced or
 transmitted in any form or by any means, electronic or mechanical, including
 photocopying and recording, for any purpose without the express written
 permission of Microsoft Corporation.
 (C) 1990 Microsoft Corporation and SYBASE, Inc. All rights reserved.


 Printed in the USA.


 Microsoft, MS-DOS, MS, and the Microsoft logo are registered
 trademarks of Microsoft Corporation.

 IBM is a registered trademark of International Business
 Machines Corporation.

 SYBASE is a registered trademark of SYBASE, Inc.
 TRANSACT-SQL and DB-LIBRARY are trademarks of SYBASE, Inc.

 Document Number: SY10233-0290
  OEM-D/0788-1Z






 Table of Contents
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Before You Begin
      Notational Conventions
      How to Use This Guide
      Finding Further Information

 Chapter 1  Overview

      Introduction
      SQL and Relational Database Management
             Queries, Data Modification, and Other Statements
             Tables, Columns, and Rows
             Relational Operations
      TRANSACT-SQL Enhancements
             Control-of-Flow Language
             Stored Procedures
             Triggers
             The COMPUTE Clause
             Data Integrity Mechanisms
             Other Extensions to SQL
      About the Examples
             Using the SQL Server Administration Facility
             Format of Examples
             Default Databases
             Using the Sample Database
             What's in the Sample Database
      Identifiers

 Chapter 2  Querying Databases

      Introduction
      Choosing a Database: USE
      SELECT Syntax
      Choosing Columns: the Select List
             Choosing All Columns: SELECT *
             Choosing Specified Columns
             Rearranging the Order of Columns
             Renaming Columns in Query Results
             Character Strings in Query Results
             Computed Values in the Select List
             Selecting text and image Values
             Select List Summary
      Eliminating Duplicates: DISTINCT
      Specifying Tables: the FROM Clause
      Selecting Rows: the WHERE Clause
             Comparison Operators
             Ranges (BETWEEN and NOT BETWEEN)
             Lists (IN and NOT IN)
             Wildcards and the LIKE Clause
             Character Strings and Quotes
             Unknown Values: IS NULL and IS NOT NULL
             Connecting Conditions with Logical Operators
      Creating a New Table for Results: SELECT INTO
      Grouping Data: GROUP BY and HAVING Clauses
      Sorting Query Results: ORDER BY
      Generating Summary Rows: COMPUTE BY

 Chapter 3  Joining Two or More Tables

      Introduction
      The Join Operation
             The Select List
             The FROM Clause
             The WHERE Clause
             Joins and the Relational Model
             How Joins Are Processed
             Equijoins and Natural Joins
             Joins with Additional Conditions
             Joins Not Based on Equality
             Self-Joins and Aliases
             The Not-Equal Join
             Joining More Than Two Tables
             Outer Joins
             Joins and Null Values
      Help on Joins

 Chapter 4  Displaying Totals: GROUP BY and COMPUTE

      Introduction
      Aggregate Functions and the GROUP BY Clause
             Aggregate Functions and Datatypes
             COUNT(*)
             The DISTINCT Keyword
             Null Values and Aggregate Functions
             The GROUP BY Clause
             GROUP BY Syntax
             GROUP BY without Aggregate Functions
             GROUP BY and Null Values
             GROUP BY and the WHERE Clause
             GROUP BY and ALL
             GROUP BY and ORDER BY
             The HAVING Clause
      The COMPUTE Clause
             COMPUTE Syntax
             Row Aggregate Functions
             More Than One Column after COMPUTE BY
             Using More Than One COMPUTE Clause
             Applying an Aggregate to More Than One Column
             Using Different Aggregate Functions in the Same COMPUTE BY
               Clause
             Grand Values: COMPUTE without BY

 Chapter 5  Using Built-In Functions

      Introduction
      System Functions
             System Function Examples
      String Functions
             SUBSTRING
             CHARINDEX
             STR
             STUFF
             Testing Similarity of Patterns: SOUNDEX and DIFFERENCE
             Concatenation
             Nested String Functions
      Text and Image Functions
             TEXTPTR
      Mathematical Functions
             Examples of Mathematical Functions
      Date Functions
             GETDATE
             DATEPART and DATENAME
             DATEDIFF
             DATEADD
      Conversion Function
             The Style Parameter

 Chapter 6  Building Subqueries

      Introduction
      Subquery Syntax
      Subquery Rules
      An Example
      Subqueries Introduced with IN
      Subqueries Introduced with NOT IN
             Qualifying Column Names
      Multiple Levels of Nesting
      Subqueries in UPDATE, DELETE, and INSERT
      Subqueries with Aliases
      Comparison Operator Subqueries
             Comparison Operators Modified with ANY or ALL
      Correlated Subqueries
             Correlated Subqueries with Aliases
             Correlated Subqueries with Comparison Operators
             Correlated Subqueries in a HAVING Clause
      Subqueries Introduced with EXISTS
      Subqueries Introduced with NOT EXISTS
             Using EXISTS and NOT EXISTS to Find Intersection and Difference
      Subqueries Used in Place of an Expression

 Chapter 7  Creating Databases, Tables, and Indexes

      Introduction
      Help on Database Objects
      Permissions
      Databases
             Choosing a Database: USE
             Creating a User Database: CREATE DATABASE
             Removing a Database: DROP DATABASE
             Changing the Size of a Database: ALTER DATABASE
             Help on Databases
      Tables
             CREATE TABLE Basics
             Table Names
             CREATE TABLE Syntax
             System-Supplied Datatypes
             User-Defined Datatypes
             Null Values
             Temporary Tables
             Creating a Table in a Different Database
             Removing a Table
             Space Used
             Table Creation Process
      Changing an Existing Table
             Adding Columns: ALTER TABLE
             Renaming Tables and Other Objects
      Indexes
             How, What, and Why to Index
             Indexing More Than One Column: Composite Indexes
             The UNIQUE Keyword
             Clustered and Nonclustered Indexes
             The FILLFACTOR Option
             The IGNORE_DUP_KEY Option
             The IGNORE_DUP_ROW and ALLOW_DUP_ROW Options
             Help on Indexes
             Removing an Index: DROP INDEX

 Chapter 8  Creating Defaults and Rules

      Introduction
      Defaults
             Creating Defaults
             Binding Defaults
             Unbinding Defaults
             Dropping Defaults
             Defaults and Null Values
      Rules
             Creating Rules
             Binding Rules
             Unbinding Rules
             Dropping Rules

 Chapter 9  Adding, Changing, and Removing Data

      Introduction
      Permissions
      Referential Integrity
      Transactions
      Using the Sample Database
      Datatype Entry Rules
             Char, Varchar, and Text
             Datetime
             Binary, Varbinary, and Image
             Money
             Float
             Int, Smallint, and Tinyint
      Adding New Rows: INSERT
             Adding New Rows with VALUES
             Adding New Rows with SELECT
      Changing Existing Data: UPDATE
             The UPDATE Clause
             The SET Clause
             The WHERE Clause
             The FROM Clause
      Adding or Changing Data with WRITETEXT
      Removing Data: DELETE
             The DELETE Clause
             The WHERE Clause
             The FROM Clause
      Deleting All Rows: TRUNCATE TABLE

 Chapter 10  Creating Views

      Introduction
      Advantages of Views
             Focus
             Simpler Data Manipulation
             Customizing
             Security
             Logical Data Independence
             View Examples
      Help on Views
      Creating and Dropping Views
             The CREATE VIEW Clause
             The SELECT Statement
             Dropping Views
      Retrieving Data through Views
             View Resolution
             Redefining Views
             Renaming Views
             Altering or Dropping Underlying Objects
      Restrictions for Modifying Data through Views
             Computed Columns in View Definition
             GROUP BY or COMPUTE BY in View Definition
             Null Values in Underlying Objects
             Changing More Than One Underlying Object

 Chapter 11  Using Control-of-Flow Language

      Introduction
      Batches
      Batches and Batch Rules
             Batch Examples
             Batches Submitted as Files
      Control-of-Flow Language
             IF...ELSE
             BEGIN...END
             WHILE and BREAK...CONTINUE
             DECLARE and Local Variables
             DECLARE and Global Variables
             GOTO
             RETURN
             WAITFOR
             PRINT and RAISERROR
             Comments

 Chapter 12  Using Stored and System Procedures

      Introduction
      Creating and Executing Stored Procedures
             Parameters
             Default Parameters
             Using More Than One Parameter
             Procedure Groups
             WITH RECOMPILE in CREATE PROCEDURE
             WITH RECOMPILE in EXECUTE
             Stored Procedure Rules
             Qualifying Names Inside Stored Procedures
             Dropping Procedures
             Renaming Procedures
             Help on Procedures
             Stored Procedures and Permissions
      System Procedures
             User Identification and Permissions
             Data Definition and Database Objects
             Other System Procedures

 Chapter 13  Creating Triggers

      Introduction
      Defining a Trigger: CREATE TRIGGER
      Removing a Trigger: DROP TRIGGER
      Trigger Rules and Considerations
             Triggers and Permissions
      Trigger Storage
      Renaming Triggers
      Help on Triggers
             sp_help
             sp_helptext
             sp_depends
      Trigger Examples
             Deleting a Primary Key
             Inserting a Primary Key
             Updating a Primary Key
             Deleting a Foreign Key
             Inserting a Foreign Key
             Inserting a NULL Foreign Key
             Updating a Foreign Key
             Summary Value Triggers
             Other Triggers
             Correlated Subqueries in Triggers

 Chapter 14  Advanced Topics for Database Owners

      Introduction
      Assigning Permissions: GRANT and REVOKE
             Object Permissions and Statement Permissions
             The Permission Hierarchy
             Permission Summary
             GRANT and REVOKE Syntax
             Views as Security Mechanisms
             Stored Procedures as Security Mechanisms
      Tuning Queries and Stored Procedures: SET
      UPDATE STATISTICS
      Locking and the HOLDLOCK Keyword
             The HOLDLOCK Keyword
             Deadlocks and Livelocks
      Data Integrity and Transaction Processing
             Transactions and Consistency
             Transactions and Recovery
             User-Defined Transactions
             Backup and Recovery
      Database Consistency Checker

 Appendix A  The pubs Sample Database

             Rules
             Trigger
             Stored Procedure
             View
             Defaults

 Glossary


 Index




 Before You Begin
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 This manual teaches you to use TRANSACT-SQL(tm), an enhanced version of the
 SQL relational database language. It describes TRANSACT-SQL statements as
 well as other features of TRANSACT-SQL.

 Before using this manual, you should have read SQL Server System
 Administrator's Guide for information on using the SQL Server Administration
 Facility (SAF). You should also be familiar with TRANSACT-SQL or another
 version of SQL.


 Notational Conventions

 Throughout this manual, the following conventions are used to distinguish
 elements of text:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Convention                        Purpose
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 UPPERCASE                         Represents statement and clause names,
                                   functions, macros, and any other portions
                                   of syntax that must appear exactly as
                                   shown.

 MIXEDcase                         Shows abbreviations for keywords in
                                   syntax. The uppercase letters show the
                                   required portion of the keyword; the
                                   lowercase letters show the optional
                                   portion.

 SMALL CAPS                        Represent key names such as CTRL.

 bold                              Represents stored procedures, system
                                   procedures, triggers, defaults, rules,
                                   utility programs, and commands.

 italic                            Represents database names, table names,
 Convention                        Purpose
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
italic                            Represents database names, table names,
                                   view names, column names, datatypes, index
                                   names, pathnames, filenames, variables
                                   that appear in text, and the first
                                   occurrence of a glossary term.

 monospace                         Represents examples, screen output,
                                   program code, and error messages.

 [brackets]                        Enclose optional items. Type only the
                                   information within the brackets, not the
                                   brackets themselves.

 {braces}                          Enclose required items. Type only the
                                   information within the braces, not the
                                   braces themselves.

 | (vertical bar)                  Separates items inside a set of braces or
                                   brackets. The vertical bar means you must
 Convention                        Purpose
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                  brackets. The vertical bar means you must
                                   choose one and only one item.

 ... (ellipsis)                    Means that you can repeat the previous
                                   item as many times as you like.

 <execute>                         Executes one or more SQL statements. (In
                                   the SQL Server Administration facility,
                                   SQL statements are executed by pressing
                                   the CONTROL+E keys. In the isql program,
                                   SQL statements are executed with the go
                                   command.)

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ




 How to Use This Guide

 The following topics are covered in this guide:

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Chapter 1
   TRANSACT-SQL, relational databases, and the sample database, pubs

 Chapter 2
   Retrieving columns and rows from a single table

 Chapter 3
   Retrieving data from two or more tables (the join operation)

 Chapter 4
   Displaying totals

 Chapter 5
   Using built-in functions

 Chapter 6
   Using subqueries

 Chapter 7
   Creating databases, tables, and indexes

 Chapter 8
   Setting up defaults and rules

 Chapter 9
   Adding, changing, and removing data

 Chapter 10
   Creating and using views

 Chapter 11
   Creating batch files and using control-of-flow language

 Chapter 12
   Creating stored procedures and using system procedures

 Chapter 13
   Creating triggers

 Chapter 14
   Managing a database (an introduction)

 Appendix
   The pubs sample database

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Finding Further Information

 The following manuals describe SQL Server and are included as part of the
 standard documentation set:

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 SQL Server Installation Guide
   A guide to installing and setting up SQL Server

 SQL Server System Administrator's Guide
   A guide for SQL Server System Administrators, including instructions
   explaining how to use the SQL Server Administration Facility (SAF) for
   database queries

 SQL Server Language Reference
   A reference to the syntax of all TRANSACT-SQL statements, commands,
   procedures, and utilities

 SQL Server Programmer's Reference
   A reference to DB-LIBRARY(tm), which is a set of C routines and macros
   that allow your application to interact with SQL Server

 SQL Server Quick Reference
   A quick reference guide for TRANSACT-SQL

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ




 Chapter 1  Overview
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 This guide documents TRANSACT-SQL, an enhanced version of the Structured
 Query Language (SQL) relational database language. It will benefit both SQL
 beginners and those who have experience with other implementations of SQL.

 Users of the SQL Server database management system who are unfamiliar with
 SQL can treat this guide as a textbook and start at the beginning. On first
 reading, novice SQL users may want to skip Chapter 6, "Building Subqueries."
 Chapter 11, "Using Control-of-Flow Language," and Chapter 14, "Advanced
 Topics for Database Owners," also cover topics that are more advanced than
 beginning SQL.

 Readers acquainted with other versions of SQL will find this guide useful
 for information on TRANSACT-SQL enhancements. Even SQL experts should study
 the capabilities and features that SQL Server has added to standard SQL,
 especially the material on stored procedures (which allow you to include
 control-of-flow language and SQL statements in named, precompiled procedures
 that can be executed at any time). These and other extensions are summarized
 later in this chapter, with references to the sections of the guide that
 discuss them in more detail.

 A concise and complete reference to TRANSACT-SQL can be found in the SQL
 Server Language Reference, which lists SQL statements, system procedures,
 and utility programs alphabetically. The SQL Server Language Reference also
 discusses some details and advanced concepts not included in this guide. The
 numerous examples in this guide are based on the pubs sample database. For
 best use of this guide, new users should work through the examples. Ask your
 System Administrator how to get a clean copy of the sample database.

 You can use TRANSACT-SQL with the SQL Server Administration Facility (SAF).
 The SAF provides an editor that makes it easy to type SQL queries and see
 the results. The SAF is described in the SQL Server System Administrator's
 Guide.


 SQL and Relational Database Management

 SQL is a high-level language for relational database systems. Originally
 developed by IBM(R) in the late 1970s, SQL has been adopted and adapted for
 many relational database management systems. It has been approved as the
 official relational query language standard by the American National
 Standards Institute (ANSI). TRANSACT-SQL is compatible with IBM SQL and most
 other commercial implementations of SQL, but provides important additional
 capabilities and functions.

 Although the "Q" in SQL stands for "Query," SQL includes commands not only
 for querying (retrieving data from) a database, but also for creating new
 databases and database objects, adding new data, modifying existing data,
 and a few other functions.


 Queries, Data Modification, and Other Statements

 In this guide, query means a request to retrieve data, using the SELECT
 statement. Data modification refers to an addition, deletion, or change to
 data, using the INSERT, DELETE, or UPDATE statement, respectively. Other SQL
 commands are instructions to perform administrative operations.

 Each SQL statement begins with a keyword, such as INSERT, that names the
 basic operation performed. Many SQL statements also have one or more clauses
 that tailor the statement to meet a particular need.

 When a query is run, TRANSACT-SQL displays the results for the user. If no
 data meets the criteria specified in the query, the user gets a message to
 that effect. Data modification statements and administrative statements do
 not display results, since they do not retrieve data. TRANSACT-SQL provides
 a message to let the user know whether the statement has been performed.


 Tables, Columns, and Rows

 SQL is a database language specifically designed for the relational model of
 database management. In a relational database management system, users see
 data as tables or relations.

 Each row, or record, of a table describes one occurrence of an entityÄa
 person, a company, a sale, or some other thing. Each column, or field,
 describes one characteristic of the entityÄa person's name or address, a
 company's name or president, items sold, a quantity, or a date.

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄ
 Pub_id  Pub_name                                           City        State
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Pub_id  Pub_name                                           City        State
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 1389    Algodata Infosystems                               Berkeley    CA

 0736    New Age Books                                      Boston      MA

 0877    Binnet & Hardley                                   Washington  DC

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Figure 1.1 A Table in a Relational Database - A database is made up of a set
 of related tables.


 Relational Operations

 The basic query operations in a relational system are selection (or
 restriction), projection, and join. All of them can be combined in the SQL
 SELECT statement.

 A selection is a subset of the rows in a table, based on some conditions
 specified by the user. For example, you might want to look at the rows for
 all the authors who live in Paris.

 A projection is a subset of the columns in a table. For example, a query can
 display only the name and city of all the authors, omitting the street
 address, the phone number, and other information.

 A join links the rows in two or more tables by comparing the values in
 specified fields. For example, say you have one table containing information
 about authors, including the columns au_id (author identification number)
 and au_lname (author's last name), and another table containing title
 information about books, including a column (au_id) that gives the ID number
 of the book's author. You might join the authors table and the titles table,
 testing for equality of the values in the au_id columns of each table.
 Whenever there is a match, a new rowÄcontaining columns from both tablesÄis
 created and displayed as part of the result of the join. Joins are often
 combined with projections and selections so that only selected columns of
 selected matching rows are displayed.


 TRANSACT-SQL Enhancements

 TRANSACT-SQL is designed to enhance the power of SQL and to minimizeÄif not
 eliminateÄthe occasions on which users must resort to a programming language
 to accomplish a desired task. TRANSACT-SQL goes beyond the ANSI standard and
 the many commercial versions of SQL.

 SQL Server enhancements are summarized in the following sections. They are
 also noted throughout this guide as they are discussed.


 Control-of-Flow Language

 TRANSACT-SQL provides control-of-flow language that can be used as part of
 any SQL statement. The following constructs are available:


   ž   BEGIN...END

   ž   BREAK

   ž   CONTINUE

   ž   DECLARE

   ž   GOTO label

   ž   IF...ELSE

   ž   PRINT

   ž   RAISERROR

   ž   RETURN

   ž   WAITFOR

   ž   WHILE


 Local variables can be defined with DECLARE and assigned values. Several
 predefined global variables are included with SQL Server.


 Stored Procedures

 One of the most important SQL Server extensions to standard SQL is the
 ability to create stored procedures. Stored procedures can combine almost
 any SQL statements with control-of-flow language and can take one or more
 parameters.

 The ability to write your own stored procedures greatly enhances the power,
 efficiency, and flexibility of the SQL database language. Since stored
 procedures are compiled the first time they are executed, they subsequently
 run much faster than the stand-alone statements. The creator of a stored
 procedure can use local variables and define parameters to be supplied when
 the stored procedure is executed.

 SQL-Server-supplied stored procedures, called system procedures, are
 provided for your use in SQL Server system administration. Chapter 12,
 "Using Stored and System Procedures," discusses system procedures and
 explains how to create stored procedures. System procedures are discussed in
 detail in the SQL Server Language Reference.


 Triggers

 A trigger is a special kind of stored procedure used to protect referential
 integrity, which enforces rules about the relationships among data in
 different tables. Triggers go into effect when a user attempts to modify
 data with an INSERT, DELETE, or UPDATE statement.

 A trigger can instruct the system to take any number of actions when a
 specified change is attempted. By preventing incorrect, unauthorized, or
 inconsistent changes to data, triggers help maintain the integrity of a
 database. Triggers can also call stored procedures.


 The COMPUTE Clause

 Another important SQL Server extension is the COMPUTE clause, which is used
 with the row aggregate functions (SUM, MAX, MIN, AVG, and COUNT) to
 calculate summary values. The results of a query that includes a COMPUTE
 clause are displayed with both detail and summary rows, and look like a
 report that most database management systems can produce only with a report
 generator. COMPUTE displays summary values as additional rows in the results
 instead of as new columns. The COMPUTE clause is described in Chapter 4,
 "Displaying Totals: GROUP BY and COMPUTE."


 Data Integrity Mechanisms

 TRANSACT-SQL provides a command language for several special data integrity
 mechanisms. In addition to triggers, briefly explained earlier, defaults and
 rules define integrity constraints that come up during the entry and
 modification of data.

 A default is a value linked to a particular column or datatype, and inserted
 by the system if no value is provided during data entry. Rules are
 user-defined integrity constraints linked to a particular column or
 datatype, and enforced at data entry time. (Chapter 8, "Creating Defaults
 and Rules," discusses defaults and rules.)


 Other Extensions to SQL

 Other unique or unusual features of TRANSACT-SQL include


   ž   Updates and selections through views. Unlike most other versions of
       SQL, TRANSACT-SQL places no restrictions on retrieving data through
       views and very few restrictions on updating data through views. (See
       Chapter 10, "Creating Views.")

   ž   Fewer restrictions on the ORDER BY clause and the GROUP BY clause.
       (See Chapter 2, "Querying Databases," and Chapter 4, "Displaying
       Totals: GROUP BY and COMPUTE.")

   ž   User-defined datatypes built on SQL-Server-supplied datatypes. (See
       Chapter 7, "Creating Databases, Tables, and Indexes," and Chapter 8,
       "Creating Defaults and Rules.")

   ž   Subqueries wherever an expression is allowed. (See Chapter 6,
       "Building Subqueries.")

   ž   Temporary tables, which exist only for the duration of the current
       work session and disappear thereafter. (See Chapter 7, "Creating
       Databases, Tables, and Indexes.")

   ž   Options to the CREATE INDEX statement for fine-tuning aspects of
       performance determined by indexes and controlling the treatment of
       duplicate keys and rows. (See Chapter 2, "Querying Databases.")

   ž   Query processing options for checking SQL Server's query execution
       plan, estimating the complexity of a query, and others. (See Chapter
       14, "Advanced Topics for Database Owners.")

   ž   User control over what happens when you attempt to enter duplicate
       keys in a unique index or duplicate rows in a table. (See Chapter 7,
       "Creating Databases, Tables, and Indexes.")

   ž   The ability to insert data from a table into that same table. (See
       Chapter 9, "Adding, Changing, and Removing Data.")

   ž   The ability to extract data from one table and put it into another
       with the UPDATE statement. (See Chapter 9, "Adding, Changing, and
       Removing Data.")

   ž   The ability to remove data based on data in other tables, using a join
       in a DELETE statement. (See Chapter 9, "Adding, Changing, and Removing
       Data.")

   ž   A fast way to delete all rows in a specified table and reclaim the
       space they took up with the TRUNCATE TABLE statement. (See Chapter 9,
       "Adding, Changing, and Removing Data.")

   ž   Bitwise operators for use with integer and bit type columns. (See the
       SQL Server Language Reference.)

   ž   Support for text and image datatypes. (See the SQL Server Language
       Reference.)

   ž   Dozens of built-in functions for operations on character and numeric
       data. (See Chapter 5, "Using Built-In Functions.")

   ž   Support for browse mode in a DB-LIBRARY application and for updates
       while browsing. (See the SQL Server Programmer's Reference.)



 About the Examples

 This section discusses the examples in this guide and the pubs sample
 database.


 Using the SQL Server Administration Facility

 The SQL Server Administration Facility (SAF) contains menus that make it
 easy to interactively develop queries and view the results. You'll be using
 the SAF to do the examples throughout this guide. For directions on using
 the SAF, see the SQL Server System Administrator's Guide.

 Before you can start the SAF, you'll need the following (if necessary, see
 your System Administrator):


   ž   The name of the server to which you will log in

   ž   Your login ID

   ž   Your password

   ž   A clean copy of the pubs sample database


 After typing SQL statements on the SAF screen, execute the statements from
 the menu or press CONTROL+E. The examples throughout this guide include SQL
 statements only; they do not include directions for executing statements.


 Format of Examples

 An SQL statement must follow precise syntactical and structural rules.
 However, SQL is a free-form language; there are no rules about how many
 words you can put on a line or where you need to break a line. For
 readability and consistency, the examples in this guide are formatted so
 that each clause of a statement begins on a new line.


 Default Databases

 Your System Administrator may have assigned you a default database, a
 database that you are connected to when you log in. For example, your
 default database could be the sample database, pubs. If the System
 Administrator has not assigned you a default database, you are connected to
 the master database.

 You can change your default database to any database that you have
 permission to use or to any database that has a guest account. Any user with
 an SQL Server login ID (that is, listed in master..syslogins) can be a
 guest. To change your default database, use the sp_defaultdb system
 procedure. For information on this system procedure, see the SQL Server
 Language Reference.

 In any case, you can make sure you're in pubs by using this statement:

   use pubs


 Using the Sample Database

 The sample database, pubs, is used for just about every example in this
 guide. You can try any of the examples on your own screen.

 You will notice that the query results you see on your screen will sometimes
 differ from the way they look in this guide. That's because some of the
 examples here have been reformatted (for example, the columns realigned) for
 visual clarity or to take up less space on the page.

 You may need to get additional permissions from your System Administrator to
 change the sample database using CREATE or data modification statements,
 which are discussed in Chapter 7, "Creating Databases, Tables, and Indexes,"
 Chapter 8, "Creating Defaults and Rules," and Chapter 9, "Adding, Changing,
 and Removing Data." If you do change the sample database, it should be
 returned to its original state for future users and uses. Ask your System
 Administrator how to do this.


 What's in the Sample Database

 The sample database, pubs, consists of eight tables: publishers, authors,
 titles, titleauthor, roysched, sales, stores, and discounts. The following
 list describes these tables:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Table                             Description
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 publishers                        Contains the identification numbers,
                                   names, cities, and states of three
                                   publishing companies.

 authors                           Contains an identification number, first
                                   and last name, address, and contract
                                   status for each author.

 titles                            Contains the identification number, name,
                                   type, identification number of the
                                   publisher, price, advance, royalty,
                                   year-to-date sales, comments, and
                                   publication date for each book that has
                                   been or is about to be published.
 Table                             Description
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                  been or is about to be published.

 titleauthor                       Links the titles and authors tables
                                   together. For each book, it contains the
                                   title ID, the author ID, the author order,
                                   and the royalty split among the authors of
                                   a book.

 roysched                          Lists the unit sales ranges and the
                                   royalty connected with each range. The
                                   royalty is some percentage of the net
                                   receipts from sales.

 sales                             Records bookstore sales of titles in the
                                   titles table.

 stores                            Lists bookstores by store ID.

 discounts                         Lists three types of discounts for
 Table                             Description
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
discounts                         Lists three types of discounts for
                                   bookstores.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 The pubs sample database is illustrated in the Appendix of this guide.


 Identifiers

 An identifier is the name of a database object. Identifiers may be between 1
 and 30 characters. The first character of an identifier can be an alphabetic
 letter (a-z or A-Z) or the symbols # (pound sign) or _ (underscore). A table
 name beginning with # denotes a temporary table.

 After the first character, identifiers can include letters, digits, or the
 $, #, or _ symbol.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
 When an identifier is preceded by the @ symbol, the identifier contains from
 1 to 29 characters. (For example, variable names and stored procedure
 parameter names are preceded by @.)
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 By default, SQL Server is not sensitive to the case (upper or lower) of
 identifiers and data. If required, a System Administrator can build a
 case-sensitive SQL Server with the bldmastr program. The case of options for
 utility programs is significant.

 No embedded spaces are allowed in identifiers, and none of the SQL reserved
 words can be used.

 The names of database objects need not be unique in a database. However,
 column names and index names must be unique within a table, and other object
 names must be unique for each owner within a database. Database names must
 be unique on SQL Server.

 If you try to create a column using a name that is not unique in the table
 or if you try to create another database object (a table, a view, a stored
 procedure, and so on) with a name that you've already used in the same
 database, SQL Server responds with an error message.

 You can uniquely identify a table or column by adding other names that
 qualify itÄthe database name, the owner's name, and the table name or view
 name for a column. Each of these qualifiers is separated from the next by a
 period:

   database.owner.table_name.column_name
    database.owner.view_name.column_name

 For example, if the user sharon owns the authors table in the pubs database,
 the unique identifier of the city column in that table is

   pubs.sharon.authors.city

 The same naming syntax applies to other database objects. You can refer to
 any object in a similar fashion:

   pubs.dbo.titleview
    dbo.ziprule

 However, the full naming syntax is not always allowed in CREATE statements
 because you cannot create a view, procedure, rule, default, or trigger in a
 database other than the one you are currently in.

 The naming conventions are indicated in the syntax as

   [[database.]owner.]object_name

 or

   [owner.]object_name

 The default value for owner is the current user, and the default value for
 database is the current database. When you reference an object in SQL
 statements (other than CREATE statements) without qualifying it with the
 database name and owner name, SQL Server tries to find the object in the
 current database among the objects you own, so you need not qualify objects
 that you own.

 In addition, you need not qualify the names of objects owned by the Database
 Owner. When you give the name of an object, SQL Server first looks at all
 the objects you own and then at the objects owned by the Database Owner,
 whose name in the database is dbo.

 As long as SQL Server is given enough information to identify an object, you
 need not type every element of its name. Intermediate elements can be
 omitted and their positions indicated by dots:

   database..table_name

 When qualifying a column name and a table name in the same statement, be
 sure to use the same naming abbreviations for each; they are evaluated as
 strings and must match or an error message is returned.

 The following examples have different entries for the column name. The
 second example is not executed because the syntax for the column name does
 not match the syntax for the table name.

   select demo.mary.publishers.city
    from demo.mary.publishers
    city
    -----------------------
    Boston
    Washington
    Berkeley

    select demo.mary.publishers.city
    from demo..publishers
    The column prefix "demo.mary.publishers" does not match
    a table name or alias name used in the query.






 Chapter 2  Querying Databases
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 The SELECT statement is used for choosing rows and columns from tables. You
 can use it for selections (retrieving a subset of the rows in one or more
 tables), projections (retrieving a subset of the columns in one or more
 tables), and joins (linking rows in two or more tables to retrieve
 cross-table data).

 In this chapter, you'll find information about


   ž   Selecting all or specified columns in a table

   ž   Changing the format for results

   ž   Including simple computed values in a SELECT statement

   ž   Eliminating duplicate rows with DISTINCT

   ž   Specifying tables and views with the FROM clause

   ž   Using the WHERE clause with comparison operators, logical operators,
       BETWEEN, IN, ANY, and LIKE

   ž   Using NULL and NOT NULL

   ž   Creating new tables for results with SELECT INTO

   ž   Using GROUP BY and HAVING

   ž   Sorting query results with ORDER BY

   ž   Producing simple summary reports with COMPUTE


 This chapter focuses on single-table SELECT statements. Information on
 advanced uses of SELECT is described in later chapters of this book:


   ž   Chapter 3, "Joining Two or More Tables," explains the join operation.

   ž   Chapter 4, "Displaying Totals: GROUP BY and  COMPUTE," gives details
       on the GROUP BY, HAVING, and COMPUTE BY clauses.

   ž   Chapter 5, "Using Built-In Functions," covers SQL Server's built-in
       functions.

   ž   Chapter 6, "Building Subqueries," focuses on subqueries, also known as
       nested queries or nested select statements.



 Choosing a Database: USE

 In most cases, when you log in to SQL Server, you are connected to the
 master database. To connect to pubs, you need to execute the USE statement.


 The USE statement accesses a database that already exists. It must be
 executed before you can do any other work in that database. The USE
 statement has the following syntax:

   USE database_name

 For example, to open pubs, type

   use pubs

 Usually, all users can access the master and pubs databases even if they
 haven't explicitly been given access to them. This is because users not
 recognized by name in the master or pubs databases are allowed access under
 the guest account.


 SELECT Syntax

 The SELECT statement specifies the columns you want to retrieve. The FROM
 clause specifies the tables where the columns are located. The WHERE clause
 specifies the rows in the tables you want to see.

 A simplified syntax for the SELECT statement is

   SELECT select_list
    FROM table_list
    WHERE search_conditions

 The following SELECT statement finds the first and last names of writers in
 the authors table who have a contract. (Authors with contracts have "1" in
 the contract column.)

   select au_fname, au_lname
    from authors
    where contract = 1

 SELECT statement results appear in a columnar format, like this:

   au_fname  au_lname
    ---------  --------------
    Johnson   White
    Marjorie  Green
    Cheryl   Carson
    Michael   O'Leary
    Dick   Straight
    Abraham   Bennet
    Ann    Dull
    Burt   Gringlesby
    Chastity  Locksley
    Reginald  Blotchet-Halls
    Akiko   Yokomoto
    Innes   del Castillo
    Michel   DeFrance
    Stearns   MacFeather
    Livia   Karsen
    Sylvia   Panteley
    Sheryl   Hunter
    Anne   Ringer
    Albert   Ringer

    (19 rows affected)

 The complete syntax of the SELECT statement includes the following phrases
 and keywords:

   SELECT [ALL | DISTINCT] select_list
    [INTO [[database.]owner.]table_name]
    [FROM [[database.]owner.]{table_name | view_name} [HOLDLOCK]
    [,[[database.]owner.]{table_name | view_name} [HOLDLOCK]...]
    [WHERE search_conditions]
    [GROUP BY [ALL] aggregate_free_expression
    [, aggregate_free_expression]...]
    [HAVING search_conditions]
    [ORDER BY {[[[database.]owner.]{table_name |
     view_name}.]column_name | select_list_number | expression}
    [ASC | DESC] [,{[[[database.]owner.]{table_name |
     view_name}.]column_name | select_list_number | expression}
    [ASC | DESC]]...]
    [COMPUTE row_aggregate(column_name)
    [, row_aggregate(column_name)...]
    [BY column_name [, column_name...]]]
    [FOR BROWSE]

 The clauses in a SELECT statement must be used in the order shown here.
 (That is, if the statement includes a GROUP BY clause and an ORDER BY
 clause, the GROUP BY clause must be typed before the ORDER BY clause.)

 As the "Identifiers" section in Chapter 1 explains, the names of database
 objects must be qualified if there is ambiguity about which object is being
 referred to. For example, if there are several columns called name, you may
 have to qualify name with the database name, owner name, or table name.

 Since the examples in this chapter involve single-table queries, column
 names in syntax models and examples are usually not qualified with the names
 of the tables, owners, or databases to which they belong. These elements are
 left out to promote readability; it is never wrong to include qualifiers.

 The following sections describe the syntax of the SELECT statement in
 detail.


 Choosing Columns: the Select List

 The select list frequently consists of a series of column names separated by
 commas (in the order in which the table was created) or an asterisk to
 represent all columns.

 However, the select list can include one or more expressions (separated by
 commas) where an expression is a constant, column name, function, subquery,
 or any combination of these connected by arithmetic or bitwise operators and
 parentheses. The select list has the following syntax:

   SELECT expression [, expression...]
    FROM table_list


 Choosing All Columns: SELECT *

 The asterisk (*) has a special meaning in SELECT statements. It stands for
 all the column names in all the tables in the FROM clause. Use it to save
 typing time (and cut down on typing errors) when you want to see all the
 columns in a table.

 The syntax for selecting all the columns in a table is as follows:

   SELECT *
    FROM table_list

 Because SELECT * finds all the columns currently in a table, changes in the
 structure of a table (adding, removing, or renaming columns) automatically
 modify the results of a SELECT *. Listing the columns individually allows
 more precise control over the results.

 The following statement retrieves all columns in the publishers table and
 displays them in the order in which they were defined when the publishers
 table was created. No WHERE clause is included, so this statement also
 retrieves every row.

   select *
    from publishers

 The results look like this:

   pub_id  pub_name    city   state
    ------  -------------------  ----------  -----
    0736  New Age Books   Boston   MA
    0877  Binnet & Hardley  Washington  DC
    1389  Algodata Infosystems Berkeley  CA

    (3 rows affected)

 You get exactly the same results by listing all the column names in the
 table in order after the SELECT keyword:

   select pub_id, pub_name, city, state
    from publishers

 You can use * more than once in a query:

   select *, *
    from publishers

 This format displays each column name and each piece of column data twice.

 Like a column name, * can be qualified with a table name, as in the
 following query:

   select publishers.*
    from publishers


 Choosing Specified Columns

 To select some (but not necessarily all) of the columns in a table, use the
 following syntax:

   SELECT column_name[, column_name...]
    FROM table_name

 Each column name must be separated from the following column name by a
 comma.


 Rearranging the Order of Columns

 The order in which you list the column names determines the order in which
 the columns are displayed. The following two examples show how you specify
 column order in a display and the results. Both of them find and display the
 publisher names and identification numbers from all three rows in the
 publishers table. The first one prints pub_id first, followed by pub_name.
 The second reverses that order. The information is exactly the same; only
 its organization changes.

   select pub_id, pub_name
    from publishers

    pub_id  pub_name
    ------  --------------------
    0736  New Age Books
    0877  Binnet & Hardley
    1389  Algodata Infosystems

    (3 rows affected)

    select pub_name, pub_id
    from publishers

    pub_name    pub_id
    -------------------- ------
    New Age Books   0736
    Binnet & Hardley  0877
    Algodata Infosystems 1389

    (3 rows affected)


 Renaming Columns in Query Results

 When query results are displayed, the name given to a column when it was
 created is the default heading. You can specify a column heading by using
 either "column heading = column_name" or "column_name column heading"
 instead of just the column name in a select list. This provides a substitute
 name for the column name. When this name is displayed in the results, it
 functions as a column heading, which can produce more readable results.

 For example, to change pub_name to Publisher in the query shown earlier,
 type either of the following statements:

   select Publisher = pub_name, pub_id
    from publishers

 or

   select pub_name Publisher, pub_id
    from publishers

 The results of either statement look like this:

   Publisher   pub_id
    ---------------- --------
    New Age Books   0736
    Binnet & Hardley  0877
    Algodata Infosystems 1389

    (3 rows affected)


 Character Strings in Query Results

 The SELECT statements you've seen so far produce results that consist of
 data from the tables in the FROM clause. Strings (of characters) can also be
 displayed in query results.

 Enclose the entire string in single or double quotation marks and separate
 it from other elements in the select list with commas. Use double quotation
 marks if there is an apostrophe in the string; otherwise, the apostrophe is
 interpreted as a single quotation mark.

 An example statement with a character string is shown here, followed by its
 results.

   select "The publisher's name is", Publisher = pub_name
    from publishers

           Publisher
    ------------------------ --------------------
    The publisher's name is  New Age Books
    The publisher's name is  Binnet & Hardley
    The publisher's name is  Algodata Infosystems

    (3 rows affected)


 Computed Values in the Select List

 You can perform computations with data from numeric columns or on numeric
 constants in a select list.


 Arithmetic Operators

 The following arithmetic operators are available. (See the SQL Server
 Language Reference for information on bitwise operators.)

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 Symbol                            Operation
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ++                                addition

 -                                 subtraction

 /                                 division

 *                                 multiplication

 %                                 modulo

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Symbol                            Operation
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 The arithmetic operatorsÄaddition, subtraction, division, and
 multiplicationÄcan be used on any numeric columnÄint, smallint, tinyint,
 float, or money. The modulo operator cannot be used on money columns. A
 modulo is the integer remainder after a division operation on two integers.
 For example, 21 % 9 = 3, because 21 divided by 9 equals 2, with a remainder
 of 3.

 Certain arithmetic operations can also be performed on datetime columns,
 using the date functions. See Chapter 5, "Using Built-In Functions," for
 information on the date functions.

 All of these operators can be used in the select list with column names and
 numeric constants in any combination. The following example shows a
 projected sales increase of 100% for all the books in the titles table:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title_id, ytd_sales, ytd_sales * 2

         from titles

         title_id                                                 ytd_sales

         --------                                                 ---------  -

         BU1032                                                   4095       8

         BU1111                                                   3876       7

         BU2075                                                   18722      3

         BU7832                                                   4095       8

         MC2222                                                   2032       4

         MC3021                                                   22246      4

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         MC3026                                                   NULL       N

         PC1035                                                   8780       1

         PC8888                                                   4095       8

         PC9999                                                   NULL       N

         PS1372                                                   375        7

         PS2091                                                   2045       4

         PS2106                                                   111        2

         PS3333                                                   4072       8

         PS7777                                                   3336       6

         TC3218                                                   375        7
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        TC3218                                                   375        7

         TC4203                                                   15096      3

         TC7777                                                   4095       8

         (18 rows affected)



 Notice the null values in the ytd_sales column and the computed column. Null
 values have no explicitly assigned values. When you perform any arithmetic
 operation on a null value, the result is NULL.

 To give the computed column a heading (say proj_sales), type

   select title_id, ytd_sales, proj_sales = ytd_sales * 2
    from titles

 You can also add character strings such as "Current sales =" and "Projected
 sales are" to the SELECT statement.

 The column from which the computed column is generated does not have to
 appear in the select list. The ytd_sales column, for example, is shown in
 these sample queries only for comparison of its values with the values from
 the ytd_sales * 2 column. To see just the computed values, type

   select title_id, ytd_sales * 2
    from titles

 Arithmetic operators also work directly with the data values in specified
 columns when no constants are involved. Here's an example:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title_id, ytd_sales * price

         from titles

         title_id

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         ------                                                          -----

         BU1032                                                          81,85

         BU1111                                                          46,31

         BU2075                                                          55,97

         BU7832                                                          81,85

         MC2222                                                          40,61

         MC3021                                                          66,51

         MC3026                                                          NULL

         PC1035                                                          201,5

         PC8888                                                          81,90
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        PC8888                                                          81,90

         PC9999                                                          NULL

         PS1372                                                          8,096

         PS2091                                                          22,39

         PS2106                                                          777.0

         PS3333                                                          81,39

         PS7777                                                          26,65

         TC3218                                                          7,856

         TC4203                                                          180,3

         TC7777                                                          61,38

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         (18 rows affected)



 Finally, computed columns can come from more than one table. (Chapter 3,
 "Joining Two or More Tables," and Chapter 6, "Building Subqueries," tell how
 to work with multiple-table queries, so check them for syntax details.)

 This query calculates the product of the quantity of books sold by an outlet
 (the qty column from the sales table) and the price of the book (the price
 column from the titles table).

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄŚÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select titles.title_id, stor_id, qty * price

         from titles, sales

         where titles.title_id = sales.title_id
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        where titles.title_id = sales.title_id

         title_id                                                 stor_id

         -------                                                  -------  ---

         BU1032                                                   8042     199

         BU1032                                                   6380     99.

         BU1111                                                   8042     298

         BU2075                                                   7896     104

         BU7832                                                   7896     299

         MC2222                                                   7896     199

         MC3021                                                   7131     74.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         MC3021                                                   8042     44.

         PC1035                                                   8042     688

         PC8888                                                   7066     1,0

         PS1372                                                   7131     431

         PS2091                                                   7066     821

         PS2091                                                   7067     109

         PS2091                                                   7131     219

         PS2091                                                   6380     32.

         PS2106                                                   7131     175

         PS3333                                                   7131     299
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        PS3333                                                   7131     299

         PS7777                                                   7131     199

         TC3218                                                   7067     838

         TC4203                                                   7067     239

         TC7777                                                   7067     299

         (21 rows affected)




 Datatype Precedence in Mixed-Mode Arithmetic

 Mixed-mode arithmetic means performing arithmetic operations on values of
 different datatypes; for example int + smallint. The hierarchy of datatypes,
 which is created from the rank ordering of the hexadecimal values of the
 datatype0 codes, determines the datatype of the result. In a mixed mode
 expression, the lower datatype is converted internally to the higher type.

 In the following example, qty from the sales table is multiplied by royalty
 from the roysched table. qty has a smallint datatype; royalty has an int
 datatype. The smallint datatype has a hexadecimal value of 34; the int
 datatype has a hexadecimal value of 38. Therefore, the resultant datatype is
 int.

   smallint(qty) * int(royalty) = int

 The hierarchy of datatypes is shown in the system table systypes. (See also
 the SQL Server System Administrator's Guide and the SQL Server Language
 Reference.)

 You can use the following query to obtain a list of datatypes with their
 decimal and hexadecimal values. Select the name of the datatype and its
 decimal type code; then use the CONVERT function to compute the hexadecimal
 value of each datatype.

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select name, type, hex=convert(varbinary(2),type) from
         systypes

         order by type

         go

         name                                                            type

         ---------                                                       ----

         image                                                           34

         text                                                            35

         timestamp                                                       37

         varbinary                                                       37

         intn                                                            38
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        intn                                                            38

         id                                                              39

         tid                                                             39

         sysname                                                         39

         varchar                                                         39

         binary                                                          45

         char                                                            47

         tinyint                                                         48

         bit                                                             50

         smallint                                                        52

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         int                                                             56

         money                                                           60

         datetime                                                        61

         float                                                           62

         floatn                                                          109

         moneyn                                                          110

         datetimn                                                        111

         (21 rows affected)



 The results of this query on one of your databases will be slightly
 different if you have any user-defined datatypes. User-defined datatypes
 take on the hexadecimal value of their base datatypes.


 Arithmetic Operator Precedence

 When there is more than one arithmetic operator in an expression,
 multiplication, division, and modulo are calculated first, followed by
 subtraction and addition. When all arithmetic operators in an expression
 have the same level of precedence, the order of execution is left to right.
 Expressions within parentheses take precedence over all other operations.

 For example, the following SELECT statement subtracts the part of the
 year-to-date sales that the author receives (sales times author's royalty
 percent, divided by 100) from the total sales. The final result is the
 amount of money the publisher receives.

 The product of ytd_sales and royalty is calculated first because the
 operator is multiplication. Next, the total is divided by 100. Then, this
 result is subtracted from ytd_sales.

   select title_id, ytd_sales - ytd_sales * royalty / 100
    from titles

 To avoid misunderstandings, use parentheses. The following query has the
 same meaning and gives the same results as the previous one, but some may
 find it easier to understand:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title_id, ytd_sales - (ytd_sales * royalty / 100)

         from titles

         title_id

         --------

         BU1032

         BU1111

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         BU2075

         BU7832

         MC2222

         MC3021

         MC3026

         PC1035

         PC8888

         PC9999

         PS1372

         PS2091
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        PS2091

         PS2106

         PS3333

         PS7777

         TC3218

         TC4203

         TC7777

         (18 rows affected)



 Use parentheses to change the order of execution; calculations inside
 parentheses are handled first. If parentheses are nested, the most deeply
 nested calculation has precedence. For example, the result and meaning of
 the preceding can be changed if you use parentheses to force evaluation of
 the subtraction before the multiplication:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title_id, (ytd_sales - ytd_sales) * royalty / 100

         from titles

         title_id

         --------

         BU1032

         BU1111

         BU2075

         BU7832
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        BU7832

         MC2222

         MC3021

         MC3026

         PC1035

         PC8888

         PC9999

         PS1372

         PS2091

         PS2106

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         PS3333

         PS7777

         TC3218

         TC4203

         TC7777

         (18 rows affected)




 Selecting text and image Values

 You may select text and image values with the SELECT or READTEXT statements.



 Using the SELECT Statement

 When the select list includes text and image values, the limit on the length
 of the data returned depends on the setting of the @TEXTSIZE global
 variable. The default setting for TEXTSIZE is 32 kilobytes (K). The value is
 changed with the SET statement, as shown in the following example:

   set textsize 25

 With this setting of TEXTSIZE, a SELECT statement that included a text
 column would display only the first 25 characters of the data, as shown in
 the following example:

   select (pub_id, blurb) from blurbs

 Since pubs does not actually contain a blurbs table with a blurb text
 column, this example cannot be run.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 When you are selecting image data, the returned value includes the
 characters "Ox" that indicate the data is hexadecimal. These two characters
 are counted as part of TEXTSIZE.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 To reset TEXTSIZE to its default value, use the following statement:

   set textsize 0

 The default display is the actual length of the data when its size is less
 than TEXTSIZE.


 Using the READTEXT Statement

 The READTEXT statement provides another way to retrieve text and image
 values. The READTEXT statement parameters include the name of the table and
 column, the text pointer, a starting offset within the column, and the
 number of bytes to retrieve. The following example finds five characters in
 the blurb column in the blurbs table:

   declare @val varbinary (30)
    select @val = textptr(blurb) from blurbs
    where title_id = "PS3333"
    readtext blurbs.blurb @val 1 5

 In this example, READTEXT displays characters 2 through 6 of the blurb
 column, since the offset was 1.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 Since pubs does not actually contain a blurbs table, this example cannot be
 run.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 The READTEXT statement has the following syntax:

   READTEXT [[database.]owner.]table_name.column_name text_ptr
    offset size [HOLDLOCK]

 Without using a local variable a statement such as the following returns a
 long binary string:

   textptr(text_col) from my_table

 It is a good idea to put this string into a local variable, as in the
 preceding example, and use it by reference.


 Select List Summary

 The select list can include * (all columns in the order in which the table
 was created), a list of column names in any order, character strings, column
 headings, and expressions including arithmetic operators. You can also
 include aggregate functions, which are discussed in "Grouping Data: GROUP BY
 and HAVING Clauses," later in this chapter, and in Chapter 4, "Displaying
 Totals: GROUP BY and COMPUTE." Here are some select lists to try with the
 tables in the pubs database:

   A. select titles.*
    from titles

   B. select Name = au_fname, Surname = au_lname
    from authors

   C. select Sales = ytd_sales, ToAuthor = ytd_sales * royalty,
    ToPublisher = ytd_sales - (ytd_sales * royalty)
    from titles

   D. select 'Social security #', au_id
    from authors

   E. select this_year = advance,
    next_year = advance + advance/10,
    third_year = advance /2, 'for book title #', title_id
    from titles

   F. select 'Total income is', Revenue = price * ytd_sales,
    'for', Book# = title_id
    from titles


 Eliminating Duplicates: DISTINCT

 The DISTINCT keyword, which is optional before the select list, eliminates
 duplicate rows from the results of a SELECT statement.

 If you don't specify DISTINCT, you get all rows, including duplicates.

 For example, if you select all the author identification codes in the
 titleauthor table without DISTINCT, you'll get these rows:

   select au_id
    from titleauthor

    au_id
    -----------
    172-32-1176
    213-46-8915
    213-46-8915
    238-95-7766
    267-41-2394
    267-41-2394
    274-80-9391
    409-56-7008
    427-17-2319
    472-27-2349
    486-29-1786
    486-29-1786
    648-92-1872
    672-71-3249
    712-45-1867
    722-51-5454
    724-80-9391
    724-80-9391
    756-30-7391
    807-91-6654
    846-92-7186
    899-46-2035
    899-46-2035
    998-72-3567
    998-72-3567

    (25 rows affected)

 Looking at the results, you'll see that there are some duplicate listings.
 You can eliminate them and see only the unique au_ids with DISTINCT:

   select distinct au_id
    from titleauthor

    au_id
    -----------
    172-32-1176
    213-46-8915
    238-95-7766
    267-41-2394
    274-80-9391
    409-56-7008
    427-17-2319
    472-27-2349
    486-29-1786
    648-92-1872
    672-71-3249
    712-45-1867
    722-51-5454
    724-80-9391
    756-30-7391
    807-91-6654
    846-92-7186
    899-46-2035
    998-72-3567

    (19 rows affected)

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 For compatibility with other implementations of SQL, SQL Server syntax
 allows the use of the ALL keyword to explicitly ask for all rows. However,
 there is no reason to use ALL because "all rows" is the default.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 For the DISTINCT keyword, null values are considered to be duplicates of
 each other. In other words, when DISTINCT is included in a SELECT statement,
 only one NULL is returned in the results, no matter how many null values are
 encountered.


 Specifying Tables: the FROM Clause

 The FROM clause is required in every SELECT statement involving data from
 tables or views. Use it to list all the tables and views containing columns
 included in the select list and in the WHERE clause. If the FROM clause
 includes more than one table or view, separate them with commas. The FROM
 clause has the following syntax:

   SELECT select_list
    FROM [[database.]owner.]{table_name | view_name} [HOLDLOCK]
    [,[[database.]owner.]{table_name | view_name} [HOLDLOCK]...]

 Table names can be from 1 to 30 characters long. You can use a letter, #, or
 _ as the first character. The following characters can be digits, letters,
 #, $, or _. A table name that begins with # denotes a temporary table. (See
 Chapter 7, "Creating Databases, Tables, and Indexes.")

 The full naming syntax for tables and views is always permitted in the FROM
 clause:

   database.owner.table_name

    database.owner.view_name

 This is necessary, however, only when there might be some confusion about
 the name.

 Table names can be given aliases to save typing. Aliases are assigned in the
 FROM clause by giving the alias after the table name:

   select p.pub_id, p.pub_name
    from publishers p

 The optional HOLDLOCK keyword in a SELECT statement makes a system-generated
 lock more restrictive. It keeps a shared lock in force until the completion
 of a transaction, instead of releasing the lock as soon as the data is no
 longer needed. (See Chapter 14, "Advanced Topics for Database Owners," for
 details.)


 Selecting Rows: the WHERE Clause

 The WHERE clause in a SELECT statement specifies the criteria for exactly
 which rows are retrieved. The WHERE clause has the following syntax:

   SELECT select_list
    FROM table_list
    WHERE search_conditions

 Search conditions, or qualifications, in the WHERE clause include


   ž   Comparison operators (=, <, >, and so on)

       where advance * 2 > ytd_sales * price


   ž   Ranges (BETWEEN and NOT BETWEEN)

       where ytd_sales between 4095 and 12000


   ž   Lists (IN, NOT IN)

       where state in ("CA", "IN", "MD")


   ž   Character matches (LIKE and NOT LIKE)

       where phone not like "415%"


   ž   Unknown values (IS NULL and IS NOT NULL)

       where advance is null


   ž   Combinations of these (AND, OR)

       where advance < 5000 or ytd_sales between 2000 and 2500



 In addition, the WHERE keyword can introduce join conditions and subqueries.
 (See Chapter 3, "Joining Two or More Tables," and Chapter 6, "Building
 Subqueries.")

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 The only WHERE condition that you can use on text columns is LIKE (and NOT
 LIKE).
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 For a complete list of the possible search conditions, including a few not
 mentioned here, see the "Search Conditions" or "WHERE Clause" section of the
 SQL Server Language Reference.


 Comparison Operators

 TRANSACT-SQL uses the following comparison operators:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 Operator                          Meaning
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 =                                 equal to

 >                                 greater than

 <                                 less than

 >=                                greater than or equal to

 <=                                less than or equal to

 !=                                not equal to

 !>                                not greater than

 !<                                not less than

 Operator                          Meaning
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 The operators have the following syntax:

   WHERE expression comparison_operator expression

 An expression is a constant, column name, function, subquery, or any
 combination of them connected by arithmetic or bitwise operators.

 In comparing char and varchar data, < means earlier in the alphabet, and >
 means later in the alphabet.

 Trailing blanks are ignored for the purposes of comparison. So, for example,
 "Dirk" is the same as "Dirk ".

 In comparing dates, < means earlier and > means later.

 Be sure to put apostrophes or quotation marks around all char, varchar,
 text, and datetime data. (For more on entering datetime data, see Chapter 9,
 "Adding, Changing, and Removing Data.")

 The following examples use SELECT statements with comparison operators:

   A. select *
    from titleauthor
    where royaltyper < 50
   B. select authors.au_lname, authors.au_fname
    from authors
    where au_lname >'McBadden'
   C. select au_id, phone
    from authors
    where phone !='415 658-9932'
   D. select title_id, newprice = price * $1.15
    from pubs..titles
    where advance > 5000

 NOT negates an expression. Either of the two following queries will find all
 business and psychology books that do not have an advance over $5,500.
 However, note the difference in position between the negative logical
 operator (NOT) and the negative comparison operator (!).

   select title_id, type, advance
    from titles
    where (type = "business" or type = "psychology")
    and not advance >5500

    select title_id, type, advance
    from titles
    where (type = "business" or type = "psychology")
    and advance !>5500

    title_id  type    advance
    --------  ----------   --------
    BU1032   business   5,000.00
    BU1111   business   5,000.00
    BU7832   business   5,000.00
    PS2091   psychology   2,275.00
    PS3333   psychology   2,000.00
    PS7777   psychology   4,000.00

    (6 rows affected)


 Ranges (BETWEEN and NOT BETWEEN)

 The BETWEEN keyword specifies an inclusive range in which the lower value
 and the upper value are searched for as well as the values they bracket.

 For example, to find all the books with sales between (and including) 4,095
 and 12,000, use this query:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title_id, ytd_sales

         from titles

         where ytd_sales between 4095 and 12000

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         title_id                                                         ytd_

         --------                                                         ----

         BU1032                                                           4095

         BU7832                                                           4095

         PC1035                                                           8780

         PC8888                                                           4095

         TC7777                                                           4095

         (5 rows affected)



 Notice that books with sales of 4,095 are included in the results. If there
 were any with sales of 12,000, they would be included too. You can specify
 an exclusive range with the greater-than and less-than operators. The same
 query using the greater-than and less-than operators returns the following
 results because these operators are not inclusive:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title_id, ytd_sales

         from titles

         where ytd_sales > 4095 and ytd_sales < 12000

         title_id                                                         ytd_

         --------                                                         ----

         PC1035                                                           8780

         (1 row affected)

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 NOT BETWEEN finds all the rows that are not inside the range. To find all
 the books with sales outside the 4,095 to 12,000 range, type

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title_id, ytd_sales

         from titles

         where ytd_sales not between 4095 and 12000

         title_id                                                         ytd_

         --------                                                         ----

         BU1111                                                           3876

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         BU2075                                                           1872

         MC2222                                                           2032

         MC3021                                                           2224

         PS1372                                                           375

         PS2091                                                           2045

         PS2106                                                           111

         PS3333                                                           4072

         PS7777                                                           3336

         TC3218                                                           375

         TC4203                                                           1509
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        TC4203                                                           1509

         (11 rows affected)




 Lists (IN and NOT IN)

 The IN keyword allows you to select values that match any one of a list of
 values. For example, without IN, if you want a list of the names and states
 of all the authors who live in California, Indiana, or Maryland, you can
 type this query:

   select au_lname, state
    from authors
    where state = 'CA' or state = 'IN' or state = 'MD'

 However, you get the same results with less typing if you use IN. The items
 following the IN keyword must be separated by commas and enclosed in
 parentheses.

   select au_lname, state
    from authors
    where state in('CA', 'IN', 'MD')

 This is what results from either query:

   au_lname  state
    --------  -----
    White   CA
    Green   CA
    Carson   CA
    O'Leary   CA
    Straight  CA
    Bennet   CA
    Dull   CA
    Gringlesby  CA
    Locksley  CA
    Yokomoto  CA
    DeFrance  IN
    Stringer  CA
    MacFeather  CA
    Karsen   CA
    Panteley  MD
    Hunter   CA
    McBadden  CA

    (17 rows affected)

 Perhaps the most important use for the IN keyword is in nested queries, also
 referred to as subqueries. (For a full discussion of subqueries, see Chapter
 6, "Building Subqueries.") The following example gives some idea of what you
 can do with nested queries and the IN keyword.

 Suppose you want to know the names of the authors who receive less than 50
 percent of the total royalties on the books they coauthor. The authors table
 gives author names and the titleauthor table gives royalty information. By
 putting the two together using IN (but without listing the two tables in the
 same FROM clause) you can extract the information you need.

 The following query finds all the au_ids in the titleauthor table for
 authors who make less than 50 percent of the royalty on any one book, then
 selects from the authors table all the author names with au_ids that match
 the results from the titleauthor query. The results show that several
 authors fall into the less than 50 percent category.

   select au_lname, au_fname
    from authors
    where au_id in
    (select au_id
    from titleauthor
    where royaltyper <50)

    au_lname   au_fname
    -----------   --------
    Green    Marjorie
    O'Leary    Michael
    O'Leary    Michael
    Gringlesby   Burt
    Yokomoto   Akiko
    MacFeather   Stearns
    Ringer    Anne

    (7 rows affected)

 NOT IN finds the authors that do not match the items in the list. The
 following query finds the names of authors who do not make less than 50
 percent of the royalties on at least one book.

   select au_lname, au_fname
    from authors
    where au_id not in
    (select au_id
    from titleauthor
    where royaltyper <50)

    au_lname   au_fname
    -------------  --------
    White    Johnson
    Carson    Cheryl
    Straight   Dick
    Smith    Meander
    Bennet    Abraham
    Dull    Ann
    Locksley   Chastity
    Greene    Morningstar
    Blotchet-Halls  Reginald
    del Castillo  Innes
    DeFrance   Michel
    Stringer   Dirk
    Karsen    Livia
    Panteley   Sylvia
    Hunter    Sheryl
    McBadden   Heather
    Ringer    Albert

    (17 rows affected)


 Wildcards and the LIKE Clause

 The LIKE keyword selects rows containing fields that match specified
 portions of character strings. LIKE is used with char, varchar, text, and
 datetime data. It takes four wildcard characters:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Wildcard                          Meaning
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 %                                 Any string of zero or more characters

 _                                 Any single character

 [ ]                               Any single character within the specified
                                   range (for example, [a-f]) or set (for
                                   example, [abcdef])

 [^]                               Any single character not within the
                                   specified range (for example, [^a-f]) or
                                   set (for example, [^abcdef])

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Enclose the wildcard(s) and character string in quotation marks. For
 example, using LIKE with the data in the authors table:


   ž   LIKE 'Mc%' searches for every name that begins with the letters "Mc"
       (McBadden).

   ž   LIKE '%inger' searches for every name that ends with "inger" (Ringer,
       Stringer).

   ž   LIKE '%en%' searches for every name that has the letters "en" in it
       (Bennet, Green, McBadden).

   ž   LIKE '_heryl' searchs for every 6-letter name ending with "heryl"
       (Cheryl).

   ž   LIKE '[CK]ars[eo]n' searches for "Carsen," "Karsen," "Carson," and
       "Karson" (Carson).

   ž   LIKE '[M-Z]inger' searches for all the names ending with "inger" that
       begin with any single letter from M to Z (Ringer).

   ž   LIKE 'M[^c]%' searches for all names beginning with "M" not having "c"
       as the second letter.


 This query finds all the phone numbers in the authors table that have 415 as
 the area code:

   select phone
    from authors
    where phone like '415%'

 You can use NOT LIKE with the same wildcards. To find all the phone numbers
 in the authors table that do not have 415 as the area code, you could use
 either of these queries (they are equivalent):

   select phone
    from authors
    where phone not like '415%'

    select phone
    from authors
    where not phone like '415%'

 Wildcards used without LIKE are interpreted as literals rather than as a
 pattern; they represent exactly their own values. The following query
 attempts to find any phone numbers that consist of the four characters
 "415%" only. It will not find phone numbers that start with 415.

   select phone
    from authors
    where phone = '415%'

 You can search for the wildcards themselves, too. To use the wildcards as
 characters in a LIKE match string rather than as wildcards, use square
 brackets to enclose the percent sign, the underscore, and the open bracket;
 use the close bracket by itself. To search for a dash, rather than using it
 to specify a range for which to search, use the dash as the first character
 inside a set of brackets.

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Symbol                            Meaning
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Symbol                            Meaning
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 LIKE '5%'                         5 followed by any string of 0 or more
                                   characters

 LIKE '5[%]'                       5%

 LIKE '_n'                         an, in, on, and so on

 LIKE '[_]n'                       _n

 LIKE '[a-cdf]'                    a, b, c, d, or f

 LIKE '[-acdf]'                    -, a, c, d, or f

 LIKE '[[]'                         [

 LIKE ']'                          ]

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Symbol                            Meaning
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 For a few more variations on LIKE and the wildcard characters, see the SQL
 Server Language Reference.


 Character Strings and Quotes

 Character and date data (char, varchar, and datetime datatypes) must be
 enclosed in single or double quotation marks when you enter or search for
 them.

 There are two ways to specify literal quotation marks within a character
 entry. The first method is to use two consecutive quotation marks. For
 example, if you have begun a character entry with a single quotation mark
 and wish to include a single quotation mark as part of the entry, use two
 single quotation marks:

   'I don"t understand.'

 With double quotation marks:

   "He said, ""It's not really confusing."""

 The second method is to enclose a quote in the other kind of quotation mark.
 In other words, surround an entry containing double quotation marks with
 single quotation marks (or vice versa). Here are some examples:

   'George said, "There must be a better way."'
    "Isn't there a better way?"
    'George asked, "Isn"t there a better way?"'

 To continue a character string that would go off the end of one line on your
 screen, enter a backslash (\) before going to the following line.


 Unknown Values: IS NULL and IS NOT NULL

 When you see NULL in a column, it means that the user or application has
 made no entry in that column. A data value for the column is "unknown" or
 "not available."

 NULL is not synonymous with zero (numerical values) or blank (character
 values). Rather, null values allow you to distinguish between a deliberate
 entry of zero (for numeric columns) or blank (for character columns) and a
 non-entry (NULL for both numeric and character columns).

 NULL can be entered in a column for which null values are permitted (as
 specified in the CREATE TABLE statement) in two ways. If no data is entered,
 SQL Server automatically enters the value NULL. The user can explicitly
 enter the value NULL by typing the word NULL or null without single or
 double quotation marks.

 If the word NULL is typed into a character column with single or double
 quotation marks, it is treated as data, not as a null value.

 When null values are retrieved, displays of query results show the word NULL
 in the appropriate position. For example, the advance column of the titles
 table allows null values. By inspecting the data in that column, you can
 tell whether a book had no advance payment by agreement (zero in the advance
 column, probably due to poor negotiating skills on the author's part, as in
 the row for MC2222) or whether the advance amount was not known when the
 data was entered (NULL in the advance column, as in the row for MC3026).

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title_id, type, advance

         from titles

         where pub_id = "0877"

         title_id                                           type        advanc

         --------                                           ----------  ------

         MC2222                                             mod_cook    0.00

         MC3021                                             mod_cook    15,000

         MC3026                                             UNDECIDED   NULL
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        MC3026                                             UNDECIDED   NULL

         PS1372                                             psychology  7,000.

         TC3218                                             trad_cook   7,000.

         TC4203                                             trad_cook   4,000.

         TC7777                                             trad_cook   8,000.

         (7 rows affected)



 When you compare a null value with another value, the result is never true;
 a null value does not match anything, not even another null value. So, for
 example, "ytd_sales > null" is false and will never return any results.

 Null values do not match each other because there is no reason to assume
 that two unknown values would be the same. So null values never join, not
 even to other null values. (See Chapter 3, "Joining Two or More Tables.")

 However, null values are not considered different for the purposes of the
 DISTINCT keyword.

 Computations involving NULL evaluate to NULL because the result must be
 unknown if any of the factors is unknown. For example, 1 + column1 evaluates
 to NULL if column1 is NULL.

 You can find null values (or non-null values when the columns being searched
 are defined as allowing null values) in the database with this pattern:

   WHERE column_name IS [NOT] NULL

 If you try to find null values in columns defined as NOT NULL, SQL Server
 displays an error message.

 Some of the rows in the titles table contain incomplete data. For example, a
 book called The Psychology of Computer Cooking has been proposed and its
 title, title identification number, and probable publisher entered. However,
 since the author has no contract as yet and details are still up in the air,
 null values appear in the price, advance, royalty, ytd_sales, and notes
 columns.

 Because null values don't match anything in a comparison, a query for all
 the title identification numbers and advances for books with moderate
 advances (under $5,000) will not find the row for The Psychology of Computer
 Cooking, title identification number MC3026.

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title_id, advance

         from titles

         where advance < $5000

         title_id                                                       advanc

         --------                                                       ------

         MC2222                                                         0.00
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        MC2222                                                         0.00

         PS2091                                                         2,275.

         PS3333                                                         2,000.

         PS7777                                                         4,000.

         TC4203                                                         4,000.

         (5 rows affected)



 Here's how a query for books with an advance under $5000 or a null value in
 the advance column would look:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title_id, advance
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title_id, advance

         from titles

         where advance < $5000

         or advance is null

         title_id                                                       advanc

         --------                                                       ------

         MC2222                                                         0.00

         MC3026                                                         NULL

         PC9999                                                         NULL

         PS2091                                                         2,275.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         PS3333                                                         2,000.

         PS7777                                                         4,000.

         TC4203                                                         4,000.

         (7 rows affected)



 (See Chapter 7, "Creating Databases, Tables, and Indexes," for information
 on NULL in the CREATE TABLE statement and for information on the
 relationship between NULL and defaults. See Chapter 9, "Adding, Changing,
 and Removing Data," for information on inserting null values into a table.
 See the SQL Server Language Reference for more information.)


 Connecting Conditions with Logical Operators

 The logical operators AND, OR, and NOT are used to connect search conditions
 in WHERE clauses.

 AND joins two or more conditions and returns results only when all of the
 conditions are true. For example, the following query finds only the rows in
 which the author's last name is Ringer and the author's first name is Anne.
 It will not find the row for Albert Ringer.

   select *
    from authors
    where au_lname = 'Ringer' and au_fname = 'Anne'

 OR also connects two or more conditions, but it returns results when any of
 the conditions is true. The following query searches for rows containing
 Anne or Ann in the au_fname column.

   select *
    from authors
    where au_fname = 'Anne' or au_fname = 'Ann'

 NOT negates the expression that follows it. The following query selects all
 the authors who do not live in California:

   select * from authors
    where not state = "CA"


 Logical Operator Precedence

 Arithmetic (and bitwise) operators are handled before logical operators.
 When more than one logical operator is used in a statement, NOT is evaluated
 first, then AND, and finally OR. See the SQL Server Language Reference for
 information on bitwise operators.

 For example, the following query finds all the business books in the titles
 table, no matter what their advances are, as well as all psychology books
 that have an advance greater than $5,500. The advance condition pertains to
 psychology books and not to business books because the AND is handled before
 the OR.

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title_id, type, advance

         from titles

         where type = "business" or type = "psychology"

         and advance >5500

         title_id                                           type        advanc

         --------                                           ----------  ------

         BU1032                                             business    5,000.

         BU1111                                             business    5,000.

         BU2075                                             business    10,125

         BU7832                                             business    5,000.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         PS1372                                             psychology  7,000.

         PS2106                                             psychology  6,000.

         (6 rows affected)



 You can change the meaning of the query by adding parentheses to force
 evaluation of the OR first. This query finds all business and psychology
 books that have advances over $5,500:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title_id, type, advance

         from titles

         where (type = "business" or type = "psychology")
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        where (type = "business" or type = "psychology")

         and advance >5500

         title_id                                           type        advanc

         --------                                           ----------  ------

         BU2075                                             business    10,125

         PS1372                                             psychology  7,000.

         PS2106                                             psychology  6,000.

         (3 rows affected)




 Creating a New Table for Results: SELECT INTO

 The SELECT INTO clause selects into a permanent table only if the select
 into/bulkcopy database option is set. To see whether this option is set,
 execute the sp_helpdb system procedure. Here's what the command and its
 results look like if the option is set:

   sp_helpdb pubs

    name  db_size  owner  dbid created    status
    ----  -------  -----  ---- ----------   --------------
    pubs  2 MB  sa   5   Jun 3 1988   no options set

     (1 row affected)


    device    size    usage
    ---------   -------    ------------
    master    2 MB    data and log

    (1 row affected)

 If the option is not set, the report from sp_helpdb will indicate this. Only
 the System Administrator or the Database Owner can set the database options.


 If the select into/bulkcopy database option is set, you can use the SELECT
 INTO clause to build a new permanent table without using a CREATE TABLE
 statement. (You can select into a temporary table even if the option is not
 set.) Unlike a view that displays a portion of a table, a table created with
 SELECT INTO is a separate, independent entity. (See Chapter 10, "Creating
 Views," for details on views.)

 The new table is based on the columns you specify in the select list, the
 table(s) you name in the FROM clause, and the rows you choose in the WHERE
 clause. The name of the new table must be unique in the database and must
 conform to the rules for identifiers.

 A SELECT statement with an INTO clause allows you to define a table and put
 data into it (based on existing definitions and data) without going through
 the usual data definition process.

 The following example shows a SELECT INTO statement and its results. A table
 called newtable is created, using two of the columns in the 4-column table
 publishers. Because this particular statement includes no WHERE clause, data
 from all of the rows (but only two of the columns) of publishers is copied
 into newtable.

   select pub_id, pub_name
    into newtable
    from publishers

    (4 rows affected)

 SQL Server's message "(4 rows affected)" refers to the row created in the
 system table sysobjects for the new table, newtable, plus the three rows
 inserted into newtable. Here's what newtable looks like:

   select *
    from newtable

    pub_id  pub_name
    ------  --------------------
    0736  New Age Books
    0877  Binnet & Hardley
    1389  Algodata Infosystems

    (3 rows affected)

 The new table contains the results of the SELECT statement. It becomes part
 of the database, just like its parent table.

 The INTO clause is useful for creating test tables, new tables as copies of
 existing tables, and for making several smaller tables out of one large
 table.

 You can also use SELECT INTO to create a skeleton table with no data by
 putting a false condition in the WHERE clause. For example:

   select *
    into newtable2
    from publishers
    where 1=2

    (0 rows affected)


    select *
    from newtable2

    pub_id  pub_name  city  state
    ------  --------  ----  -----

    (0 rows affected)

 No rows are inserted into the new table because 1 never equals 2.


 Grouping Data: GROUP BY and HAVING Clauses

 This section provides an overview of GROUP BY, HAVING, and the aggregate
 functions. (See Chapter 4, "Displaying Totals: GROUP BY and COMPUTE," for a
 detailed description of these topics.)

 Generally, GROUP BY organizes data into groups; HAVING sets conditions on
 which groups to include in the results. (HAVING is almost never used by
 itself.)

 Aggregate functions return summary values, either for the whole table or for
 groups within the table. For this reason they work well with GROUP BY.
 Aggregate functions can appear in a select list or a HAVING clause, but not
 in a WHERE clause.


 You can use these aggregate functions with GROUP BY (expression is almost
 always a column name):

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Aggregate Function                Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 SUM([DISTINCT] expression)        Total of the [distinct] values in the
                                   numeric column

 AVG([DISTINCT] expression)        Average of the [distinct] values in the
                                   numeric column

 COUNT([DISTINCT] expression)      Number of [distinct] non-null values in
 Aggregate Function                Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
COUNT([DISTINCT] expression)      Number of [distinct] non-null values in
                                   the column

 COUNT(*)                          Number of selected rows

 MAX(expression)                   Highest value in the expression

 MIN(expression)                   Lowest value in the expression

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 SUM and AVG work only with numeric values. SUM, AVG, COUNT, MAX, and MIN
 ignore null values, but COUNT(*) does not.

 The following query finds the total year-to-date sales of each publisher in
 the database:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   select pub_id, total = sum(ytd_sales)

                                   from titles

                                   group by pub_id



ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         pub_id             total

         ------             ------

         0736               28286

         0877               44219

         1389               24941
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        1389               24941

         (3 rows affected)



 Because of the GROUP BY clause, only one row is returned for each publisher,
 and it contains the sum of all sales for that publisher.

 The HAVING clause sets conditions on the GROUP BY clause, similar to the way
 the WHERE clause interacts with the SELECT clause. The HAVING clause syntax
 is exactly like the WHERE clause syntax except that the HAVING clause can
 contain aggregate functions and the WHERE clause cannot.

 If, for example, you want to make sure that there are more than five books
 involved in the calculations for each publisher, use HAVING COUNT(*)>5 to
 eliminate any publishers that are returning totals for fewer than six books.
 The query looks like this:

   select pub_id, total = sum(ytd_sales)
    from titles
    group by pub_id
    having count(*)>5

    pub_id  total
    ------  -----
    0877  44219
    1389  24941

    (2 rows affected)

 With this statement, two rows are returned. New Age Books (0736) is
 eliminated.


 Sorting Query Results: ORDER BY

 The ORDER BY clause sorts query results by one or more columns. You can sort
 up to 16 columns. Each sort can be either ascending (ASC) or descending
 (DESC). If neither is specified, ASC is assumed. The following query returns
 results ordered by pub_id:

   select pub_id, type, title_id
    from titles
    order by pub_id

    pub_id  type    title_id
    ------  ------------  --------
    0736  business   BU2075
    0736  psychology   PS2091
    0736  psychology   PS2106
    0736  psychology   PS3333
    0736  psychology   PS7777
    0877  UNDECIDED   MC3026
    0877  mod_cook   MC2222
    0877  mod_cook   MC3021
    0877  psychology   PS1372
    0877  trad_cook   TC3218
    0877  trad_cook   TC4203
    0877  trad_cook   TC7777
    1389  business   BU1032
    1389  business   BU1111
    1389  business   BU7832
    1389  popular_comp  PC1035
    1389  popular_comp  PC8888
    1389  popular_comp  PC9999

    (18 rows affected)

 If more than one column is named in the ORDER BY clause, sorts are nested.
 The following statement sorts the rows in the titles table first by
 publisher in descending order, then by type (ascending) within each
 publisher, and finally by title number (also ascending, since DESC is not
 specified). Null values are sorted first within any group.

   select pub_id, type, title_id
    from titles
    order by pub_id desc, type, title_id

    pub_id  type    title_id
    ------  ------------  --------
    1389  business   BU1032
    1389  business   BU1111
    1389  business   BU7832
    1389  popular_comp  PC1035
    1389  popular_comp  PC8888
    1389  popular_comp  PC9999
    0877  UNDECIDED   MC3026
    0877  mod_cook   MC2222
    0877  mod_cook   MC3021
    0877  psychology   PS1372
    0877  trad_cook   TC3218
    0877  trad_cook   TC4203
    0877  trad_cook   TC7777
    0736  business   BU2075
    0736  psychology   PS2091
    0736  psychology   PS2106
    0736  psychology   PS3333
    0736  psychology   PS7777

    (18 rows affected)

 The positional number of a column in a select list can be used instead of
 the column name. Column names and select list numbers can be mixed. Both of
 the following statements produce the same results as the preceding one:

   select pub_id, type, title_id
    from titles
    order by 1 desc, 2, 3

    select pub_id, type, title_id
    from titles
    order by 1 desc, type, 3

 Most versions of SQL require that ORDER BY items appear in the select list,
 but TRANSACT-SQL has no such restriction. You can order the results of the
 preceding query by title, although that column does not appear in the select
 list.


 Generating Summary Rows: COMPUTE BY

 Use COMPUTE BY with row aggregate functions to produce reports that
 summarize values whenever the value in a specified column changes. Such
 reports (usually produced by a report generator) are called control-break
 reports since summary values appear in the report under the control of the
 groupings ("breaks") you specify in the COMPUTE BY clause.

 These summary values appear as additional rows in the query results, unlike
 the aggregate function results of a GROUP BY clause, which appear as new
 columns.

 A COMPUTE BY clause allows you to see detail and summary rows with one
 SELECT statement. You can calculate summary values for subgroups and you can
 calculate more than one row aggregate function for the same group.

 The COMPUTE BY clause has the following syntax:

   COMPUTE row_aggregate(column_name)
    [, row_aggregate(column_name)...]
    [BY column_name [, column_name...]]

 The row aggregate functions you can use with COMPUTE BY are SUM, AVG, MIN,
 MAX, and COUNT. SUM and AVG are used with numeric columns only.

 Following are two queries and their results. The first one uses GROUP BY and
 aggregate functions. The second uses COMPUTE BY and row aggregate functions.
 Notice the difference in the displays.

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select type, sum(price), sum(advance)

         from titles

         group by type

         type

         ------------                                     -----        -------

         UNDECIDED                                        NULL         NULL

         business                                         54.92        25,125.

         mod_cook                                         22.98        15,000.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         popular_comp                                     42.95        15,000.

         psychology                                       67.52        21,275.

         trad_cook                                        47.89        19,000.

         (6 rows affected)

         select type, price, advance

         from titles

         order by type

         compute sum(price), sum(advance) by type

         type                                             price        advance

         ------------                                     -----------  -------
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        ------------                                     -----------  -------

         UNDECIDED                                        NULL         NULL

                                                          sum          sum

                                                          -----------  -------

                                                          NULL         NULL

         type                                             price        advance

         --------                                         -----------  -------

         business                                         2.99         10,125.

         business                                         11.95        5,000.0

         business                                         19.99        5,000.0

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         business                                         19.99        5,000.0

                                                          sum          sum

                                                          -----------  -------

                                                          54.92        25,125.

         type                                             price        advance

         --------                                         -----------  -------

         mod_cook                                         2.99         15,000.

         mod_cook                                         19.99        0.00

                                                          sum          sum

                                                          -----------  -------
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                                         -----------  -------

                                                          22.98        15,000.

         type                                             price        advance

         ------------                                     -----------  -------

         popular_comp                                     NULL         NULL

         popular_comp                                     20.00        8,000.0

         popular_comp                                     22.95        7,000.0

                                                          sum          sum

                                                          -----------  -------

                                                          42.95        15,000.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         type                                             price        advance

         ----------                                       -----------  -------

         psychology                                       7.00         6,000.0

         psychology                                       7.99         4,000.0

         psychology                                       10.95        2,275.0

         psychology                                       19.99        2,000.0

         psychology                                       21.59        7,000.0

                                                          sum          sum

                                                          -----------  -------

                                                          67.52        21,275.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                                         67.52        21,275.

         type                                             price        advance

         ---------                                        -----------  -------

         trad_cook                                        11.95        4,000.0

         trad_cook                                        14.99        8,000.0

         trad_cook                                        20.95        7,000.0

                                                          sum          sum

                                                          -----------  -------

                                                          47.89        19,000.

         (24 rows affected)

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 The summary values are treated as new rows, which is why SQL Server's
 message says "(24 rows affected)." (See Chapter 4, "Displaying Totals: GROUP
 BY and COMPUTE," for a fuller explanation, rules, and more examples of the
 COMPUTE clause.)






 Chapter 3  Joining Two or More Tables
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 This chapter describes retrieval operations that involve data from two or
 more tables. These tables can be in the same database or in different
 databases.

 The multi-table operation discussed in this chapter is the join. Subqueries
 (queries nested within other queries), which also can involve two or more
 tables, are covered in Chapter 6, "Building Subqueries." Many joins can be
 stated as subqueries.


 The Join Operation

 Joining two or more tables is a process of comparing the data in specified
 fields and using the comparison results to form a new table from the rows
 that qualify. Specifically, a join statement does the following:


   ž   Specifies a column from each table

   ž   Compares the values in those columns row by row

   ž   Combines rows with qualifying values into new rows


 The comparison is usually for equalityÄvalues that match exactlyÄbut other
 types of joins can be specified. If a join is to have meaningful results,
 the columns being compared must have similar valuesÄvalues drawn from the
 same domain.

 There are several varieties of joinsÄequijoins, natural joins, outer joins,
 and so on.

 The most common variety is the join based on equality. Here is an example of
 a join that finds the names of authors and publishers located in the same
 city:

   select au_fname, au_lname, pub_name
    from authors, publishers
    where authors.city = publishers.city

    au_fname  au_lname  pub_name
    --------  -------   --------------------
    Cheryl   Carson   Algodata Infosystems
    Abraham   Bennet   Algodata Infosystems

    (2 rows affected)

 Since the query draws on information contained in two separate tables,
 publishers and authors, a join is required to retrieve the requested
 information.


 The Select List

 A join statement, like a selection statement, starts with the keyword
 SELECT. The columns named after the SELECT keyword are the columns to be
 included in the query results, in their desired order. The previous example
 specified the columns that contained the authors' and publishers' names.

 In the preceding example, the columns au_fname, au_lname, and pub_name did
 not have to be qualified by a table name since there is no ambiguity about
 the table to which they belong. But the city column used for the join
 comparison did have to be qualified since there are columns of that name in
 both the publishers and authors tables. Though in this example neither of
 the city columns is printed in the results, SQL Server needs the table name
 to perform the comparison.

 As in a SELECT statement, you can use the "*" to specify that all the
 columns of the tables involved in the query be included in the results. For
 example, to include all the columns in publishers and authors in the
 preceding join query, the SQL statement is as follows:

   select *
    from authors, publishers
    where authors.city = publishers.city

    au_id   au_lname  au_fname
     phone      address
     city      state zip  contract pub_id
     pub_name     city   state
    -----------  ---------  ---------
     ------------     ----------------
     --------     ----- ----- -------- ------
     --------------------  --------  -----
    238-95-7766  Carson   Cheryl
     415 548-7723     589 Darwin Ln.
     Berkeley     CA  94705 1    1389
     Algodata Infosystems  Berkeley  CA


   409-56-7008  Bennet   Abraham
     415 658-9932    6223 Bateman St.
     Berkeley     CA  94705 1   1389
     Algodata Infosystems  Berkeley  CA

    (2 rows affected)

 The display shows a total of 2 rows with 13 columns each. Because of the
 length of the rows, each takes up multiple horizontal lines in this display.


 Whenever "*" is used, the columns in the results are displayed in their
 order in the CREATE statement for the table. (See Chapter 7, "Creating
 Databases, Tables, and Indexes," for details.)

 The select list (and the results) of a join need not include columns from
 both of the tables being joined. For example, to find the names of the
 authors that live in the same city as one of the publishers, your query need
 not include any columns from publishers:

   select au_lname, au_fname
    from authors, publishers
    where authors.city = publishers.city

 Remember, just as in any SELECT statement, column names in the select list
 (and table names in the FROM clause) must be separated by commas.


 The FROM Clause

 The FROM clause of a join statement names all the tables (or views) involved
 in the join. This is actually the clause that indicates to SQL Server that a
 join is desired. The order in which the tables (or views) are listed is not
 important.

 More than two tables or views can be named in the FROM clause. Joins
 involving more than two tables or views are discussed later in this chapter.


 As explained in Chapter 2, "Querying Databases," table or view names can be
 qualified by the names of the owner and database, and can be given alias
 names for convenience.

 Views can be joined in exactly the same way as tables and used wherever
 tables are used. Chapter 10, "Creating Views," discusses views; this chapter
 uses only tables in its examples.


 The WHERE Clause

 The WHERE clause specifies the connection between the tables named in the
 FROM clause, restricting the rows to be included in the results. It gives
 the names of the columns to be joined (qualified by table names if
 necessary) and the join operatorÄoften equality, sometimes "greater than" or
 "less than." (For details of WHERE clause syntax, see Chapter 2, "Querying
 Databases," or the SQL Server Language Reference.)

 Joins that match columns on the basis of equality are called equijoins. A
 more precise definition of an equijoin is given later in this chapter, along
 with examples of joins not based on equality.

 Use the following relational operators to determine the basis on which
 columns will be matched:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 Symbol                            Meaning
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 =                                 equal to

 >                                 greater than

 >=                                greater than or equal to

 <                                 less than

 <=                                less than or equal to

 !=                                not equal to

 Symbol                            Meaning
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 !>                                not greater than

 !<                                not less than

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Joins that use the relational operators are collectively called theta joins.
 Another set of join operators is used for outer joins, also discussed in
 detail later in this chapter. The outer join operators are *=, which
 includes in the results all the rows from the first table, not just the ones
 in which the joined columns match, and =*, which includes in the results all
 the rows from the second table, not just the ones in which the joined
 columns match.

 Columns being joined do not need to have the same name, though they often
 will, or be the same datatype. (See Chapter 7, "Creating Databases, Tables,
 and Indexes.")

 However, if the datatypes are not identical, they must be compatibleÄtypes
 that SQL Server automatically converts. For example, SQL Server
 automatically converts among any of the numeric type columnsÄint, smallint,
 tinyint, decimal, or float, and among any of the character type and date
 columnsÄchar, varchar, and datetime. (For details on datatype conversion,
 see Chapter 5, "Using Built-In Functions," or the SQL Server Language
 Reference.)

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
 Tables cannot be joined in text or image columns.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 The WHERE clause of a join statement can include other conditions in
 addition to the one that links columns from different tables. In other
 words, you can include a join operation and a select operation in the same
 SQL statement. An example is given later in this chapter.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 WARNING

 You will get unexpected results if you leave off the WHERE clause of a join.
 Without a WHERE clause, any of the join queries discussed so far will
 produce 69 rows instead of 2! This situation is explained later in the
 section "How Joins Are Processed."
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Joins and the Relational Model

 The join operation is the hallmark of the relational model of database
 management. More than any other feature, the join distinguishes relational
 database management systems from other types of database management systems.


 In structured database management systems (often known as network and
 hierarchical systems), relationships between data values are predefined.
 Once a database has been set up, it's difficult to make queries about
 unanticipated relationships among the data.

 On the other hand, in a relational database management system, relationships
 among data values are left unstated in the definition of a database. They
 become explicit when the data is manipulatedÄwhen you query the database,
 not when you create it. You can ask any question that comes to mind about
 the data stored in the database, regardless of what was intended when the
 database was set up.

 According to the rules of good database design (called normalization rules),
 each table should describe one kind of entityÄa person, place, event, or
 thing. When you want to compare information about two or more kinds of
 entities, you will use the join operation.

 The join operation gives you unlimited flexibility in adding new kinds of
 data to your database. You can always create a new table that contains data
 about a different kind of entity. If the new table has a field with values
 similar to those in some field of an existing table or tables, it can be
 linked to those other tables by joining.


 How Joins Are Processed

 Knowing how joins are processed helps to understand them and to figure out
 why, when you incorrectly state a join, you sometimes get unexpected
 results. This section describes the processing of joins in conceptual terms.
 SQL Server's actual procedure is more sophisticated.

 Conceptually speaking, the first step in processing a join is to form the
 Cartesian product of the tables, all the possible combinations of the rows
 from each of the tables. The number of rows in a Cartesian product of two
 tables is equal to the number of rows in the first table times the number of
 rows in the second table.

 The Cartesian product of the authors table and the publishers table is 69Ä23
 authors multiplied by 3 publishers. You can have a look at a Cartesian
 product with any query that includes columns from more than one table in the
 select list, more than one table in the FROM clause, and no WHERE clause.
 For example, if you leave the WHERE clause off the join used in previous
 examples, SQL Server combines each of the 23 authors with each of the 3
 publishers and returns all 69 rows.

 This Cartesian product does not contain any particularly useful information.
 In fact, it is misleading, since it seems to imply that every author in the
 database has a relationship with every publisher in the databaseÄwhich is
 not true.

 That's why a join must include a WHERE clause, which specifies the columns
 to be matched and the basis on which to match them. (It may also include
 other restrictions.) Once the Cartesian product has been formed, the rows
 that do not satisfy the join are eliminated, using the conditions in the
 WHERE clause.

 The WHERE clause included in the previous example eliminates from the
 results all the rows in which the author's city is not the same as the
 publisher's city.


 Equijoins and Natural Joins

 An equijoin is a join in which the values in the columns being joined are
 compared for equality, and all the columns in the tables being joined are
 included in the results.

 The following query is an example of an equijoin:

   select *
    from authors, publishers
    where authors.city = publishers.city

 In the results of that statement, the city column appears twice. By
 definition, the results of an equijoin contain two identical columns.

 Since there's usually no point in repeating the same information, one of
 these two identical columns can be eliminated by restating the query. The
 result is called a natural join.

 The query that results in the natural join of publishers and authors on the
 city column is

   select publishers.pub_id, publishers.pub_name,
    publishers.state, authors.*
    from publishers, authors
    where publishers.city = authors.city

 The column publishers.city does not appear in the results.


 Joins with Additional Conditions

 The WHERE clause of a join query can include selection criteria as well as
 specifying the join condition. For example, to retrieve the names and
 publishers of all the books for which advances greater than $7500 were paid,
 the statement is

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title, pub_name, advance

         from titles, publishers

         where titles.pub_id = publishers.pub_id

         and advance > $7500

         title

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         pub_name                                                      advance

         -------------------------------

         --------------------                                          -------

         You Can Combat Computer Stress!

         New Age Books                                                 10,125.

         The Gourmet Microwave

         Binnet & Hardley                                              15,000.

         Secrets of Silicon Valley

         Algodata Infosystems                                          8,000.0

         Sushi, Anyone?
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        Sushi, Anyone?

         Binnet & Hardley                                              8,000.0

         (4 rows affected)



 Notice that the columns being joined need not appear in the select list (and
 thus, do not show up in the results).

 As many selection criteria as desired can be included in a join statement.
 The order of the selection criteria and the join condition is not important.



 Joins Not Based on Equality

 You can join values in two columns that are not equal. The following
 comparison operators can be used:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 Symbol                            Meaning
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 >                                 greater than

 >=                                greater than or equal to

 <                                 less than

 <=                                less than or equal to

 !=                                not equal to

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 This example of a greater-than join finds New Age authors who live in states
 that come after New Age Books' state (Massachusetts) in alphabetical order:


   select pub_name, publishers.state,
    au_lname, au_fname, authors.state
    from publishers, authors
    where authors.state > publishers.state
    and pub_name = "New Age Books"

    pub_name   state au_lname  au_fname  state
    --------------  ----- -------------- -----------  -----
    New Age Books  MA  Greene   Morningstar  TN
    New Age Books  MA  Blotchet-Halls Reginald  OR
    New Age Books  MA  del Castillo Innes   MI
    New Age Books  MA  Panteley  Sylvia   MD
    New Age Books  MA  Ringer   Anne   UT
    New Age Books  MA  Ringer   Albert   UT

    (6 rows affected)

 The following example uses a greater-than-or-equal-to join and a less-than
 join to print the royalty columns from the titles and roysched tables. If
 these columns match in this output, it means that the trigger that updates
 the royalty column in titles has been written correctly.

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄŚ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select t.title_id, t.ytd_sales, r.royalty,
         t.royalty

         from titles t, roysched r

         where t.title_id = r.title_id

         and t.ytd_sales >= r.lorange and t.ytd_sales
         < r.hirange

         title_id                                       ytd_sales  royalty  ro

         --------                                       ---------  -------  --

         BU1032                                         4095       10       10

         BU1111                                         3876       10       10
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        BU1111                                         3876       10       10

         BU2075                                         18722      24       24

         BU7832                                         4095       10       10

         MC2222                                         2032       12       12

         MC3021                                         22246      24       24

         PC1035                                         8780       16       16

         PC8888                                         4095       10       10

         PS1372                                         375        10       10

         PS2091                                         2045       12       12

         PS2106                                         111        10       10

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         PS3333                                         4072       10       10

         PS7777                                         3336       10       10

         TC3218                                         375        10       10

         TC4203                                         15096      14       14

         TC7777                                         4095       10       10

         (16 rows affected)




 Self-Joins and Aliases

 You can compare values within a column of a table with a self-join. For
 example, you can use a self-join to find out which authors in Oakland,
 California, live in the same zip code area.

 Since this query involves a join of the authors table with itself, the
 authors table appears in two roles. To distinguish these roles, you can
 temporarily (and arbitrarily) give the authors table two different
 aliasesÄau1 and au2Äin the FROM clause. These aliases are used to qualify
 the column names in the rest of the query. The self-join statement looks
 like this:

   select au1.au_fname, au1.au_lname,
    au2.au_fname, au2.au_lname
    from authors au1, authors au2
    where au1.city = "Oakland" and au2.city = "Oakland"
    and au1.state = "CA" and au2.state = "CA"
    and au1.zip = au2.zip

    au_fname  au_lname  au_fname  au_lname
    --------  --------  --------  --------
    Marjorie  Green   Marjorie  Green
    Dick   Straight  Dick   Straight
    Dick   Straight  Dirk   Stringer
    Dick   Straight  Livia   Karsen
    Dirk   Stringer  Dick   Straight
    Dirk   Stringer  Dirk   Stringer
    Dirk   Stringer  Livia   Karsen
    Stearns   MacFeather  Stearns   MacFeather
    Livia   Karsen   Dick   Straight
    Livia   Karsen   Dirk   Stringer
    Livia   Karsen   Livia   Karsen

    (11 rows affected)

 To eliminate the rows in the results where the authors match themselves and
 to eliminate rows that are identical except that the order of the authors is
 reversed, you can make this addition to the self-join query:

   select au1.au_fname, au1.au_lname,
    au2.au_fname, au2.au_lname
    from authors au1, authors au2
    where au1.city = "Oakland" and au2.city = "Oakland"
    and au1.state = "CA" and au2.state = "CA"
    and au1.zip = au2.zip
    and au1.au_id < au2.au_id


    au_fname au_lname  au_fname  au_lname
    --------- --------  --------  --------
    Dick  Straight  Dirk   Stringer
    Dick  Straight  Livia   Karsen
    Dirk  Stringer  Livia   Karsen

    (3 rows affected)

 It is now clear that Dick Straight, Dirk Stringer, and Livia Karsen all have
 the same zip code.


 The Not-Equal Join

 The not-equal join is rarely used, except by mistake. As a general rule,
 not-equal joins make sense only when used with a self-join. For example, a
 not-equal join and a self-join are used to find the categories in which
 there are 2 or more inexpensive (less than $15) books of different prices:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select distinct t1.type, t1.price

         from titles t1, titles t2

         where t1.price <$15 and t2.price <$15

         and t1.type = t2.type

         and t1.price != t2.price

         type                                                            price

         ----------                                                      -----

         business                                                        2.99

         business                                                        11.95

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         psychology                                                      7.00

         psychology                                                      7.99

         psychology                                                      10.95

         trad_cook                                                       11.95

         trad_cook                                                       14.99

         (7 rows affected)



 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 The expression NOT column_name = column_name is equivalent to column_name !=
 column_name.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 The following example uses a not-equal join combined with a self-join. It
 finds all the rows in the titleauthor table where there are 2 or more rows
 with the same title_id but different au_id numbers (that is, books that have
 more than one author).

   select distinct t1.au_id,t1.title_id
    from titleauthor t1, titleauthor t2
    where t1.title_id = t2.title_id
    and t1.au_id != t2.au_id

    au_id    title_id
    -----------   --------
    213-46-8915   BU1032
    267-41-2394   BU1111
    267-41-2394   TC7777
    409-56-7008   BU1032
    427-17-2319   PC8888
    472-27-2349   TC7777
    672-71-3249   TC7777
    722-51-5454   MC3021
    724-80-9391   BU1111
    724-80-9391   PS1372
    756-30-7391   PS1372
    846-92-7186   PC8888
    899-46-2035   MC3021
    899-46-2035   PS2091
    998-72-3567   PS2091

    (15 rows affected)


 Not-Equal Joins and Subqueries

 Sometimes a not-equal join query is not sufficiently restrictive and needs
 to be replaced by a subquery. For example, suppose you wanted to list the
 names of authors who live in a city where no publisher is located. For the
 sake of clarity, we have further restricted this query to authors whose last
 names begin with "A," "B," or "C." A not-equal join query might be

   select distinct au_lname, authors.city
    from publishers, authors
    where au_lname like "[ABC]%"
    and publishers.city != authors.city

 Here are the results. Notice that they do not answer the question that was
 asked.

   au_lname    city
    --------------   ---------
    Bennet     Berkeley
    Carson     Berkeley
    Blotchet-Halls   Corvallis

    (3 rows affected)

 The system interprets this version of the SQL statement to mean "find the
 names of authors who live in a city where some publisher is not located."
 All the authors that have last names beginning with "A," "B," or "C"
 qualify, including the authors who live in Berkeley (home of the publisher
 Algodata Infosystems).

 In this case, the way that the system processes joins (first finding every
 eligible combination before evaluating other conditions) causes this query
 to fail. The failure is a consequence of relational theory, not the
 implementation. In cases like this, you must use a subquery to get the
 results you want. A subquery can eliminate the ineligible rows first and
 then perform the remaining restrictions.

 Here's the correct statement:

   select distinct au_lname, authors.city
    from publishers, authors
    where au_lname like "[ABC]%"
    and authors.city not in
    (select city from publishers
    where authors.city = publishers.city)

 The following results are what we want:

   au_lname     city
    --------------    ---------
    Blotchet-Halls    Corvallis

    (1 row affected)

 Subqueries are covered in greater detail in Chapter 6, "Building
 Subqueries."


 Joining More Than Two Tables

 The titleauthor table of the pubs database offers a good example of a
 situation in which joining more than two tables is helpful. To find the
 titles of all the books of a particular type and the names of their authors,
 the query is

   select au_lname, au_fname, title
    from authors, titles, titleauthor
    where authors.au_id = titleauthor.au_id
    and titles.title_id = titleauthor.title_id
    and titles.type = "trad_cook"





   au_lname   au_fname
     title
    --------------  ---------
     ---------------------------------------------------------------
    Panteley   Sylvia
     Onions, Leeks, and Garlic: Cooking Secrets of the Mediterranean
    Blotchet-Halls  Reginald
     Fifty Years in Buckingham Palace Kitchens
    O'Leary    Michael
     Sushi, Anyone?
    Gringlesby   Burt
     Sushi, Anyone?
    Yokomoto   Akiko
     Sushi, Anyone?

    (5 rows affected)

 Notice that one of the tables in the FROM clause, titleauthor, does not
 contribute any columns to the results. Nor do any of the columns that are
 joined, au_id and title_id, appear in the results. Nonetheless, this join is
 possible only by using titleauthor as an intermediate table.

 You can also join more than two pairs of columns in the same statement. For
 example, here's how to find the authors who live in the same city and state
 as a publisher:

   select au_fname, au_lname, pub_name
    from authors, publishers
    where authors.city = publishers.city
    and authors.state = publishers.state

    au_fname  au_lname  pub_name
    --------  --------  --------------------
    Cheryl   Carson   Algodata Infosystems
    Abraham   Bennet   Algodata Infosystems

    (2 rows affected)

 When there is more than one join operator in the same statement, either to
 join more than two tables or to join more than two pairs of columns, the
 join expressions are almost always connected with AND, as in earlier
 examples. However, it is also legal to connect them with OR.


 Outer Joins

 In the joins we've discussed so far, only matching rows (rows with values in
 the specified columns that satisfy the join condition) are included in the
 results. In a sense, these join operations eliminate the information
 contained in the rows that do not match.

 Sometimes it is desirable to retain that information by including
 nonmatching rows in the results of a join. On such occasions, the outer join
 is the operation of choice. TRANSACT-SQL is one of a few versions of SQL
 that supports the outer join.

 Consider a join of the authors table and the publishers table on their city
 columns. The results show only the authors that live in cities in which a
 publisher is located.

   select au_fname, au_lname, pub_name
    from authors, publishers
    where authors.city = publishers.city

    au_fname  au_lname  pub_name
    --------  --------  --------------------
    Abraham   Bennet   Algodata Infosystems
    Cheryl   Carson   Algodata Infosystems

    (2 rows affected)

 To include all the authors in the results, regardless of whether a publisher
 is located in the same city, use an outer join. Here's what the query and
 the results of the outer join look like:

   select au_fname, au_lname, pub_name
    from authors, publishers
    where authors.city *= publishers.city

    au_fname  au_lname   pub_name
    -----------  --------------  --------------------
    Johnson   White    NULL
    Marjorie  Green    NULL
    Cheryl   Carson    Algodata Infosystems
    Michael   O'Leary    NULL
    Dick   Straight   NULL
    Meander   Smith    NULL
    Abraham   Bennet    Algodata Infosystems
    Ann    Dull    NULL
    Burt   Gringlesby   NULL
    Chastity  Locksley   NULL
    Morningstar  Greene    NULL
    Reginald  Blotchet-Halls  NULL
    Akiko   Yokomoto   NULL
    Innes   del Castillo  NULL
    Michel   DeFrance   NULL
    Dirk   Stringer   NULL
    Stearns   MacFeather   NULL
    Livia   Karsen    NULL
    Sylvia   Panteley   NULL
    Sheryl   Hunter    NULL
    Heather   McBadden   NULL
    Anne   Ringer    NULL
    Albert   Ringer    NULL

    (23 rows affected)

 The comparison operator "*=" distinguishes the outer join from an ordinary
 join. It tells SQL Server to include all the rows in the authors table in
 the results, whether or not there is a match on the city column in the
 publishers table. Notice that in the results, there is no matching data for
 most of the authors listed, so these rows contain null values in the
 pub_name column.

 The other variety of outer join is specified with the comparison operator
 "=*", which indicates that all the rows in the second table are to be
 included in the results, regardless of whether there is matching data in the
 first table.

 Substituting this operator in the outer join query shown earlier gives this
 result:

   select au_fname, au_lname, pub_name
    from authors, publishers
    where authors.city =* publishers.city

    au_fname  au_lname  pub_name
    --------  --------  ------------------
    NULL   NULL   New Age Books
    NULL   NULL   Binnet & Hardley
    Cheryl   Carson   Algodata Infosystems
    Abraham   Bennet   Algodata Infosystems

    (4 rows affected)

 An outer join can be helpful in other situations. Say you are generating a
 list of authors' names and phone numbers for a salesperson in your
 publishing company to contact. You have just opened offices in Salt Lake
 City and Oakland, and you wish to flag the authors who live in those cities
 so that the salesperson will remember to mention the new office to them.

 The first step is to create a table like this one:

   create table reminder
    (city varchar(20),
    message varchar(25))

 After inserting city and message data in reminder, it looks like this:

   select *
    from reminder

    city     message
    ---------------   ------------------
    Salt Lake City   MENTION NEW OFFICE
    Oakland     MENTION NEW OFFICE

    (2 rows affected)

 Now, an outer join between authors and reminder produces the list you want:


   select au_fname, au_lname, phone, message
    from authors, reminder
    where authors.city *= reminder.city

    au_fname  au_lname  phone   message
    ---------  -------------- ------------ ------------------
    Johnson   White   408 496-7223 NULL
    Marjorie  Green   415 986-7020 MENTION NEW OFFICE
    Cheryl   Carson   415 548-7723 NULL
    Michael   O'Leary   408 286-2428 NULL
    Dick   Straight  415 834-2919 MENTION NEW OFFICE
    Meander   Smith   913 843-0462 NULL
    Abraham   Bennet   415 658-9932 NULL
    Ann    Dull   415 836-7128 NULL
    Burt   Gringlesby  707 938-6445 NULL
    Chastity  Locksley  415 585-4620 NULL
    Morningstar  Greene   615 297-2723 NULL
    Reginald  Blotchet-Halls 503 745-6402 NULL
    Akiko   Yokomoto  415 935-4228 NULL
    Innes   del Castillo 615 996-8275 NULL
    Michel   DeFrance  219 547-9982 NULL
    Dirk   Stringer  415 843-2991 MENTION NEW OFFICE
    Stearns   MacFeather  415 354-7128 MENTION NEW OFFICE
    Livia   Karsen   415 534-9219 MENTION NEW OFFICE
    Sylvia   Panteley  301 946-8853 NULL
    Sheryl   Hunter   415 836-7128 NULL
    Heather   McBadden  707 448-4982 NULL
    Anne   Ringer   801 826-0752 MENTION NEW OFFICE
    Albert   Ringer   801 826-0752 MENTION NEW OFFICE

    (23 rows affected)


 Joins and Null Values

 If there are null values in the columns of the tables being joined, the null
 values will never match each other. This is because null values represent
 unknown values and there is no reason to believe one unknown value will
 match another.


 Help on Joins

 The sp_helpjoins system procedure lists the columns in two tables or views
 that are likely join candidates. The sp_helpjoins system procedure has the
 following syntax:

   sp_helpjoins leftab, righttab

 For example, here's how you'd use sp_helpjoins to find the likely join
 columns between titleauthor and titles:

   sp_helpjoins titleauthor, titles

 The column pairs that sp_helpjoins displays come from two sources. First,
 sp_helpjoins checks the syskeys table in the current database to see if any
 common keys have been defined on the two tables with sp_commonkey. If it
 doesn't find any common keys there, the procedure applies less restrictive
 criteria to come up with any keys that can be reasonably joined; it checks
 for keys with the same user datatypes and if that fails, for columns with
 the same name and datatype.

 For complete information on system procedures, see the SQL Server Language
 Reference.






 Chapter 4  Displaying Totals: GROUP BY and COMPUTE
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 This chapter describes aggregate functions and the GROUP BY clause, and row
 aggregate functions and the COMPUTE clause.

 Aggregate functionsÄSUM, AVG, COUNT, COUNT(*), MAX, and MINÄgenerate summary
 values that appear as new columns in the query results. The GROUP BY clause,
 part of the SELECT statement, divides a table into groups. TRANSACT-SQL has
 eliminated some of the restrictions that apply to the GROUP BY clause in
 other versions of SQL. GROUP BY and aggregate functions are often used
 together, in which case a summary value is calculated for each group.

 Row aggregate functionsÄSUM, AVG, COUNT, MAX, and MINÄand the COMPUTE clause
 are always used together as part of a SELECT statement. Row aggregate
 functions generate summary values that appear as additional rows in the
 query results. An SQL statement that includes a COMPUTE clause and row
 aggregate functions produces a report with detail and summary rows.


 Aggregate Functions and the GROUP BY Clause

 Aggregate functions calculate summary values from the data in a particular
 column. Aggregate functions can be applied to all the rows in a table, to a
 subset of the table specified by a WHERE clause, or to one or more groups of
 rows in the table. From each set of rows to which an aggregate function is
 applied, a single value is generated.

 This example calculates the sum of year-to-date sales for all books in the
 titles table:

 select sum(ytd_sales)

 from titles



 ------------

 97446



 (1 row affected)

 Notice that to use an aggregate function, you give the function name
 followed by an expression on whose values the aggregate function will
 operate. Enclose the expression in parentheses.

 Aggregate functions have the following syntax:

   aggregate_function ([DISTINCT] expression)

 The aggregate operators are SUM, AVG, MAX, MIN, COUNT, and COUNT(*). The
 optional keyword DISTINCT can be used with SUM, AVG, and COUNT to eliminate
 duplicate values before aggregate function is applied. DISTINCT is not
 allowed with MAX, MIN, or COUNT(*).


 The expression to which the syntax statement refers is usually a column
 name. It can also be a constant, a function, or any combination of column
 names, constants, and functions connected by arithmetic or bitwise
 operators. An expression can also be a subquery.

 For example, with this query you can find the average price of all books if
 the prices were doubled:

 select avg(price * 2)

 from titles



 ------------

  29.53



 (1 row affected)

 The syntax of the aggregate functions and their results are as follows:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Aggregate Function                Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 SUM([DISTINCT] expression)        Total of the [distinct] values in the
                                   numeric column

 AVG([DISTINCT] expression)        Average of the [distinct] values in the
                                   numeric column

 COUNT([DISTINCT] expression)      Number of [distinct] non-null values in
                                   the column

 COUNT(*)                          Number of selected rows

 MAX(expression)                   Highest value in the expression
 Aggregate Function                Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
MAX(expression)                   Highest value in the expression

 MIN(expression)                   Lowest value in the expression

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Aggregate functions can be used in a select list, as in the previous
 examples, or in the HAVING clause of a SELECT statement that includes a
 GROUP BY clause.

 Aggregate functions cannot be used in a WHERE clause. However, a SELECT
 statement with aggregate functions in its select list often includes a WHERE
 clause that restricts the rows to which the aggregate function is applied.
 In the examples given earlier, each aggregate function produced a single
 summary value for the whole table. If a SELECT statement includes a WHERE
 clause (but not a GROUP BY clause), an aggregate function produces a single
 value for the subset of rows that the WHERE clause specifies.

 This query returns the average advance and the sum of year-to-date sales for
 business books only:

 select avg(advance),, sum(ytd_sales)

 from titles

 where type = "business"



 --------------

 6281.2530788



 (1 row affected)

 Whenever an aggregate function is used in a SELECT statement that does not
 include a GROUP BY clause, it produces a single value. This is true whether
 it is operating on all the rows in a table or on a subset of rows defined by
 a WHERE clause. It is called a scalar aggregate.

 Note that you can use more than one aggregate function in the same select
 list and produce more than one scalar aggregate in a single SELECT
 statement.

 When you sum or average integer data, TRANSACT-SQL treats the result as an
 int value, even if the datatype of the column is smallint or tinyint. To
 avoid overflow errors in DB-LIBRARY programs, declare all variables for
 results of averages or sums as type int.


 Aggregate Functions and Datatypes

 SUM and AVG can be used with numeric columns onlyÄint, smallint, tinyint,
 float, and money. MIN and MAX cannot be used with bit datatypes. Aggregate
 functions cannot be used with text and image datatypes.

 With these exceptions the other aggregate functions can be used with any
 type of column. For example, you can use MIN (minimum) to find the lowest
 value, the one closest to the beginning of the alphabet, in a character type
 column:

   select min(au_lname)
    from authors

    --------------------
    Bennet

    (1 row affected)


 COUNT(*)

 COUNT(*) does not require an expression as a parameter because, by
 definition it does not use information about any particular column. It is
 used to find the total number of rows in a table. This statement finds the
 total number of books:

 select count(*)

 from titles



 ------------

 18



 (1 row affected)

 COUNT(*) returns the number of rows in the specified table without
 eliminating duplicates. It counts each row separately, including rows that
 contain null values.

 Like other aggregate functions, COUNT(*) can be combined with other
 aggregate functions in the select list, with WHERE clauses, and so on. This
 statement shows COUNT(*) combined with a WHERE clause:

 select count(*), avg(price)

 from titles

 where advance > 1000



 -------------------

 1514.42



 (1 row affected)


 The DISTINCT Keyword

 The DISTINCT keyword is optional with SUM, AVG, and COUNT. It is not allowed
 with MIN, MAX, or COUNT(*). When DISTINCT is used, duplicate values are
 eliminated before the sum, average, or count is calculated.

 If you use DISTINCT, the parameter cannot include an arithmetic expression.
 It must consist of a column name only.

 When DISTINCT is used, it appears inside the parentheses and before the
 column name. The following statement returns the average of the distinct
 prices of business books:

 select avg(distinct price)

 from titles

 where type = "business"



 ------------

 11.64



 (1 row affected)

 Without DISTINCT, the AVG function finds the average price of all business
 titles in the titles table:

 select avg(price)

 from titles

 where type = "business"



 ------------

 13.73



 (1 row affected)

 To find the number of different cities the authors live in, type

 select count(distinct city)

 from authors



 ------------

 16



 (1 row affected)


 Null Values and Aggregate Functions

 If there are any null values in the column on which the aggregate function
 is operating, they are ignored for the purposes of the function.

 For example, if you ask for the count of advances in the titles table, your
 answer is not the same as if you ask for the count of title names because of
 the null values in the advance column:

 select count(advance)

 from titles



 ------------

 16



 (1 row affected)



 select count(title)

 from titles



 -----------

 18



 (1 row affected)

 The exception to this rule is COUNT(*), which counts each row, even if every
 field in it is NULL.

 If no rows meet the condition(s) specified in the WHERE clause, COUNT
 returns a value of zero. The other functions all return NULL. Here are
 examples:

 select count(distinct title)

 from titles

 where type = "poetry"



 ------------

 0



 (1 row affected)





 select avg(advance)

 from titles

 where type = "poetry"



 ------------

 NULL



 (1 row affected)


 The GROUP BY Clause

 The GROUP BY clause is used in SELECT statements to divide a table into
 groups. You can group by a column name or by the results of computed columns
 when using numeric datatypes. You cannot group by columns of bit, text, or
 image datatypes. A GROUP BY clause almost always appears in statements that
 also include aggregate functions, in which case the aggregate function
 produces a value for each group. These values are called vector aggregates.
 (A scalar aggregate is a single value produced by an aggregate function
 without a GROUP BY clause.)

 This statement finds the average advance and sum of year-to-date sales for
 each type of book:

 select type, avg(advance), sum(ytd_sales)

 from titles

 group by type



 type

 -------------------------

 UNDECIDEDNULLNULL

 business6,281.2530788

 mod_cook7,500.0024278

 popular_comp7,500.0012875

 psychology4,255.009939

 trad_cook6,333.3319566



 (6 rows affected)

 The summary values produced by SELECT statements with GROUP BY and aggregate
 functions appear as new columns in the results.


 GROUP BY Syntax

 The GROUP BY clause has the following syntax:

   [GROUP BY [ALL]
    aggregate_free_expression
    [, aggregate_free_expression...]
    [HAVING search_conditions]

 The SQL Server Language Reference lists the complete syntax of the SELECT
 statement. Remember that the order of the clauses in the SELECT statement is
 significant. You are free to omit any of the optional clauses, but when you
 use them, they must appear in the order shown in the SQL Server Language
 Reference.

 There are no restrictions on what you can include in the select list of a
 SELECT statement that includes GROUP BY. The columns in the select list are
 not limited to the grouping columns or columns used with the aggregate
 functions. For example, the inclusion of the title_id column in the select
 list is allowed:

 select type, title_id, avg(price), avg(advance)

 from titles

 group by type



 typetitle_id

 -------------------------  --------

 businessBU103213.73  6,281.25

 businessBU111113.73  6,281.25

 businessBU207513.73  6,281.25

 businessBU783213.73  6,281.25

 mod_cookMC222211.49  7,500.00

 mod_cookMC302111.49  7,500.00

 UNDECIDEDMC3026NULL   NULL

 popular_compPC103521.48  7,500.00

 popular_compPC888821.48  7,500.00

 popular_compPC999921.48  7,500.00

 psychologyPS137213.50  4,255.00

 psychologyPS209113.50  4,255.00

 psychologyPS210613.50  4,255.00

 psychologyPS333313.50  4,255.00

 psychologyPS777713.50  4,255.00

 trad_cookTC321815.96  6,333.33

 trad_cookTC420315.96  6,333.33

 trad_cookTC777715.96  6,333.33



 (18 rows affected)

 You can group by an expression as long as it does not include aggregate
 functions. For example:

 select avg(ytd_sales), ytd_sales*royalty

 from titles

 group by ytd_sales * royalty



 --------------------

 1111110

 3753750

 203224384

 204524540

 333633360

 387638760

 407240720

 409540950

 8780140480

 15096211344

 18722449328

 22246533904



 (12 rows affected)

 However, you cannot group by an alias. This statement produces an error
 message:

   select Category = type, title_id, avg(price), avg(advance)
    from titles
    group by Category

 You can list more than one column in the GROUP BY clause to nest groupsÄthat
 is, you can group a table by any combination of columns. For example, here's
 the statement that finds the average price and the sum of the year-to-date
 sales, grouped by type and within type by publisher identification number:

 select type, pub_id, avg(price), sum(ytd_sales)

 from titles

 group by type, pub_id



 typepub_id

 -----------------------  -----

 UNDECIDED0877NULL   NULL

 business07362.99   18722

 business138917.31  12066

 mod_cook087711.49  24278

 popular_comp138921.48  12875

 psychology073611.48  9564

 psychology087721.59  375

 trad_cook087715.96  19566



 (8 rows affected)

 Another kind of nesting, nesting a vector aggregate inside a scalar
 aggregate, is a TRANSACT-SQL extension. For example, to find the average
 price of all the types of books, type

 select avg(price)

 from titles

 group by type



 ------------

 NULL

 13.73

 11.49

 21.48

 13.50

 15.96



 (6 rows affected)

 You can find the highest average price of all the types of books in a single
 query:

 select max(avg(price))

 from titles

 group by type



 ------------

 21.48



 (1 row affected)

 By definition, the GROUP BY clause applies to the vector aggregateÄin this
 case, AVG.


 GROUP BY without Aggregate Functions

 If GROUP BY is used without an aggregate function, it rearranges the table
 in the FROM clause into groups without producing any summary values. Each of
 the rows that make up a group contains the same value in the GROUP BY
 column. (The actual data in the database is not affected, of course.)

 Here's an example:

 select type, title_id, price

 from titles

 group by type



 typetitle_idprice

 -------------------------

 businessBU103219.99

 businessBU111111.95

 businessBU20752.99

 businessBU783219.99

 mod_cookMC222219.99

 mod_cookMC30212.99

 UNDECIDEDMC3026NULL

 popular_compPC103522.95

 popular_compPC888820.00

 popular_compPC9999NULL

 psychologyPS137221.59

 psychologyPS209110.95

 psychologyPS21067.00

 psychologyPS333319.99

 psychologyPS77777.99

 trad_cookTC321820.95

 trad_cookTC420311.95

 trad_cookTC777714.99



 (18 rows affected)


 GROUP BY and Null Values

 If the grouping column contains a null value, that row becomes a group in
 the results. If the grouping column contains more than one null value, the
 null values are put into a single group.

 The royalty column in the titles table contains some null values. Here's an
 example that uses GROUP BY and the royalty column:

 select royalty, avg(price * 2)

 from titles

 group by royalty



 royalty

 -------------

 NULLNULL

 1032.89

 1230.94

 1423.90

 1645.90

 245.98



 (6 rows affected)


 GROUP BY and the WHERE Clause

 You can use a WHERE clause in a statement with GROUP BY. Rows that don't
 satisfy the conditions in the WHERE clause are eliminated before any
 grouping is done. Here's an example:

 select type, avg(price)

 from titles

 where advance > 5000

 group by type



 type

 -----------------

 business2.99

 mod_cook2.99

 popular_comp21.48

 psychology14.30

 trad_cook17.97



 (5 rows affected)

 Only the rows with advances greater than $5000 are included in the groups
 shown in the query results.


 GROUP BY and ALL

 The ALL keyword in the GROUP BY clause is a TRANSACT-SQL enhancement to SQL.
 It is meaningful only if the SELECT statement in which it is used also
 includes a WHERE clause.

 If you use ALL, the query results will include all the groups produced by
 the GROUP BY clause, even if some of the groups don't have any rows that
 meet the search conditions. Without ALL, a SELECT statement that includes
 GROUP BY will not show groups for which no rows qualify.

 Here's an example:

 select type, avg(price)

 from titles

 where royalty = 10

 group by type



 type

 -----------------

 business17.31

 popular_comp20.00

 psychology14.14

 trad_cook17.97



 (4 rows affected)



 select type, avg(price)

 from titles

 where royalty = 10

 group by all type



 type

 --------------

 UNDECIDEDNULL

 business17.31

 mod_cookNULL

 popular_comp20.00

 psychology14.14

 trad_cook17.97



 (6 rows affected)

 The first statement produces groups only for those books that commanded
 royalties of 10%. Since no modern cookbooks have a royalty of 10%, there is
 no group in the results for the mod_cook type.

 The second statement produces groups for all types, including modern cooking
 and "UNDECIDED," even though the modern cooking group doesn't include any
 rows that meet the qualification specified in the WHERE clause.

 The column that holds the aggregate value (the average price) is NULL for
 groups that lack qualifying rows.


 GROUP BY and ORDER BY

 Use an ORDER BY clause when you want the results to be ordered in a
 particular way.

 Put the ORDER BY clause after the GROUP BY clause. For example, to find the
 average price of each type of book and order the results by average price,
 type

 select type, avg(price)

 from titles

 group by type

 order by avg(price)



 type

 ------------------

 UNDECIDEDNULL

 mod_cook11.49

 psychology13.50

 business13.73

 trad_cook15.96

 popular_comp21.48



 (6 rows affected)


 The HAVING Clause

 HAVING sets conditions for the GROUP BY clause. It is similar to WHERE, but
 HAVING places conditions on groups, whereas WHERE places conditions on rows.
 HAVING should only be used with GROUP BY.

 HAVING search conditions are identical to WHERE search conditions with one
 exception: WHERE search conditions cannot include aggregate functions, while
 HAVING search conditions often do. HAVING clauses can reference any of the
 items that appear in the select list. There is no limit on the number of
 conditions that can be included in a HAVING clause.

 The following statement is an example of a HAVING clause with an aggregate
 function. It groups the rows in the titles table by type but eliminates the
 groups that include only one book:

   select type
    from titles
    group by type
    having count(*) > 1

    type
    ------------
    business
    mod_cook
    popular_comp
    psychology
    trad_cook

    (5 rows affected)

 Here's an example of a HAVING clause without aggregate functions. It groups
 the titles table by type and eliminates those types that do not start with
 the letter "p":

   select type
    from titles
    group by type
    having type like 'p%'

    type
    ------------
    popular_comp
    psychology

    (2 rows affected)

 When more than one condition is included in the HAVING clause, they are
 combined with AND, OR, or NOT. For example, say you want to group the titles
 table by publishers, including only those groups of publishers with
 identification numbers greater than 0800. These publishers have paid more
 than $15,000 in total advances and sell books for less than $20 on the
 average. Type

 select pub_id, sum(advance), avg(price)

 from titles

 group by pub_id

 having sum(advance) > 15000

 and avg(price) < 20

 and pub_id > "0800"



 pub_id

 --------------------

 087741,000.0015.41

 138930,000.0018.98



 (2 rows affected)

 The following statement illustrates the use of GROUP BY, HAVING, WHERE and
 ORDER BY clauses in one SELECT statement. It produces the same groups and
 summary values as the previous example but does so after eliminating the
 titles with prices under $5. It also organizes the results by pub_id:

 select pub_id, sum(advance), avg(price)

 from titles

 where price >=5

 group by pub_id

 having sum(advance) > 15000

 and avg(price) < 20

 and pub_id > "0800"

 order by pub_id



 pub_id

 ---------------------

 087726,000.0017.89

 138930,000.0018.98



 (2 rows affected)


 The COMPUTE Clause

 The COMPUTE clause is used in SELECT statements with row aggregate
 functionsÄSUM, AVG, MIN, MAX, and COUNTÄto generate summary values based on
 the values in groups of rows.

 These summary values appear as new rows in the query results, allowing you
 to see both detail and summary rows in one set of results. (Compare this to
 results from aggregate functions and the GROUP BY clause, which appear as
 new columns.)

 The results of a query that includes a COMPUTE clause are like a
 control-break report, which is a report whose summary values are controlled
 by the groupings ("breaks") that you specify. You can produce summary values
 for groups, and you can calculate more than one aggregate function for the
 same group.

 Here's how to use the COMPUTE clause to calculate the sum for the prices of
 the different types of cookbooks:

 select type, price

 from titles

 where type like "%cook"

 order by type, price

 compute sum(price) by type



 typeprice

 --------------------

 mod_cook2.99

 mod_cook19.99

 sum



 ------------

 22.98



 typeprice

 ---------------------

 trad_cook11.95

 trad_cook14.99

 trad_cook20.95

 sum

 ------------

 47.89



 (7 rows affected)

 Notice that the summary values appear as new rows in the results, labeled
 with the word "sum," and that all the detail rows are also included.

 The same summary information could be produced with this statement:

 select type, sum(price)

 from titles

 where type like "%cook"

 group by type



 type

 ---------------

 mod_cook22.98

 trad_cook47.89



 (2 rows affected)

 With GROUP BY, detail rows are not shown, and the summary values are
 displayed as new columns.


 COMPUTE Syntax

 The COMPUTE clause has the following syntax:

   [COMPUTE row_aggregate(column_name)
    [, row_aggregate(column_name)...]
    [BY column_name [, column_name...]]]

 See the SQL Server Language Reference for the complete syntax of the SELECT
 statement.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
 You cannot include text or image datatypes in a COMPUTE or COMPUTE BY
 clause.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Row Aggregate Functions

 The row aggregate functions used with COMPUTE are as follows:


ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Row Aggregate Function            Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 SUM                               Total of the values in the numeric column

 AVG                               Average of the values in the numeric
                                   column

 MAX                               Highest value in the expression

 MIN                               Lowest value in the expression

 COUNT                             Number of selected rows

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 These row aggregate functions are the same aggregate functions that can be
 used with GROUP BY except that there is no row aggregate function equivalent
 to COUNT(*). To find the summary information produced by GROUP BY and
 COUNT(*), use a COMPUTE clause without BY.


 Rules for COMPUTE Clauses


   ž   The DISTINCT keyword is not allowed with row aggregate functions.

   ž   The columns in a COMPUTE clause must appear in the statement's select
       list.

   ž   You cannot use SELECT INTO in the same statement as a COMPUTE clause
       because statements that include COMPUTE do not generate normal rows.

   ž   If you use COMPUTE BY, you must also use an ORDER BY clause. The
       columns listed after COMPUTE BY must be identical to or a subset of
       those listed after ORDER BY, and must be in the same left-to-right
       order, start with the same expression, and not skip any expressions.

       For example, if the ORDER BY clause is

       order by a, b, c

       The COMPUTE BY clause can be any of these:

       compute row_aggregate (column_name) by a, b, c
        compute row_aggregate (column_name) by a, b
        compute row_aggregate (column_name) by a

       The COMPUTE BY clause cannot be any of these:

       compute row_aggregate (column_name) by b, c
        compute row_aggregate (column_name) by a, c
        compute row_aggregate (column_name) by c

   ž   You must use a column name or an expression in the ORDER BY clause;
       you cannot sort by a column heading.

   ž   The COMPUTE keyword can be used without BY to generate grand totals,
       grand counts, and so on. ORDER BY is optional if you use the COMPUTE
       keyword without BY. COMPUTE without BY is discussed later.



 More Than One Column after COMPUTE BY

 Listing more than one column after the keyword BY breaks a group into
 subgroups and applies the specified row aggregate function at each level of
 grouping. For example, the following query finds the sum of the prices of
 psychology books from each publisher:

 select type, pub_id, price

 from titles

 where type = "psychology"

 order by type, pub_id, price

 compute sum(price) by type, pub_id



 typepub_idprice

 ----------------------------

 psychology07367.00

 psychology07367.99

 psychology073610.95

 psychology073619.99

 sum

 ------------

 45.93



 typepub_idprice

 ----------------------------

 psychology087721.59

 sum

 ------------

 21.59



 (7 rows affected)


 Using More Than One COMPUTE Clause

 You can use different aggregate functions in the same statement by including
 more than one COMPUTE BY clause. Here's a query similar to the preceding
 one. It finds the sum of the prices of all psychology books, as well as the
 sum of the prices of psychology books by publisher:

 select type, pub_id, price

 from titles

 where type = "psychology"

 order by type, pub_id, price

 compute sum(price) by type, pub_id

 compute sum(price) by type



 typepub_idprice

 --------------------------

 psychology07367.00

 psychology07367.99

 psychology073610.95

 psychology073619.99

 sum

 ----------

 45.93



 typepub_idprice

 ---------------- ---------

 psychology087721.59



 sum

 ---------

 21.59

 sum

 ---------

 67.52



 (8 rows affected)


 Applying an Aggregate to More Than One Column

 One COMPUTE BY clause can apply the same aggregate function to several
 columns. This query finds the sum of the prices and advances for each type
 of cookbook:

 select type, price, advance

 from titles

 where type like "%cook"

 order by type

 compute sum(price), sum(advance) by type



 typepriceadvance

 --------------------------------

 mod_cook2.9915,000.00

 , mod_cook, 19.99, 0.00,

 sumsum

 ------------------------

 22.9815,000.00



 typepriceadvance

 ---------------------------------

 trad_cook11.954,000.00

 trad_cook14.998,000.00

 trad_cook20.957,000.00

 sumsum

 ------------------------

 47.8919,000.00



 (7 rows affected)

 Remember, the columns to which the aggregate functions apply must also be in
 the select list.


 Using Different Aggregate Functions in the Same COMPUTE BY Clause

 You can use different aggregate functions in the same COMPUTE BY clause:

 select type, pub_id, price

 from titles

 where type like "%cook"

 order by type, pub_id

 compute sum(price), max(pub_id) by type



 typepub_idprice

 --------------------------

 mod_cook08772.99

 mod_cook087719.99

 sum

 ------------

 22.98



 max

 -----

 0877



 typepub_idprice

 ---------------------------

 trad_cook087711.95

 trad_cook087714.99

 trad_cook087720.95

 sum

 ------------

 47.89

 max

 ----

 0877



 (7 rows affected)


 Grand Values: COMPUTE without BY

 The COMPUTE keyword can be used without BY to generate grand totals, grand
 counts, and so on.

 This statement finds the grand total of the prices and advances for all
 types of books over $20:

 select type, price,, advance

 from titles

 where price > $20

 compute sum(price), sum(advance)



 typepriceadvance

 ------------------------------------

 popular_comp22.957,000.00

 psychology21.597,000.00

 trad_cook20.957,000.00

 sumsum

 ------------------------

 65.4921,000.00



 (4 rows affected)

 You can use COMPUTE BY and COMPUTE without BY in the same query. The
 following query finds the sum of prices and advances by type, and then
 computes the grand total of prices and advances for all types of books:

 select type, price, advance

 from titles

 where type like "%cook"

 order by type

 compute sum(price), sum(advance) by type

 compute sum(price), sum(advance)



 typepriceadvance

 --------------------------------

 mod_cook2.9915,000.00

 mod_cook19.990.00

 sumsum

 ------------------------

 22.9815,000.00



 typepriceadvance

 ---------------------------------

 trad_cook11.954,000.00

 trad_cook14.998,000.00

 trad_cook20.957,000.00

 sumsum

 ------------------------

 47.8919,000.00

 sumsum

 ------------------------

 70.8734,000.00



 (8 rows affected)






 Chapter 5  Using Built-In Functions
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 SQL Server provides a variety of built-in functions. They can be divided
 into the following categories:


   ž   System functions, most of which return information from the system
       tables

   ž   String functions for manipulating char, varchar, binary, and varbinary
       values

   ž   Text functions for manipulating text values

   ž   Mathematical functions for trigonometry, geometry, and other number
       handling

   ž   Date functions for manipulating datetime values

   ž   Conversion function (CONVERT) for converting expressions from one
       datatype to another and for formatting dates in a wide variety of
       styles



 System Functions

 System functions return special information from the database. Many of them
 provide a shorthand way of querying the system tables.

 System functions have the following syntax:

   select function_name(parameter[s])

 The system functions can be used in the select list, in the WHERE clause,
 and anywhere an expression is allowed.

 For example, to find the user identification number of your coworker who
 logs in as harold, type

   select user_id("harold")

 Assuming that harold's user ID is 13, the result is

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   ------------

                                   13

                                   (1 row affected)



 Generally, as in this example, the name of the function tells you what kind
 of information will be returned.

 The USER_NAME system function takes an ID number as its parameter and
 returns the user's name:

   select username(13)

    ---------
    harold

    (1 row affected)

 To find the name of the current user (that is, your name), the parameter is
 omitted:

   select username()

    ------
    dbo

    (1 row affected)

 Note that the System Administrator's server user ID is always 1 and that a
 guest user is always given the server user ID - 1. Inside a database, the
 USER_NAME of the Database Owner is always dbo, with a user ID of 1. Inside a
 database, the guest user's ID (uid) is always 2. The System Administrator is
 treated as the Database Owner inside a user database. Built-in functions are
 always used with parentheses even if the parameter is NULL.

 This list gives the name of each system function, the parameter it takes,
 and the result it returns:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Function     Parameters                       Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 COL_NAME     (object ID #, column ID #)       Returns the column name

 COL_LENGTH   ("object name", "column name")   Returns the column length

 DB_ID        (["database name"])              Returns the database ID #

 DB_NAME      ([database ID #])                Returns the database name

 HOST_ID      ( )                              Returns the host process ID #

 HOST_NAME    ( )                              Returns the current host
                                               computer name

 INDEX_COL    ("object name", index ID #, key  Returns the indexed column name
              #)

 ISNULL       (expression, value)              Returns the substitutes for the
 Function     Parameters                       Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
ISNULL       (expression, value)              Returns the substitutes for the
                                               specified value for NULL
                                               entries

 OBJECT_ID    ("database object name")         Returns the database object ID
                                               #

 OBJECT_NAME  (database object ID #)           Returns the database object
                                               name

 SUSER_ID     (["server user name"])           Returns the server user's ID #

 SUSER_NAME   ([server user ID #])             Returns the server user's name

 USER_ID      (["user name"])                  Returns the user's ID number

 USER_NAME    ([user ID number])               Returns the user's name

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Function     Parameters                       Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 When the parameter to a system function is optional, the current database,
 host computer, server user, or database user is assumed.




 System Function Examples


   1.  This query finds the length of the title column in the titles table:
ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   select x = col_length("titles", "title")

                                   x
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                  x

                                   ------------

                                   80

                                   (1 row affected)



 The "x =" in the preceding statement is included to give some column heading
 to the result.

   2.  This query finds the average of the prices of all titles, substituting
       the value "$10.00" for all NULL entries in price:

       select avg(isnull(price,$10.00))
        from titles


   3.  This query finds all rows in sysusers where the name is equal to the
       result of applying the system function USER_NAME to user ID 1:

       select name
        from sysusers
        where name = username(1)

        name
        ------------
        dbo

        (1 row affected)




 String Functions

 String functions are used for various operations on binary data, character
 strings, or expressions.

 String functions return values commonly needed for operations on character
 data. String function names are not keywords.

 String functions have the following syntax:

   select function_name(parameters)

 You can concatenate binary or character expressions like this:

   (expression + expression [+ expression]...)

 The string functions and concatenation can be used on char, varchar, binary,
 and varbinary datatypes only. In addition, the REPLICATE and RIGHT string
 functions can be used on datetime datatypes.

 String functions can be nested, and they can be used anywhere an expression
 is allowed. When you use constants with a string function, enclose them in
 single or double quotation marks.

 Function names, parameters, and results are listed in the following table:



ÖŚÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Function    Parameters                        Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ASCII       (char_expr)                       The ASCII code value of the
                                               leftmost character in a
                                               character expression.

 CHAR        (integer_expr)                    A character converted from an
                                               ASCII code. The ASCII code
                                               should be between 0 and 255;
                                               otherwise, NULL is returned.

 CHARINDEX   ("Pattern", expression)           The starting position of the
                                               specified pattern. The first
                                               parameter is the pattern. The
                                               second parameter is an
                                               expression, usually a column
                                               name, in which SQL Server
                                               searches for the pattern. Not
 Function    Parameters                        Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                              searches for the pattern. Not
                                               implemented for text and image
                                               data.

 DATALENGTH  (char_expr)                       The length of any type of data
                                               value. A NULL string returns a
                                               length of 1.

 DIFFERENCE  (char_expr1, char_expr2)          The difference between the
                                               values of two character
                                               expressions as returned by the
                                               SOUNDEX function.

 LOWER       (char_expr)                       Lowercase converted from
                                               uppercase.

 LTRIM       (char_expr)                       Data without leading blanks.

 REPLICATE   (char_expr, integer_expr)         A character expression repeated
 Function    Parameters                        Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
REPLICATE   (char_expr, integer_expr)         A character expression repeated
                                               a specified number of times. If
                                               integer_expr is negative, a NUL
                                               string is returned.

 RIGHT       (char_expr, integer_expr)         Part of a character string
                                               starting integer_expr character
                                               from the right. If integer_expr
                                               is negative, a NULL string is
                                               returned.

 RTRIM       (char_expr)                       Data without trailing blanks.

 SOUNDEX     (char_expr)                       A 4-digit (SOUNDEX) code for us
                                               in evaluating the similarity of
                                               two strings.

 SPACE       (integer_expr)                    A string of repeated spaces. Th
                                               number of spaces is equal to
 Function    Parameters                        Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                              number of spaces is equal to
                                               integer_expr. If integer_expr i
                                               negative, a NULL string is
                                               returned.

 STR         (float_expr                       Character data converted from
             [, length [, decimal]])            numeric data. The length is th
                                               total length, including decimal
                                               point, sign, digits, and spaces
                                               The decimal value is the number
                                               of spaces to the right of the
                                               decimal.

 STUFF       (char_expr1, start, length,       Data with length characters
             char_expr2)                       deleted from char_expr1 at star
                                               and char_expr2 inserted into
                                               char_expr1 at start. Not
                                               implemented for text data.

 Function    Parameters                        Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 SUBSTRING   (expression, start, length)       Part of a character or binary
                                               string. The first parameter can
                                               be a character or binary string
                                               a column name, or an expression
                                               that includes a column name. (D
                                               not use expressions that includ
                                               functions or subqueries.) The
                                               second parameter specifies the
                                               position at which the substring
                                               begins. The third parameter
                                               specifies the number of
                                               characters in the substring. No
                                               implemented for text data.

 UPPER       (char_expr)                       Uppercase converted from
                                               lowercase.

 ++                                            The + option indicates two or
 Function    Parameters                        Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
++                                            The + option indicates two or
                                               more concatenated character
                                               strings, binary strings, column
                                               names, or a combination of them
                                               Enclose character strings in
                                               single or double quotation
                                               marks.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 The following sections provide examples using string functions.


 SUBSTRING

 This example illustrates the SUBSTRING function. It displays the last name
 and first initial of each author, for example, Bennet A.

   select au_lname, substring(au_fname, 1, 1)
    from authors

 The SUBSTRING function does what its name impliesÄit returns a portion of a
 character or binary string.

 The SUBSTRING function always takes three parameters. The first can be a
 character or binary string, a column name, or a string-valued expression
 that includes a column name. The second parameter specifies the position at
 which the substring should begin. The third specifies the length (in number
 of characters) of the string to be returned.

 The SUBSTRING function has the following syntax:

   substring(expression, start, length)

 For example, here's how to take the second, third, and fourth characters of
 the string constant "abcdef":

   select x = substring("abcdef", 2, 3)

    x
    ----------
    bcd


 CHARINDEX

 The CHARINDEX function returns the starting position of a pattern you
 specify. It takes two parameters. The first is the pattern whose position
 you want. The pattern is a literal string that does not contain wildcard
 characters. The second parameter is a string-valued expression, usually a
 column name, in which SQL Server searches for the specified pattern.

 The CHARINDEX function has the following syntax:

   charindex("pattern", expression)

 To find the position at which the pattern "wonderful" begins in a certain
 row of the notes column in the titles table, type

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   select charindex("wonderful", notes)

                                   from titles

                                   where title_id = "TC3218"

                                   ------------

                                   46

                                   (1 row affected)



 If you do not restrict the rows to be searched, the query returns all rows
 in the table and reports non-zero values for those rows in which the pattern
 was found.


 STR

 The STR function converts numbers to characters, with optional parameters
 for specifying the length of the integral (the part of the number before the
 decimal point) and the number of places after the decimal point.

 Length and decimal parameters to STR (if supplied) should be positive. The
 default length is 10. The number is rounded to an integer by default or if
 the decimal parameter is 0. The specified length should be at least equal to
 or greater than the part of the number before the decimal point plus the
 number's sign (if any). If the expression exceeds the specified length, the
 string returns "**" of the specified length. For example:

   select str(123.45, 2, 2)

    --
    **

    (1 row affected)

 A short expression is right justified in the specified length, and a long
 expression is truncated to the specified number of decimal places.


 STUFF

 The STUFF function inserts a string into another string. It deletes a
 specified length of characters in the first string at the start position. It
 then inserts the second string into the first string at the start position.
 If the start position or the length is negative, a NULL string is returned.


 If the start position is longer than the first string, a NULL string is
 returned. If the length to delete is longer than the first string, it is
 deleted to the first character in the first string. For example:

   select stuff("abc", 2, 3, "xyz")

    ----
    axyz

    (1 row affected)


 Testing Similarity of Patterns: SOUNDEX and DIFFERENCE

 The SOUNDEX function converts a character string to a 4-digit code for use
 in a comparison. Nonalphabetic characters are used to terminate the
 compression. This function always returns some value.

 The DIFFERENCE function compares the SOUNDEX values of two strings and
 evaluates the similarity between them, returning a value from 0 to 4. Four
 is the best match. For example:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   select difference("abc", "abc")

                                   ------------

                                   4

                                   (1 row affected)
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                  (1 row affected)

                                   select difference("abc", "abd")

                                   ------------

                                   3

                                   (1 row affected)



 Most of the remaining string functions are easy to use and to understand.
 For example:

   Statement       Result

    select right("abcde", 3)   cde
    select right("abcde", 6)   abcde
    select str(123.45, 6, 2)   123.45
    select stuff("abc", 2, 1, "xyz") axyzc


 Concatenation

 You can concatenate binary or character expressions, combining two or more
 character or binary strings, character or binary data, or a combination of
 them.

 If you're concatenating a character string, enclose it in single or double
 quotation marks.

 Concatenation has the following syntax:

   (expression + expression [+ expression]...)

 Here's how you'd combine two character strings:

   select ("abc" + "def")

    ------
    abcdef

    (1 row affected)

 This query displays California author names under the column heading Moniker
 in last-name, first-name order with a comma and space after the last name:

   select Moniker = (au_lname + ", " + au_fname)
    from authors
    where state = "CA"

    Moniker
    -------------------------
    White, Johnson
    Green, Marjorie
    Carson, Cheryl
    O'Leary, Michael
    Straight, Dick
    Bennet, Abraham
    Dull, Ann
    Gringlesby, Burt
    Locksley, Chastity
    Yokomoto, Akiko
    Stringer, Dirk
    MacFeather, Stearns
    Karsen, Livia
    Hunter, Sheryl
    McBadden, Heather

    (15 rows affected)


 Nested String Functions

 String functions can be nested. For example, to display the last name and
 the first initial of each author living in Oakland, with a comma after the
 last name and a period after the first name, type

   select (au_lname + "," + " " + substring(au_fname, 1, 1) + ".")
    from authors
    where city = "Oakland"

    ----------------------
    Green, M.
    Straight, D.
    Stringer, D.
    MacFeather, S.
    Karsen, L.

    (5 rows affected)

 To display the pub_id and the first two characters of each title_id for
 books over $20, type

   select substring(pub_id + title_id, 1, 6)
    from titles
    where price > $20

    -------
    1389PC
    0877PS
    0877TC

    (3 rows affected)


 Text and Image Functions

 Text functions are used for operations on text and image data. Text and
 image function names, parameters, and results are listed in the following
 table.


ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Function      Parameters                                  Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 PATINDEX      ("pattern", column_name)                    The starting positi
                                                           first occurrence of
                                                           the specified colum
                                                           use wildcard charac
                                                           pattern.

 SET TEXTSIZE  {n|0}                                       The limit in bytes
                                                           or image data to be
 Function      Parameters                                  Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                                          or image data to be
                                                           with a SELECT state
                                                           current setting is
                                                           the @TEXTSIZE globa
                                                           The integer n speci
                                                           limit on the number
                                                           be returned. 0 rest
                                                           default limit (32K

 TEXTPTR       (column_name)                               The text pointer va
                                                           varbinary format. T
                                                           is checked to ensur
                                                           to the first text p

 TEXTVALID     ("table_name.column_name",                  Returns 1 if the gi
               text_ptr)                                   pointer is valid an
                                                           pointer is invalid.
                                                           the identifier for
                                                           column must include
 Function      Parameters                                  Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                                          column must include
                                                           name.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ




 TEXTPTR

 This example uses the TEXTPTR function to locate the text column (blurb)
 associated with title_id BU7832 in table texttest. The text pointer (a long
 binary string) is put into a local variable @val and supplied as a parameter
 to the READTEXT statement, which returns 5 bytes starting at the second byte
 (offset of 1).

   declare @val varbinary(30)
    select @val = textptr(blurb) from texttest
    where title_id = "BU7832"
    readtext texttest.blurb @val 1 5

 Without using a local variable, a statement such as

   select textptr(text_col) from my_table

 returns a long binary string. It is a good idea to put this string into a
 local variable, as in the preceding example, and use it by reference.

 Explicit conversion using the CONVERT function is supported from text to
 varchar and from image to varbinary, but the text or image data is truncated
 to 255 bytes. Implicit conversion of text or image to another datatype is
 not supported.


 Mathematical Functions

 Mathematical functions return values commonly needed for operations on
 mathematical data.

 Mathematical functions have the following syntax:

   function_name(parameters)

 Function names, parameters, and results are listed in the following table.




ÖŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Function  Parameters                         Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ABS       (numeric_expr)                     An absolute value of a given
                                              expression. The expression can b
                                              of integer, float, or money type
                                              Results are of the same type as
                                              the numeric expression.

 ACOS      (float_expr)                       An angle (in radians) whose
                                              cosine is the specified
                                              floating-point value.

 Function  Parameters                         Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 ASIN      (float_expr)                       An angle (in radians) whose sine
                                              is the specified floating-point
                                              value.

 ATAN      (float_expr)                       An angle (in radians) whose
                                              tangent is the specified
                                              floating-point value.

 ATN2      (float_expr1, float_expr2)         An angle (in radians) whose
                                              tangent is (
                                              float_expr1/float_expr2).

 CEILING   (numeric_expr)                     The smallest integer greater tha
                                              or equal to the specified value.
                                              The expression can be of integer
                                              float, or money type.  Results
                                              are of the same type as the
                                              numeric expression.
 Function  Parameters                         Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                             numeric expression.

 COS       (float_expr)                       The trigonometric cosine of the
                                              specified angle (in radians).

 COT       (float_expr)                       The trigonometric cotangent of
                                              the specified angle (in radians)

 DEGREES   (numeric_expr)                     Degrees converted from radians.
                                              The expression can be of integer
                                              float, or money type. Results ar
                                              of the same type as the numeric
                                              expression.

 EXP       (float_expr)                       The exponential value of the
                                              specified value.

 FLOOR     (numeric_expr)                     The largest integer less than or
                                              equal to the specified value. Th
 Function  Parameters                         Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                             equal to the specified value. Th
                                              expression can be of integer,
                                              float, or money type. Results ar
                                              of the same type as the numeric
                                              expression.

 LOG       (float_expr)                       The natural logarithm of the
                                              specified value.

 LOG10     (float_expr)                       The base 10 logarithm of the
                                              specified value.

 PI        ( )                                The constant value of
                                              3.1415926535897936.

 POWER     (numeric_expr, y)                  The value of numeric_expr to the
                                              power of y. The expression and y
                                              can be of integer, float, or
                                              money type. Results are of the
 Function  Parameters                         Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                             money type. Results are of the
                                              same type as numeric_expr.

 RADIANS   (numeric_expr)                     Radians converted from degrees.
                                              The expression can be of integer
                                              float, or money type. Results ar
                                              of the same type as numeric_expr

 RAND      ([int_expr])                       A random float number, using
                                              int_expr as the optional seed.

 ROUND     (numeric_expr, int_expr)           A numeric expression rounded off
                                              to the precision specified in
                                              int_expr. The expression can be
                                              of integer, float, or money type
                                              Results are of the same type as
                                              the numeric expression.

 SIGN      (numeric_expr)                     Positive (+1), zero (0), or
 Function  Parameters                         Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
SIGN      (numeric_expr)                     Positive (+1), zero (0), or
                                              negative (-1). The expression ca
                                              be of integer, float, or money
                                              type. Results are of the same
                                              type as the numeric expression.

 SIN       (float_expr)                       The trigonometric sine of the
                                              specified angle (measured in
                                              radians).

 SQRT      (float_expr)                       The square root of the specified
                                              value.

 TAN       (float_expr)                       The trigonometric tangent of the
                                              specified angle (measured in
                                              radians).

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Function  Parameters                         Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ




 Examples of Mathematical Functions

 Mathematical functions operate on numeric data of integer, money, or float
 datatype. The precision of built-in operations on float datatype data is six
 decimal places by default.

 Error traps are provided to handle domain or range errors of mathematical
 functions. A user can set the options ARITHABORT or ARITHIGNORE, which
 respectively abort the query or return NULL when a mathematical function
 encounters a domain error condition. No warning message is displayed. If
 neither of these options is set, the system returns NULL and prints a
 warning message after the query is executed.

 Some simple examples follow:

   Statement   Result

   A. select floor(123.45) 123.000000
    select floor(-123.45) -124.000000
    select floor($123.45) $123.00

   B. select ceiling(123.45) 124.000000
    select ceiling(-123.45) -123.000000
    select ceiling($123.45) $124.00

   C. select round(123.4545, 2) 123.450000
    select round(123.45, -2) 100.000000

 The ROUND function always returns a value even if the length is illegal. If
 the specified length is positive and longer than the digits after the
 decimal point, 0 is added after the fraction digits.

 If the length is negative and larger than or equal to the digits before the
 decimal point, ROUND returns 0.00.

 The last digit in a floating-point number is always an estimate:

   round(123.9995, 3) = 123.999
    while
    round(123.9996, 3) = 124.000


 Date Functions

 Date functions are used to display information about dates and times. They
 manipulate datetime values, performing arithmetic operations on them.

 Date functions can be used in the select list, in the WHERE clause, or
 wherever an expression can be used.

 Values with the datetime datatype are stored internally by SQL Server as two
 4-byte integers. The first four bytes store the number of days before or
 after the base date, January 1, 1900. The base date is the system's
 reference date. The datetime values earlier than January 1, 1753 are not
 permitted. The other four bytes store the time of day represented as the
 number of milliseconds after midnight.

 The default display format for dates looks like this:

   Apr 15 1987 10:23PM

 When you enter datetime values, enclose them in single or double quotation
 marks. SQL Server recognizes a wide variety of datetime data entry formats.
 (For more information about datetime values, see Chapter 8, "Creating
 Defaults and Rules," and Chapter 10, "Creating Views."

 The date functions, their parameters, and the results they produce are
 listed in the following table.

ÖŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Function  Parameter                          Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 DATEADD   (datepart, number, date)           A date produced by adding an
                                              interval to a date you specify.
                                              The result is a datetime value
                                              equal to the date plus the numbe
                                              of date parts.

 Function  Parameter                          Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 DATEDIFF  (datepart, date1, date2)           The number of dateparts between
                                              two specified dates. The result
                                              is a signed integer value equal
                                              to date2 minus date1 in date
                                              parts.

 DATENAME  (datepart, date)                   The specified datepart (the firs
                                              parameter) of the specified date
                                              (the second parameter) as a
                                              character string.

 DATEPART  (datepart, date)                   The specified datepart (the firs
                                              parameter) of the specified date
                                              (the second parameter) as an
                                              integer.

 GETDATE   ( )                                The current date and time in SQL
                                              Server's standard internal forma
 Function  Parameter                          Result
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                             Server's standard internal forma
                                              for datetime values. GETDATE
                                              takes the NULL parameter, ( ).

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 The DATENAME, DATEPART, DATEDIFF, and DATEADD functions take as parameters a
 date partÄthe year, month, hour, and so on. The following table gives each
 date part, its abbreviation if there is one, and the possible integer values
 for that date part. The DATENAME function produces ASCII values where
 appropriate, such as for day of week.

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 Date Part    Abbreviation  Values
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 year         yy            1900-9999

 Date Part    Abbreviation  Values
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 quarter      qq            1-4

 month        mm            1-12

 day of year  dy            1-366

 day          dd            1-31

 week         wk            0-51

 weekday      dw            1-7 (1 is Sunday)

 hour         hh            0-23

 minute       mi            0-59

 second       ss            0-59

 Date Part    Abbreviation  Values
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 millisecond  ms            0-999

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ




 GETDATE

 The GETDATE function produces the current date and time in SQL Server
 internal format for datetime values. GETDATE takes the NULL parameter, ( ).


 To find the current system date and time, type

   select getdate()

    --------------------------
    July 29 1986 2:50 PM

    (1 row affected)

 You might use GETDATE in designing a report so that the current date and
 time are printed every time the report is produced. GETDATE is also useful
 for functions such as logging the time a transaction occurred on an account.



 DATEPART and DATENAME

 The DATEPART and DATENAME functions produce the specified part of a datetime
 valueÄthe year, quarter, day, hour, and so onÄ as either an integer or an
 ASCII string.

 The following examples assume the July 29 date shown in the preceding
 example:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   select datepart(month, getdate())

                                   ------------

                                   7

                                   (1 row affected)

                                   select datename(month, getdate())

                                   -----------

                                   July

                                   (1 row affected)




 DATEDIFF

 The DATEDIFF function calculates the amount of time in date parts between
 the second and first of two dates you specifyÄin other words, it finds an
 interval between two dates. The result is a signed integer value equal to
 date2-date1 in date parts.

 This query uses the date November 30, 1985, and finds the number of days
 that elapsed between pubdate and that date:

   select newdate = datediff(day, pubdate, "Nov 30 1985")
    from titles

 For the rows in titles having a pubdate of October 21, 1985, the result
 produced by the previous query is 40. (There are 40 days between October 21
 and November 30.) To calculate an interval in months, type

   select interval = datediff(month, pubdate, "Nov 30 1985")
    from titles

 It produces a value of 1 for the rows with a pubdate in October and a value
 of 5 for the rows with a pubdate in June.

 When the first date in the DATEDIFF function is later than the second date
 specified, the resulting value is negative. Since two of the rows in titles
 have values for pubdate that are assigned using the GETDATE function as a
 default, these values will be set to the date that your pubs database was
 created and will return negative values in the two preceding queries.


 DATEADD

 DATEADD adds an interval to a date you specify.

 For example, if the publication dates of all the books in the titles table
 slipped three days, you could get the new publication dates with this
 statement:

   select dateadd(day, 3, pubdate)

    from titles
    -------------------
    Jun 15 1985 12:00AM
    Jun 12 1985 12:00AM
    Jul  3 1985 12:00AM
    Jun 25 1985 12:00AM
    Jun 12 1985 12:00AM
    Jun 21 1985 12:00AM
    Sep 11 1986 11:02AM
    Jul  3 1985 12:00AM
    Jun 15 1985 12:00AM
    Sep 11 1986 11:02AM
    Oct 24 1985 12:00AM
    Jun 18 1985 12:00AM
    Oct  8 1985 12:00AM
    Jun 15 1985 12:00AM
    Jun 15 1985 12:00AM
    Oct 24 1985 12:00AM
    Jun 15 1985 12:00AM
    Jun 15 1985 12:00AM

    (18 rows affected)


 Conversion Function

 The conversion function, CONVERT, is used to convert expressions of one
 datatype to another datatype whenever these conversions are not performed
 automatically by SQL Server. It is also used to obtain a variety of special
 date formats.

 The CONVERT function has the following syntax:

   CONVERT(datatype [(length)], expression [, style])

 The conversion function can be used in the select list, in the WHERE clause,
 and anywhere else an expression is allowed.

 Here's an example that uses CONVERT in the select list:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title, convert(char(20), ytd_sales)

         from titles
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        from titles

         where type = "trad_cook"

         title

         -------------------------------------------------<19
         6>-------------

         Onions, Leeks, and Garlic: Cooking Secrets of the Mediterranean

         Fifty Years in Buckingham Palace Kitchens

         Sushi, Anyone?

         (3 rows affected)



 In this example, the ytd_sales column, an int column, is converted to a
 char(20) column so that it can be used with the LIKE clause:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title, ytd_sales

         from titles

         where convert(char(20), ytd_sales) like "15%"

         and type = "trad_cook"



         title

         -----------------------------------------

         Fifty Years in Buckingham Palace Kitchens

         (1 row affected)
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        (1 row affected)



 SQL Server automatically handles certain datatype conversions. For example,
 if you compare a char expression and a datetime expression, or a smallint
 expression and an int expression, or char expressions of different lengths,
 SQL Server converts them automatically. You need not use the CONVERT
 function.

 It is never wrong to use the CONVERT function, even when you are comparing
 two expressions of exactly the same datatype.

 If you attempt a conversion that is not possible (for example, if you try to
 convert a char expression that includes letters to an int), SQL Server
 displays an error message.

 Table 5.1 shows the possibilities for converting datatypes.

 Table 5.1  Datatype Conversion Chart

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 To:             binary  varbinary  tinyint(INT1)  smallint(INT2)  int(INT4)

 From:

 binary          -       I          I              I               I

 varbinary       I       -          I              I               I

 tinyint(INT1)   E       I          -              I               I

 smallint(INT2)  E       I          I              -               I

 int(INT4)       E       E          I              I               -

 float           N       N          I              I               I

 char            E       E          E              E               E

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 varchar         E       E          E              E               E

 money           I       I          I              I               I

 bit             I       I          I              I               I

 datetime        E       E          N              N               N

 text            N       N          N              N               N

 image           E       E          N              N               N

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Key:
 I Implicit conversion
 E Explicit conversion, CONVERT function must be used
 N Conversion not allowed
 - Conversion of a datatype to itself; allowed but meaningless


 If you do not specify a length when converting to char, varchar, binary, or
 varbinary datatypes, the converted data adapts to any size necessary.

 When converting to datetime, SQL Server rejects all values it cannot
 recognize as dates (including dates earlier than January 1, 1753).

 Converting to bit changes any non-zero value to 1.

 When converting to money from integer datatypes, units are assumed to be
 dollars. For example, the integer value of 4 would be converted to the money
 equivalent of 4 dollars.

 Expressions of datatypes char or varchar that are being converted to an
 integer datatype must consist only of digits and an optional + or - sign.
 Leading blanks are ignored. Expressions of datatypes char or varchar that
 are being converted to money can also include an optional decimal point and
 dollar sign ($).

 Expressions of datatypes char or varchar that are being converted to float
 can also include optional exponential notation (a lowercase "e" or uppercase
 "E," followed by an optional + or - sign, and then a number).

 When expressions are converted to a datatype of a different size, values too
 long for the new datatype are truncated, and SQL Server displays an asterisk
 (*). Here's an example:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title, convert(char(2), ytd_sales)

         from titles

         where type = "trad_cook"

         title

         -------------------------------------------------<19 6>-------------

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         Onions, Leeks, and Garlic: Cooking Secrets of the Mediterranean

         Fifty Years in Buckingham Palace Kitchens

         Sushi, Anyone?

         (3 rows affected)



 When converting between datatypes with a different number of decimal points,
 the value is truncated. For example, the result of CONVERT(money, 10.3496)
 is $10.34.


 The Style Parameter

 The STYLE parameter of CONVERT provides a wide variety of date display
 formats when converting datetime data to char or varchar. The number
 parameter you supply as the STYLE parameter determines how the datetime data
 is displayed. The year can be displayed in either 2 digits or 4 digits. Add
 100 to a STYLE value to get a 4-place year, including the century (yyyy).

 The following table shows the possible values for STYLE and the variety of
 date formats you can use:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Without              With
 Century (yy)         Century (yyyy)      Standard             Output
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 -                    0 or 100            default              mon dd yyyy
                                                               hh:miAM (or PM)

 1                    101                 USA                  mm/dd/yy

 2                    102                 ANSI                 yy.mm.dd

 3                    103                 English/French       dd/mm/yy

 4                    104                 German               dd.mm.yy
 Without              With
 Century (yy)         Century (yyyy)      Standard             Output
4                    104                 German               dd.mm.yy

 5                    105                 Italian              dd-mm-yy

 6                    106                                      dd mon yy

 7                    107                                      mon dd yy

 8                    108                                      hh:mi:ss

 -                    9 or 109            default +            mon dd, yyyy
                                          milliseconds         hh:mi:sssAM (or
                                                               PM)

 10                   110                 USA                  mm-dd-yy

 11                   111                 Japan                yy/mm/dd

 12                   112                 ISO                  yymmdd
 Without              With
 Century (yy)         Century (yyyy)      Standard             Output
12                   112                 ISO                  yymmdd

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 The default values (STYLE 0 or 100, 9 or 109) always return the century
 (yyyy).

 The following example shows CONVERT with the STYLE parameter:

   select convert(char(12), getdate(), 3)

 This converts the current date to style "3," dd/mm/yy.






 Chapter 6  Building Subqueries
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 A subquery is a SELECT statement that is nested inside another SELECT,
 INSERT, UPDATE, or DELETE statement or inside another subquery. A statement
 that includes a subquery operates on rows from one table based on its
 evaluation of the subquery's SELECT statement, which can refer to the same
 table as the outer query or to a different table. In TRANSACT-SQL, a
 subquery can also be used anywhere an expression is allowed if it returns a
 single value.

 SELECT statements that contain one or more subqueries are sometimes called
 nested queries or nested select statements. The practice of nesting one
 select statement inside another is the reason for the word "structured" in
 Structured Query Language.

 Many SQL statements that include subqueries can be alternatively formulated
 as joins. Other questions can be posed only with subqueries.

 Some people prefer subqueries to alternative formulations because they find
 subqueries easier to understand. Other SQL users prefer joins. In
 TRANSACT-SQL, there are no performance differences between a statement that
 includes a subquery and a semantically equivalent version that does not. You
 can choose whichever formulation you prefer.

 After giving subquery syntax, some general rules, and an introductory
 example, this chapter discusses:


   ž   Subqueries introduced with the keywords IN or NOT IN

   ž   Subqueries introduced with comparison operators and with comparison
       operators modified with the keywords ANY or ALL

   ž   Correlated subqueries (subqueries that cannot be evaluated
       independently, but depend on the outer query for their results)

   ž   Subqueries introduced with EXISTS (a variety of a correlated subquery)



 Subquery Syntax

 The SELECT statement that includes a subquery can display data from only one
 table. You can name more than one table in the outer query's FROM list,
 although there is no advantage to so doing. The SELECT statement of a
 subquery is always enclosed in parentheses.

 A subquery nested in the outer SELECT statement has the following restricted
 SELECT syntax:

   (SELECT [DISTINCT] subquery_select_list
    [FROM [[database.]owner.]{table_name | view_name} [HOLDLOCK]
    [,[[database.]owner.]{table_name | view_name} [HOLDLOCK]...]
    [WHERE search_conditions]
    [GROUP BY aggregate_free_expression [, aggregate_free_expression...]]
    [HAVING search_conditions] )

 Subqueries can be nested inside the WHERE or HAVING clause of an outer
 SELECT, INSERT, UPDATE, or DELETE statement, or inside another subquery.
 There is no limit on the level of nesting.

 In TRANSACT-SQL, a subquery can appear anywhere an expression can be used,
 so long as it returns a single value.

 Statements that include a subquery usually take one of these formats:

   A. WHERE expression [NOT] IN (subquery)

   B. WHERE expression comparison_operator [ANY | ALL] (subquery)

   C. WHERE [NOT] EXISTS (subquery)


 Subquery Rules

 A subquery is subject to a number of restrictions:


   ž   The select list of a subquery introduced with a comparison operator
       can include only one expression or column name (except that EXISTS and
       IN operate on "SELECT *" or a list, respectively). If the WHERE clause
       of the outer statement includes a column name, it must be
       join-compatible with the column named in the subquery select list.

   ž   The select list of a subquery introduced with EXISTS almost always
       consists of an asterisk (*) instead of the single column name. Do not
       specify more than one column name. The select list rules for a
       subquery introduced with EXISTS are identical to those for a standard
       select list because a subquery introduced with EXISTS constitutes an
       existence test and returns true or false rather than any data.

   ž   Subqueries cannot include ORDER BY or COMPUTE clauses or the INTO
       keyword.

   ž   Subqueries introduced by an unmodified comparison operator (a
       comparison operator not followed by the keyword ANY or ALL) cannot
       include GROUP BY and HAVING clauses because these subqueries must
       return a single value.

   ž   The DISTINCT keyword cannot be used with subqueries that include a
       GROUP BY clause.


 In some SQL statements, the subquery (also called the inner query) can be
 evaluated as if it were an independent query. Conceptually, the results of
 the subquery are substituted into the main statement, or outer query. (This
 is not how SQL Server actually processes SQL statements with subqueries
 though.)

 There are three basic types of subqueries:


   ž   Subqueries that operate on lists, introduced with IN, or with a
       comparison operator modified by ANY or ALL

   ž   Subqueries that are introduced with an unmodified comparison operator,
       and must return a single value

   ž   Subqueries that are an existence test, introduced with EXISTS



 An Example

 If you want to find all the books that are the same price as Straight Talk
 About Computers, you can perform the task in two steps. First, find the
 price of Straight Talk:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   select price

                                   from titles

                                   where title = "Straight Talk About
                                   Computers"

                                   price

                                   ------------

                                   $19.99
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                  $19.99

                                   (1 row affected)



 Now, use the result (the price) in a second query to find all the books that
 cost the same as Straight Talk:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title, price

         from titles

         where price = $19.99

         title

         ---------------------------------------------
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        ---------------------------------------------

         The Busy Executive's Database Guide

         Straight Talk About Computers

         Silicon Valley Gastronomic Treats

         Prolonged Data Deprivation: Four Case Studies

         (4 rows affected)



 With a subquery you can solve the problem with a single statement:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select title, price

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         from titles

         where price =

         (select price

         from titles

         where title = "Straight Talk About Computers")

         title

         ---------------------------------------------

         The Busy Executive's Database Guide

         Straight Talk About Computers

         Silicon Valley Gastronomic Treats
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        Silicon Valley Gastronomic Treats

         Prolonged Data Deprivation: Four Case Studies

         (4 rows affected)




 Subqueries Introduced with IN

 Subqueries that are introduced with the keyword IN have the following
 syntax:

   WHERE [expression [NOT] IN (subquery)

 The result of a subquery introduced with IN (or with NOT IN, discussed
 later) is a list of zero or more values. Once the subquery returns results,
 the outer query makes use of them.

 This query finds the names of the publishers who have published business
 books:

   select distinct pub_name
    from publishers
    where pub_id in
     (select pub_id
     from titles
     where type = "business")

    pub_name
    --------------------
    New Age Books
    Algodata Infosystems

    (2 rows affected)

 This statement is evaluated in two steps. First, the inner query returns the
 identification numbers of those publishers that have published business
 books (1389 and 0736). Second, these values are substituted into the outer
 query, which finds the names that go with the identification numbers in the
 publishers table. It looks like this:

   select pub_name
    from publishers
    where pub_id in ("1389", "0736")

 Another way to formulate this query using a subquery is

   select distinct pub_name
    from publishers
    where "business" in
     (select type
     from titles
     where pub_id = publishers.pub_id)

 Note that the expression following the WHERE keyword in the outer query can
 be a constant as well as a column name. Other types of expressions (for
 example, combinations of constants and column names) are also acceptable.

 The preceding queries, like many other subqueries, could be alternatively
 formulated as a join query:

   select distinct pub_name
    from publishers, titles
    where publishers.pub_id = titles.pub_id
     and type = "business"

 This statement and the subquery versions all find publishers who have put
 out business books. They all produce the same results, so in cases like
 this, choose the method you are most comfortable with.

 However, there is one advantage to using a join rather than a subquery for
 this and similar problems. The join lets you show columns from more than one
 table in the result. For example, if you wanted to include the titles of the
 business books in the result, you'd need to use the join version:

   select pub_name, title
    from publishers, titles
    where publishers.pub_id = titles.pub_id
     and type = "business"

    pub_name
     title
    --------------------
     ----------------------------------------------------
    Algodata Infosystems
     The Busy Executive's Database Guide
    Algodata Infosystems
     Cooking with Computers: Surreptitious Balance Sheets
    New Age Books
     You Can Combat Computer Stress!
    Algodata Infosystems
     Straight Talk About Computers
    (4 rows affected)

 Here's another example of a statement that can be formulated either with a
 subquery or with a join query. This query finds the names of all second
 authors who live in California and receive less than thirty percent of the
 royalties on some book:

   select au_lname, au_fname
    from authors
    where state = "CA"
     and au_id in
    (select au_id
    from titleauthor
    where royaltyper < 30
     and au_ord = 2)

    au_lname   au_fname
    ----------   --------
    MacFeather   Stearns

    (1 row affected)

 The inner query is evaluated, producing a list of the IDs of the three
 authors that meet the qualification. The outer query is then evaluated.
 Notice that you can include more than one condition in the WHERE clause of
 both the inner and the outer query.

 Using a join, the query is expressed like this:

   select au_lname, au_fname
    from authors, titleauthor
    where state = "CA"
     and authors.au_id = titleauthor.au_id
     and royaltyper < 30
     and au_ord = 2

 A join can always be expressed as a subquery. A subquery can often, but not
 always, be expressed as a join.


 Subqueries Introduced with NOT IN

 Subqueries introduced with the keywords NOT IN also return a list of zero or
 more values.

 This query finds the names of the publishers who have not published business
 books (the opposite of an earlier example):

   select distinct pub_name
    from publishers
    where pub_id not in
     (select pub_id
     from titles
     where type = "business")

    pub_name
    --------------------
    Binnet & Hardley

    (1 row affected)

 The query is exactly the same as the previous one except that NOT IN is
 substituted for IN. However, this statement cannot be converted to a join.
 The analogous "not equal" join has a different meaningÄit finds the names of
 publishers who have published some book that is not a business book. (The
 difficulties with interpreting the meaning of joins not based on equality
 are discussed in more detail in Chapter 3, "Joining Two or More Tables.")


 Qualifying Column Names

 In the earlier example, the pub_id column in the WHERE clause of the outer
 query is implicitly qualified by the table name in the outer query's FROM
 clause, publishers. The reference to pub_id in the select list of the
 subquery is qualified by the subquery's FROM clause, that is, by the titles
 table.

 The general rule is that column names in a statement are implicitly
 qualified by the table referenced in the FROM clause at the same level.

 Here's what the query looks like with these implicit assumptions spelled
 out:

   select distinct pub_name
    from publishers
    where publishers.pub_id not in
     (select titles.pub_id
     from titles
     where type = "business")

 It is never wrong to state the table name explicitly, and it is always
 possible to override implicit assumptions about table names with explicit
 qualifications.


 Multiple Levels of Nesting

 A subquery can itself include one or more subqueries. Any number of
 subqueries can be nested in a statement.

 An example of a problem that can be solved using a statement with multiple
 levels of nested queries is, "Find the names of authors who have
 participated in writing at least one popular computer book."

   select au_lname, au_fname
    from authors
    where au_id in
     (select au_id
     from titleauthor
     where title_id in
      (select title_id
      from titles
      where type = "popular_comp") )

    au_lname  au_fname
    --------  -----------
    Carson   Cheryl
    Dull   Ann
    Hunter   Sheryl
    Locksley  Chastity

    (4 rows affected)

 The innermost query returns the title ID numbers PC1035, PC8888, and PC9999.
 The query the next level up is evaluated with these title IDs and returns
 the author ID numbers. Finally, the outer query uses the author IDs to find
 the names of the authors.

 You can also express this query as a join:

   select au_lname, au_fname
    from authors, titles, titleauthor
    where authors.au_id = titleauthor.au_id
     and titles.title_id = titleauthor.title_id
     and type = "popular_comp"


 Subqueries in UPDATE, DELETE, and INSERT

 Subqueries can be nested in UPDATE, DELETE, and INSERT statements, as well
 as in SELECT statements.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE
 Executing these example queries will change the sample database. Ask your
 System Administrator for help in getting a clean copy of the pubs sample
 database.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 The following query doubles the price of all books published by New Age
 Books. The statement updates the titles table; its subquery references the
 publishers table.

   update titles
    set price = price * 2
    where pub_id in
     (select pub_id
     from publishers
     where pub_name = "New Age Books")

 An equivalent UPDATE statement using a join is

   update titles
    set price = price * 2
    from titles, publishers
    where titles.pub_id = publishers.pub_id
     and pub_name = "New Age Books"

 You can remove all records of sales of business books with this nested
 select statement:

   delete sales
    where title_id in
    (select title_id
    from titles
    where type = "business"

 An equivalent DELETE statement using a join is

   delete sales
    from sales, titles
    where sales.title_id = titles.title_id
     and type = "business"


 Subqueries with Aliases

 Many statements in which the subquery and the outer query refer to the same
 table can be alternatively stated as self-joins (joining a table to itself).
 For example, you can find authors who live in the same city as Livia Karsen
 by using a subquery:

   select au_lname, au_fname, city
    from authors
    where city in
     (select city
     from authors
     where au_fname = "Livia"
      and au_lname = "Karsen")

    au_lname   au_fname   city
    ----------   ---------   -------
    Green    Marjorie   Oakland
    Straight   Dick    Oakland
    Stringer   Dirk    Oakland
    MacFeather   Stearns    Oakland
    Karsen    Livia    Oakland

    (5 rows affected)

 Or, you can use a self-join:

   select au1.au_lname, au1.au_fname, au1.city
    from authors au1, authors au2
    where au1.city = au2.city
     and au2.au_lname = "Karsen"
     and au2.au_fname = "Livia"

 You may recall from the discussion of self-joins in Chapter 3, "Joining Two
 or More Tables," that table aliases are required because the table being
 joined to itself appears in two different roles. Aliases can also be used in
 nested queries that refer to the same table in an inner and outer query:

   select au1.au_lname, au1.au_fname, au1.city
    from authors au1
    where au1.city in
     (select au2.city
     from authors au2
     where au2.au_fname = "Livia"
      and au2.au_lname = "Karsen")

 Explicit aliases make it clear that the reference to authors in the subquery
 does not mean the same thing as the reference in the outer query.


 Comparison Operator Subqueries

 This section discusses subqueries that are introduced with one of the
 comparison operators =, !=, >, >=, <, !>, !< or <=. These subqueries have
 the following syntax:

   WHERE expression comparison_operator (subquery)

 A subquery introduced with an unmodified comparison operator (a comparison
 operator not followed by ANY or ALL) must return a single value, rather than
 a list of values like subqueries introduced with IN. If such a subquery
 returns more than one value, SQL Server displays an error message.

 Ideally, to use a subquery introduced by an unmodified comparison operator,
 you must be familiar enough with your data and with the nature of the
 problem to know that the subquery will return exactly one value.

 For example, if you suppose each publisher to be located in only one city,
 and you wish to find the names of authors who live in the city where
 Algodata Infosystems is located, you can write an SQL statement with a
 subquery introduced with the simple comparison operator "=":

   select au_lname, au_fname
    from authors
    where city =
     (select city
     from publishers
     where pub_name = "Algodata Infosystems")

    au_lname  au_fname
    --------  --------
    Carson   Cheryl
    Bennet   Abraham

    (2 rows affected)

 Subqueries introduced with unmodified comparison operators often include
 aggregate functions, since these return a single value. For example, this
 statement finds the names of all the books priced higher than the current
 minimum price:

   select title
    from titles
    where price >
     (select min(price)
     from titles)

    title
    ----------------------------------------------------------
    The Busy Executive's Database Guide
    Cooking with Computers: Surreptitious Balance Sheets
    Straight Talk About Computers
    Silicon Valley Gastronomic Treats
    But Is It User Friendly?
    Secrets of Silicon Valley
    Computer Phobic and Non-Phobic Individuals: Behavior Variations
    Is Anger the Enemy?
    Life Without Fear
    Prolonged Data Deprivation: Four Case Studies
    Emotional Security: A New Algorithm
    Onions, Leeks, and Garlic: Cooking Secrets of the Mediterranean
    Fifty Years in Buckingham Palace Kitchens
    Sushi, Anyone?

    (14 rows affected)

 Because subqueries introduced by unmodified comparison operators must return
 a single value, they cannot include GROUP BY or HAVING clauses unless you
 know that the GROUP BY or HAVING clause itself returns a single value. For
 example, this query finds the books priced higher than the lowest priced
 book in the trad_cook category:

   select title
    from titles
    where price >
     (select min(price)
     from titles
     group by type
     having type = "trad_cook")


 Comparison Operators Modified with ANY or ALL

 Comparison operators that introduce a subquery can be modified by the
 keywords ALL or ANY.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 The "=ALL" construct is not supported.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Subqueries introduced with a modified comparison operator have the following
 syntax:

   WHERE expression comparison_operator [ANY | ALL] (subquery)

 Subqueries introduced with a modified comparison operator return a list of
 zero or more values and can include a GROUP BY or HAVING clause. These
 subqueries can be restated with EXISTS, which is covered later in this
 chapter.

 Using the ">" comparison operator as an example, "> ALL" means greater than
 every valueÄin other words, greater than the maximum value. For example, ">
 ALL (1, 2, 3)" means greater than 3. "> ANY" means greater than at least one
 valueÄin other words, greater than the minimum. So "> ANY (1, 2, 3)" means
 greater than 1.

 In the context of a subquery, "> ALL" means that for a row to satisfy the
 condition specified in the outer query, the value in the column that
 introduces the subquery must be greater than each value in the list of
 values returned by the subquery.

 Similarly, "> ANY" means that for a row to satisfy the condition specified
 in the outer query, the value in the column that introduces the subquery
 must be greater than at least one of the values in the list of values
 returned by the subquery.

 The use of ALL and ANY can be confusing because computers can't tolerate the
 ambiguity that these words sometimes have in English. For example, you might
 ask the question, "Which books commanded an advance greater than any book
 published by Algodata Infosystems?"

 This question can be paraphrased to make the SQL "translation" of it
 clearer: "Which books commanded an advance greater than the largest advance
 paid by Algodata Infosystems?" The ALL keyword (not the ANY keyword) is
 required here:

   select title
    from titles
    where advance > all
     (select advance
     from publishers, titles
     where titles.pub_id = publishers.pub_id
      and pub_name = "Algodata Infosystems")

    title
    -------------------------------
    You Can Combat Computer Stress!
    The Gourmet Microwave

    (2 rows affected)

 For each title, the inner query finds a list of advance amounts paid by
 Algodata. The outer query looks at the largest value in the list and
 determines whether the title currently being considered has commanded an
 even bigger advance.

 If a subquery introduced with ALL and a comparison operator does not return
 any values, all the rows in the outer query satisfy the condition.

 The following query provides an example of a subquery introduced with a
 comparison operator modified by ANY. It finds the titles that got an advance
 larger than the minimum advance amount paid by Algodata Infosystems.

   select distinct title
    from titles
    where advance > any
     (select advance
     from titles, publishers
     where titles.pub_id = publishers.pub_id
      and pub_name = "Algodata Infosystems")

    title
    ---------------------------------------------------------------
    Sushi, Anyone?
    Life Without Fear
    The Gourmet Microwave
    But Is It User Friendly?
    Secrets of Silicon Valley
    You Can Combat Computer Stress!
    Computer Phobic and Non-Phobic Individuals: Behavior Variations
    Onions, Leeks, and Garlic: Cooking Secrets of the Mediterranean

    (8 rows affected)

 For each title, the inner query finds a list of advance amounts paid by
 Algodata. The outer query looks at all the values in the list and determines
 whether the title currently being considered has commanded an advance larger
 than any of those amounts. In other words, it finds titles with advances as
 large or larger than the lowest value paid by Algodata.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 The use of the DISTINCT keyword in the outer query suppresses false
 duplicates that result from the join condition in the subquery. DISTINCT is
 frequently needed when subqueries include a join.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 If the subquery does not return any values, all the rows in the outer query
 satisfy the condition.

 The "=ANY" operator is exactly equivalent to IN. For example, to find
 authors that live in the same city as some publisher, you can use either IN
 or =ANY:

   select au_lname, au_fname
    from authors
    where city in
     (select city
     from publishers)

    select au_lname, au_fname
    from authors
    where city = any
     (select city
     from publishers)

    au_lname   au_fname
    --------   ---------
    Carson    Cheryl
    Bennet    Abraham

    (2 rows affected)

 However, the "!=ANY" operator is different from NOT IN. !=ANY means "not = a
 or not = b or not = c". NOT IN means "not = a and not = b and not = c".

 For example, say you want to find the authors who live in a city where no
 publisher is located. You might try this query:

   select distinct au_lname, au_fname
    from authors
    where city != any
    (select city
    from publishers)

 The results include all 23 authors because every author lives in some city
 where no publisher is located, since each author lives in one and only one
 city.

 What's happened is that the inner query finds all the cities in which
 publishers are located, and then, for each city, the outer query finds the
 authors who don't live there. The DISTINCT keyword eliminates the
 duplicates.

 Here's what happens when you substitute NOT IN in this query:

   select au_lname, au_fname
    from authors
    where city not in
     (select city
     from publishers)

    au_lname    au_fname
    --------------   ----------
    del Castillo   Innes
    Blotchet-Halls   Reginald
    Gringlesby    Burt
    DeFrance    Michel
    Smith     Meander
    White     Johnson
    Greene     Morningstar
    Green     Marjorie
    Straight    Dick
    Stringer    Dirk
    MacFeather    Stearns
    Karsen     Livia
    Dull     Ann
    Hunter     Sheryl
    Panteley    Sylvia
    Ringer     Anne
    Ringer     Albert
    Locksley    Chastity
    O'Leary     Michael
    McBadden    Heather
    Yokomoto    Akiko

    (21 rows affected)

 These are the results you want. They include all the authors except Cheryl
 Carson and Abraham Bennet, who live in Berkeley, where Algodata Infosystems
 is located.

 You get the same results as in the previous example with the "!=ALL"
 operator, which is equivalent to NOT IN:

   select au_lname, au_fname
    from authors
    where city != all
     (select city
     from publishers)


 Correlated Subqueries

 Many of the previous queries could be evaluated by executing the subquery
 once and substituting the resulting value or values into the WHERE clause of
 the outer query. In queries that include a correlated or repeating subquery,
 the subquery depends on the outer query for its values. This means that the
 subquery is executed repeatedly, once for each row that might be selected by
 the outer query.

 This statement finds the names of all authors who earn 100% royalty on a
 book:

   select distinct au_lname, au_fname
    from authors
    where 100 in
     (select royaltyper
     from titleauthor
     where au_id = authors.au_id)

    au_lname    au_fname
    -------------   --------
    Carson     Cheryl
    Ringer     Albert
    Straight    Dick
    White     Johnson
    Green     Marjorie
    Panteley    Sylvia
    Locksley    Chastity
    del Castillo   Innes
    Blotchet-Halls   Reginald

    (9 rows affected)

 Unlike most of the subqueries previously considered, the subquery in this
 statement cannot be resolved independently of the main query. It needs a
 value for authors.au_id, but this value is a variableÄit changes as SQL
 Server examines different rows of the authors table.

 That's exactly how this query is evaluated: TRANSACT-SQL considers each row
 of the authors table for inclusion in the results by substituting the value
 in each row into the inner query. Say that TRANSACT-SQL first examines the
 row for Cheryl Carson. The variable authors.au_id takes the value
 "238-95-7766," which TRANSACT-SQL substitutes into the inner query:

   select royaltyper
    from titleauthor
    where au_id = "238-95-7766"

 The result is 100, so the outer query evaluates to

   select au_lname, au_fname
    from authors
    where 100 in (100)

 Since this is true, the row for Cheryl Carson is included in the results. If
 you go through the same procedure with the row for Abraham Bennet, you will
 see that this row is not included in the results.


 Correlated Subqueries with Aliases

 A correlated subquery can be used to find the types of books that are
 published by more than one publisher:

   select distinct t1.type
    from titles t1
    where t1.type in
     (select t2.type
     from titles t2
     where t1.pub_id != t2.pub_id)

    type
    ----------
    business
    psychology

    (2 rows affected)

 Aliases are required here to distinguish the two different roles in which
 the titles table appears. This nested query is equivalent to the self-join
 statement:

   select distinct t1.type
    from titles t1, titles t2
    where t1.type = t2.type
     and t1.pub_id != t2.pub_id


 Correlated Subqueries with Comparison Operators

 To find sales where the quantity is less than the average quantity for sales
 of that title, type

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select s1.ord_num, s1.title_id, s1.qty

         from sales s1

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         where s1.qty <

         (select avg(s2.qty)

         from sales s2

         where s2.title_id = s1.title_id)

         ord_num                                                    title_id

         --------                                                   --------

         D4482                                                      PS2091

         N914008                                                    PS2091

         423LL922                                                   MC3021

         722a                                                       PS2091
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        722a                                                       PS2091

         6871                                                       BU1032

         (5 rows affected)



 The outer query selects the rows of the sales table (that is, of s1,) one by
 one. The subquery calculates the average quantity for each sale being
 considered for selection in the outer query. For each possible value of s1,
 TRANSACT-SQL evaluates the subquery and puts the record being considered in
 the results if the quantity is less than the calculated average.

 Sometimes a correlated subquery mimics a GROUP BY statement. To find titles
 whose price is greater than the average for books of its type, type

   select t1.type, t1.title
    from titles t1
    where t1.price >
     (select avg(t2.price)
     from titles t2
     where t1.type = t2.type)

    type
     title
    -----
     -------------------------------
    business
     The Busy Executive's Database Guide
    business
     Straight Talk About Computers
    mod_cook
     Silicon Valley Gastronomic Treats
    popular_comp
     But Is It User Friendly?
    psychology
     Computer Phobic and Non-Phobic Individuals: Behavior Variations
    psychology
     Prolonged Data Deprivation: Four Case Studies
    trad_cook
     Onions, Leeks, and Garlic: Cooking Secrets of the Mediterranean

    (7 rows affected)

 For each possible value of t1, TRANSACT-SQL evaluates the subquery and
 includes the row in the results if the price value of that row is greater
 than the calculated average. It is not necessary to group by type
 explicitly, because the rows for which average price is calculated are
 restricted by the WHERE clause in the subquery.


 Correlated Subqueries in a HAVING Clause

 A correlated subquery can also be used in the HAVING clause of an outer
 query. This construction can be used to find the types of books in which the
 maximum advance is more than twice the average within a given group.

   select t1.type
    from titles t1
    group by t1.type
    having max(t1.advance) >=all
     (select 2 * avg(t2.advance)
     from titles t2
     where t1.type = t2.type)

    type
    --------
    mod_cook

    (1 row affected)

 In this case, the subquery is evaluated once for each group defined in the
 outer queryÄonce for each type of book.


 Subqueries Introduced with EXISTS

 When a subquery is introduced with the keyword EXISTS, the subquery
 functions as an "existence test." In other words, the WHERE clause of the
 outer query tests for the existence of rows returned by the subquery. The
 subquery does not actually produce any data, but rather returns a value of
 "true" or "false."

 A subquery introduced with EXISTS has the following syntax:

   WHERE [NOT] EXISTS (subquery)

 To find the names of all the publishers who publish business books, type

   select distinct pub_name
    from publishers
    where exists
     (select *
     from titles
     where pub_id = publishers.pub_id
      and type = "business")

    pub_name
    --------------------
    New Age Books
    Algodata Infosystems

    (2 rows affected)

 To determine the results of this query, consider each publisher's name in
 turn. Does this value cause the subquery to return at least one row? In
 other words, does it cause the existence test to evaluate to true?

 In this case, the first publisher's name is Algodata Infosystems, with an
 identification number of 1389. Are there any rows in the titles table in
 which pub_id is 1389 and type is business? If so, "Algodata Infosystems"
 should be one of the values selected. The same process is repeated for each
 of the other publisher's names.

 Notice that subqueries introduced with EXISTS are a bit different from other
 subqueries in these ways:


   ž   The keyword EXISTS is not preceded by a column name, constant, or
       other expression.

   ž   The select list of a subquery introduced by EXISTS almost always
       consists of an asterisk (*). There is no real point in listing column
       names, since you are simply testing for the existence of rows that
       meet the conditions specified in the subquery. A subquery introduced
       with EXISTS evaluates to true or false rather than returning any data.


 The EXISTS keyword is very important because there is often no alternative,
 nonsubquery formulation. A subquery introduced by EXISTS will always be a
 correlated subquery.

 Although some queries formulated with EXISTS cannot be expressed any other
 way, all queries that use IN or a comparison operator modified by ANY or ALL
 can be expressed with EXISTS. Some examples of statements using EXISTS and
 their equivalent alternatives follow. You've seen some of them before.

 Here are two ways to find authors that live in the same city as some
 publisher:

   select au_lname, au_fname
    from authors
    where city =any
     (select city
    from publishers)
    select au_lname, au_fname
    from authors
    where exists
     (select *
     from publishers
     where authors.city = publishers.city)

    au_lname   au_fname
    --------   --------
    Carson    Cheryl
    Bennet    Abraham

    (2 rows affected)

 Here are two queries that find titles of books published by any publisher
 located in a city that begins with the letter "B":

   select title
    from titles
    where pub_id in
     (select pub_id
     from publishers
     where city like "B%")

    ----------------------------------------------------
    You Can Combat Computer Stress!
    Is Anger the Enemy?
    Life Without Fear
    Prolonged Data Deprivation: Four Case Studies
    Emotional Security: A New Algorithm
    The Busy Executive's Database Guide
    Cooking with Computers: Surreptitious Balance Sheets
    Straight Talk About Computers
    But Is It User Friendly?
    Secrets of Silicon Valley
    Net Etiquette

    (11 rows affected)

    select title
    from titles
    where exists
     (select *
     from publishers
     where pub_id = titles.pub_id
      and city like "B%")

 The second query produces the same results in a different order since it
 evaluates the tables in different order.


 Subqueries Introduced with NOT EXISTS

 NOT EXISTS is just like EXISTS, except that the WHERE clause in which it is
 used is satisfied if no rows are returned by the subquery.

 For example, to find the names of publishers who do not publish business
 books, type

   select pub_name
    from publishers
    where not exists
     (select *
     from titles
     where pub_id = publishers.pub_id
      and type = "business")

    pub_name
    ----------------
    Binnet & Hardley

    (1 row affected)

 This query finds the titles for which there have been no sales:

   select title
    from titles
    where not exists
     (select title_id
     from sales
     where title_id = titles.title_id)

    title
    ----------------------------------
    The Psychology of Computer Cooking
    Net Etiquette

    (2 rows affected)


 Using EXISTS and NOT EXISTS to Find Intersection and Difference

 Subqueries introduced with EXISTS and NOT EXISTS can be used for two set
 theory operations: intersection and difference. The intersection of two sets
 contains all elements that belong to both of the two original sets. The
 difference contains the elements that belong only to the first of the two
 sets.

 The intersection of authors and publishers over the city column is the set
 of cities in which both an author and a publisher are located:

   select distinct city
    from authors
    where exists
     (select *
     from publishers
     where authors.city = publishers.city)

    city
    --------
    Berkeley

    (1 row affected)

 The difference between authors and publishers over the city column is the
 set of cities where an author lives but no publisher is located (that is,
 all the cities except Berkeley):

   select distinct city
    from authors
    where not exists
     (select *
     from publishers
     where authors.city = publishers.city)

    city
    ----------
    Gary
    Covelo
    Oakland
    Lawrence
    San Jose
    Ann Arbor
    Corvallis
    Nashville
    Palo Alto
    Rockville
    Vacaville
    Menlo Park
    Walnut Creek
    San Francisco
    Salt Lake City

    (15 rows affected)


 Subqueries Used in Place of an Expression

 In TRANSACT-SQL, a subquery can be substituted any place an expression can
 be used in SELECT, UPDATE, INSERT, and DELETE statements. Here's an example
 that illustrates how you might use this TRANSACT-SQL enhancement.

 This statement finds the books that have been written by an author living in
 California and published there:

   select distinct title, type
    from titles
    where title in
     (select title
     from titles, titleauthor, authors
     where titles.title_id = titleauthor.title_id
      and titleauthor.au_id = authors.au_id
      and authors.state = "CA")
      and title in
      (select title
      from titles, publishers
      where titles.pub_id = publishers.pub_id
       and publishers.state = "CA")

    title              type
    ---------------------------------------------------   ------------
    Straight Talk About Computers        business
    The Busy Executive's Database Guide       business
    Cooking with Computers: Surreptitious Balance Sheets  business
    Net Etiquette            popular_comp
    But Is It User Friendly?         popular_comp
    Secrets of Silicon Valley         popular_comp

    (6 rows affected)






 Chapter 7  Creating Databases, Tables, and Indexes
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 This chapter explains how to


   ž   Use and create a database

   ž   Create tables and define columns and their datatypes

   ž   Change tablesÄrename them, add columns, change the length of their
       columns

   ž   Create customized datatypes

   ž   Create and drop indexes


 This chapter also explains how to set up database objects. The process of
 setting up databases and database objects is called data definition.

 The database objects in SQL Server are


   ž   Tables

   ž   Rules

   ž   Defaults

   ž   Stored procedures

   ž   Triggers

   ž   Views


 An easy way to find out which components of a database are considered
 database objects is to look at the rows of the sysobjects system table.

 In covering these subjects, the following statements are discussed: USE;
 CREATE DATABASE, DROP DATABASE, and ALTER DATABASE; CREATE TABLE, DROP
 TABLE, and ALTER TABLE; and CREATE INDEX and DROP INDEX.

 In addition, these system procedures are discussed or mentioned: sp_addtype,
 sp_droptype, sp_commonkey, sp_primarykey, sp_foreignkey, sp_dropkey,
 sp_helpkey, sp_helpjoins, sp_rename, sp_defaultdb, sp_changedbowner,
 sp_spaceused, sp_help, sp_helpdb, and sp_helpindex.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 If you do not plan to create your own databases, tables, or indexes, relying
 on the System Administrator or another user to create them for you, you can
 read about the USE statement in this chapter and then skip to Chapter 8,
 "Creating Defaults and Rules."
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Help on Database Objects

 The sp_help system procedure reports information about a specified database
 object (any object listed in sysobjects), about a specified datatype (listed
 in systypes), or about all objects and types in the current database. (For
 information on databases, use sp_helpdb, which is discussed later in this
 chapter.)

 The sp_help system procedure has the following syntax:

   sp_help [objname]

 Here's the output for the publishers table:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         Name          Owner              type          Created_on

         ----------    -----              ----------    --------------------

         publishers    dbo                user table    Jan 28 1987  1:29 PM

         (0 rows
         affected)

         Column_nam    Type               Length        Nulls
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        Column_nam    Type               Length        Nulls

         ----------    --------------     ------        -----

         pub_id        char nocase        4             0

         pub_name      varchar nocase     40            1

         city          varchar nocase     20            1

         state         char nocase        2             1



         (0 rows
         affected)



         index_name    index_description                index_keys
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        index_name    index_description                index_keys

         ----------    -----------------                ----------

         pubind        clustered, unique                pub_id

         (1 row
         affected)

         No defined
         keys for
         this object.



 If you execute sp_help without supplying an object name, the resulting
 report shows a brief listing of each object in sysobjects, giving its name,
 owner, and object type. The report also shows a brief listing of each
 user-defined datatype in systypes, giving its name, storage type, length,
 nulltype (whether null values are allowed), and the names of any defaults or
 rules bound to it. The report also notes whether any primary or foreign key
 columns have been defined (for a table or view) with the sp_primarykey or
 sp_foreignkey system procedure.


 Permissions

 Whether or not you can create and drop databases and database objects
 depends on your permissions. Ordinarily, your System Administrator or
 Database Owner sets up the permissions for you, based on the kind of work
 you do and the functions you need. These permissions might be different for
 each user in a given installation.

 To make your experiments with database objects as convenient as possible,
 the sample database, pubs, has a guest user in its sysusers system table.
 The script that creates pubs grants a variety of permissions to guest. The
 guest mechanism means that anyone who has a login ID on SQL Server (that is,
 is listed in master..syslogins) has access to pubs and permission to create
 and drop objects such as tables, indexes, defaults, rules, procedures, and
 so on. The guest username also allows you to use certain stored procedures,
 create user datatypes, query the database, and modify the data in it.

 To use the pubs database, execute the USE statement. SQL Server checks to
 see if you are listed under your own name in pubs..sysusers. If not, you're
 admitted as a guest without any action on your part. If you are listed in
 pubs..sysusers, you'll be admitted as yourself, but your permissions may be
 different from guest's. All the examples in this chapter assume you are
 being treated as guest.


 Databases

 A database is a collection of related tables and other database objects.

 When SQL Server is first installed, it contains three system databases:


   ž   The master database controls the user databases and the operation of
       SQL Server as a whole.

   ž   The temporary database, tempdb, stores temporary tables, which
       disappear automatically at the end of a session.

   ž   The model database is a template in creating new user databases.


 The pubs database is a user database. All of your dataÄyour reason for using
 a database management systemÄis stored in user databases.

 SQL Server manages each database by means of system tables. The data
 dictionary tables in the master database and in other databases are
 considered system tables.


 Choosing a Database: USE

 Most of the time, you will be using a database that already exists. The USE
 statement accesses an existing database and has the following syntax:

   USE database_name

 For example, to open the database called pubs, type

   use pubs

 This statement opens the pubs database if you are a known user in pubs. If
 you are not known in the database, SQL Server displays a message telling you
 so. It is up to the owner of the database to give you access to it by
 executing the sp_adduser system procedure.

 Users not recognized by name in the master database are allowed in and
 treated as a user named guest. The guest user is added to the master
 database in the script that creates the master database when it is
 installed.

 A Database Owner can add a guest user to any user database with the
 sp_adduser system procedure. The System Administrator automatically becomes
 the Database Owner of whatever database he or she uses. (For more
 information, see the SQL Server System Administrator's Guide or the SQL
 Server Language Reference.)

 It is likely that you will be automatically connected to the master database
 when you log in to SQL Server. You must execute the USE statement to access
 some other database. You or the System Administrator can change the database
 to which you initially connect, using the sp_defaultdb system procedure.
 Only the System Administrator can change the default database for another
 user.


 Creating a User Database: CREATE DATABASE

 You can create a new database if the System Administrator has granted you
 permission to use the CREATE DATABASE statement. You must be using the
 master database when you create a new database. In many environments, the
 System Administrator creates all databases.

 Another user who creates a database for you can transfer ownership of it to
 you with the sp_changedbowner system procedure. The creator of a database is
 its owner.

 The Database Owner is responsible for giving users access to the database
 and for granting and revoking certain other permissions to users. In some
 organizations, the Database Owner is also responsible for maintaining
 regular backups of the database and for reloading it in case of system
 failure. The Database Owner can always impersonate any other user of the
 database, temporarily attaining that user's permissions, with the SETUSER
 statement.

 Because each database is allocated a significant amount of space, even if it
 contains only small amounts of data, you may not be given permission to use
 the CREATE DATABASE statement. If this is the case, you may want to skip
 this section and go on to the discussion of creating database objects.

 A simplified syntax of the CREATE DATABASE statement is

   CREATE DATABASE database_name

 To create the newpubs database, be sure you are using the master database,
 and then type

   create database newpubs

 A database name must be unique on SQL Server and must follow the rules for
 identifiers given in Chapter 1, "Overview." SQL Server can manage up to
 32,767 databases. You can create only one database at a time.

 When creating a new database, SQL Server copies the model database, which
 contains the system tables that belong in every user database.

 The creation of a new database is recorded in the master database tables
 sysdatabases and sysusages.

 The complete syntax of the CREATE DATABASE statement is

   CREATE DATABASE database_name
    [ON {DEFAULT | database_device} [= size]
    [, database_device [= size]...]]

 The ON clause allows you to specify where to store the database and how much
 space to allocate for it in megabytes. If you use the DEFAULT keyword, the
 database is assigned to an available database device in the pool of default
 database devices indicated in the master database table sysdevices. To
 specify a size of 4 megabytes for a database to be stored in this default
 location, use "ON DEFAULT = size" like this:

   create database newpubs
    on default = 4

 If you wish to specify a different location for the database, give the
 sysdevices logical name of the database device on which you want it stored.
 A database can be stored on more than one database device with different
 amounts of space on each.

 This statement creates the newpubs database and allocates 3 megabytes to it
 on file1 and 2 megabytes on file2:

   create database newpubs
    on file1 = 3, file2 = 2

 If the ON clause or the size is omitted, the database is created with 2
 megabytes of space from the pool of default database devices indicated in
 sysdevices.

 A database allocation can range in size from a minimum of 2 megabytes to 215
 megabytes.


 Removing a Database: DROP DATABASE

 The DROP DATABASE statement removes a database. DROP DATABASE deletes the
 database and all of its contents from SQL Server, frees the storage space
 that had been allocated for it, and deletes references to it from the master
 database.

 The DROP DATABASE statement has the following syntax:

   DROP DATABASE database_name [, database_name...]

 As indicated, you can drop more than one database in a single statement. For
 example:

   drop database newpubs, newdb


 Changing the Size of a Database: ALTER DATABASE

 If the space allocated for a database is too small, the Database Owner can
 increase it with the ALTER DATABASE statement. ALTER DATABASE permission
 defaults to the Database Owner and cannot be transferred. You must be using
 master to use the ALTER DATABASE statement.

 If a database has filled all of its allocated storage space, you cannot add
 new data to it or update it. (Existing data, of course, is always
 preserved.)

 The minimum (and the default) increase is 1 megabyte from the default pool
 of disk space. This statement adds 1 megabyte to newpubs on the default
 database device:

   alter database newpubs

 The full ALTER DATABASE syntax allows you to extend a database by more than
 1 megabyte and to specify where the storage space is to be added:

   ALTER DATABASE database_name
    [ON {DEFAULT | database_device} [=size] [, database_device [= size]...]]

 The ON clause in the ALTER DATABASE statement is just like the ON clause in
 the CREATE DATABASE statement.

 To increase the space allocated for newpubs by 2 megabytes on the database
 device named file1 and by 3 megabytes on file2, type

   alter database newpubs
    on file1 = 2, file2 = 3


 Help on Databases

 The sp_helpdb system procedure reports information about a specified
 database or about all the databases on SQL Server and has the following
 syntax:

   sp_helpdb [dbname]

 Here's how you'd get a report on pubs:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         sp_helpdb pubs

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         name            db_size  owner           dbid    created         stat

         ----            -------  -----           -----   -----------     ----

         pubs            2 MB     sa              4       Jan 10 1988     no o



         (1 row
         affected)



         device          size     usage

         ------          ----     ------------

         master          2 MB     data and log

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



         (1 row
         affected)



 For complete information on system procedures, see the SQL Server Language
 Reference.


 Tables

 When you create a table, you name its columns and supply a datatype for each
 column. You also specify whether or not a particular column can hold null
 values.

 There can be as many as 2 billion tables per database.


 CREATE TABLE Basics

 To create a table, use the CREATE TABLE statement. A simplified syntax of
 the CREATE TABLE statement is

   CREATE TABLE table_name
    (column_name datatype)

 For example, to create a table named names with one column named some_name
 and a fixed length of 11 characters, type

   create table names
    (some_name char(11) )

 You can define up to 250 columns. Column names must follow the rules for
 identifiers. They must be unique within a given table, but you can use the
 same column name in different tables in the same database.

 There must be a datatype for each column. The word "char" after the column
 name refers to the datatype of the columnÄthe type of values that a column
 will contain. Datatypes are described under "System-Supplied Datatypes"
 later in this chapter.

 The number in parentheses after the datatype gives the maximum number of
 characters that can be stored in the column. You give a maximum length for
 some datatypes. Others have a system-defined length.

 Be sure to put parentheses around the list of column names and commas after
 each column definition.


 Table Names

 The CREATE TABLE statement builds the new table in the currently open
 database. Table names must follow the rules for identifiers given in Chapter
 1, "Overview," and must be unique for each user.

 You can create temporary tables (which are automatically put into the
 temporary database, tempdb, and destroyed at the end of the current session)
 by preceding the table name with a pound sign (#), like this:

   create table #authors
    (au_id char (11))

 Since all temporary objects belong to the tempdb database you can create a
 temporary table with the same name as a table already in another database.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 Temporary tables and other temporary database objects are always created in
 the tempdb database. The names of temporary tables are assigned a number
 suffix internally.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 You can use any tables (or other objects) that you have created without
 qualifying their names. You can also use objects created by the Database
 Owner without qualifying their names, as long as you have the appropriate
 permissions on them. (These rules hold for all users, including the System
 Administrator and the Database Owner.)

 While table names must be unique for each user, different users can create a
 table of the same name. For example, a user named Jonah and one named Sally
 can both create a table named info. Users who have permission on both of
 those tables will have to qualify them as jonah.info and sally.info. Sally
 will have to qualify all references to Jonah's info table, although she can
 refer to her own simply as info.


 CREATE TABLE Syntax

 The complete syntax of the CREATE TABLE statement is

   CREATE TABLE [database.[owner.]]table_name
     (column_name datatype [NOT NULL | NULL]
     [, column_name datatype [NOT NULL | NULL]...])

 The CREATE TABLE statement for the titles table in the pubs database is

   create table titles
     (title_id tid,
     title varchar(80) not null,
     type char(12),
     pub_id char(4) null,
     price money null,
     advance money null,
     royalty int null,
     ytd_sales int null,
     notes varchar(200) null,
     pubdate datetime)

 The following sections define and discuss the system-supplied datatypes and
 user-defined datatypes. Then null values, temporary tables, and the DROP
 TABLE statement are explained. The section on tables ends with an example of
 the entire table creation process.


 System-Supplied Datatypes

 The datatype of a column specifies what kind of information (for example,
 characters, numbers, or dates) the column will hold and how the data will be
 stored. For example, the integer (int) datatype applies to a column that
 holds whole numbers in the range of plus or minus 2 billion, while the tiny
 integer (tinyint) datatype works with positive whole numbers between 0 and
 255 only.

 Thirteen system-supplied datatypes are available. You can also build
 customized user-defined datatypes (as explained later). Some of the
 system-supplied datatypes require you to specify a length in the form

   datatype(n)

 where n is the length. At data entry time, SQL Server checks to make sure
 that a value being entered is of the right type.

 If the value being entered is longer than n, SQL Server makes the entry,
 truncating it to the specified length. No warning message is given.

 Defaults and rules can be associated with user-defined datatypes by using
 the sp_bindrule and sp_bindefault system procedures. (Defaults and rules are
 discussed in Chapter 8, "Creating Defaults and Rules.")

 The datatypes provided by SQL Server are as follows:

 int
   An integer column that holds whole numbers between 231 -1 (2,147,483,647)
   and -231 +1 (-2,147,483,648), inclusive. However, you cannot enter
   -2,145,483,648, but you can enter -2,147,483,647 -1. Numbers may be
   positive or negative, but fractions and decimals are not permitted.
   Storage size is 4 bytes. All arithmetic operations and aggregate functions
   can be used with int data. Note that the maximum negative number should be
   added with INSERT and E-notation. Choose this datatype when the numbers
   you enter could be outside the range of smallint size.

 smallint
   A small integer column that holds whole numbers between 215 -1 (32,767)
   and -215 (-32,768), inclusive. Numbers may be positive or negative, but
   fractions and decimals are not permitted. Storage size is 2 bytes. All
   arithmetic operations and aggregate functions can be used with smallint
   data. Use this datatype for whole numbers within the given size range.

 tinyint
   A tiny integer column that holds whole numbers between 0 and 255,
   inclusive. Negative numbers, fractions, and decimals are not permitted.
   Storage size is 1 byte. All arithmetic operations and aggregate functions
   can be used with tinyint data. Use this datatype for whole numbers that
   will never exceed 255 or be less than 0.

 float
   A floating-point column that stores floating-point numbers. Storage size
   is 8 bytes. Use this datatype for positive and negative decimal numbers
   and for numbers with exponents. All arithmetic operations and aggregate
   functions except modulo are available with float. For float data with an
   exponential component, enter a number (with or without a decimal point and
   positive or negative sign), followed by e or E, followed by a number that
   can include a sign but not a decimal point. The value represented by a
   float is the product of the first number and the power of 10 of the second
   number.

 char(n)
   A character column that holds any combination of up to 255 letters,
   numbers, and symbols. Specify the maximum length of the column with n.
   Storage size is n, no matter what the actual entry length. Built-in string
   functions for concatenation and for finding substrings are provided for
   char, varchar, binary, and varbinary data. Columns of the char datatype
   (with fixed lengths) are accessed a little faster than columns of the
   varchar datatype (with variable lengths). They use more storage space if
   the number of characters you enter in this column is less than the fixed
   length you have specified. Choose char when you are using fixed-length
   data (as in a social security number or a zip code). Char data must be
   enclosed in single or double quotation marks when it's input or when you
   are searching for it. You can use the LIKE keyword and wildcard characters
   with char, varchar, and datetime data. (For details on LIKE, see Chapter
   2, "Querying Databases.") Rules for entering literal quotes and strings
   longer than the width of a screen are explained later. Numbers can be
   stored in char columns but they must be converted to a numeric datatype
   before you can do arithmetic on them.

 varchar(n)
   A variable-length character column that holds any combination of letters,
   numbers, and symbols from 1 to 255 characters. Specify the maximum size of
   the columns with n. Storage size is the actual size of the data values
   entered, not n. Built-in string functions for concatenation and for
   finding substrings are provided for char, varchar, binary, and varbinary
   data.

   Because of the way it uses space, varchar is best suited for data in which
   the length of an entry can vary considerably. Names, for example, can
   fluctuate from short entries like Ho to long ones like
   Wynglosphringfenster-Dundee y San Carlos Clemente. Like char data, varchar
   data must be enclosed in single or double quotation marks when it's input.
   You can use the LIKE keyword and wildcard characters with varchar, char,
   and datetime data.

 text
   A column of variable length that can hold up to 2,147,483,647 characters.
   The text data is stored on linked lists of data pages. When you initialize
   a text column with an INSERT, UPDATE, or WRITETEXT statement, SQL Server
   assigns a text pointer and allocates an entire data page, 2K bytes, to
   hold the text value.

   Text data can be converted to char, varchar, binary, and varbinary
   datatypes only.

   You can use the WRITETEXT statement to add text data without saving large
   blocks of text in your transaction log. See the SQL Server Language
   Reference for details.

 binary(n)
   A column that holds up to 255 bytes of fixed-length binary data. The
   binary datatypes such as bit patterns are meant to hold programming code
   or pictures.

   Specify the maximum length of the column with n. Storage size is n, no
   matter what the actual entry length is. Built-in string functions for
   concatenation and for finding substrings are provided for char, varchar,
   binary, and varbinary data. Binary columns (with fixed lengths) are
   accessed a little faster than varbinary columns (with variable lengths).
   They can use more storage space if the number of characters you enter in
   this column is less than the fixed length you have specified. Choose
   binary when you think the data entries in the column will be close to the
   same size. Data of type binary is specified with the characters 0 to 9 and
   A to F, and must be preceded with "0x" when it's input. For example, to
   input FF, type 0xFF.

 varbinary(n)
   A variable-length binary column that holds up to 255 bytes of
   variable-length binary data. The binary datatypes are not for hexadecimal
   data, but rather for bit patterns. They are used for storing programming
   code or pictures, not for hexadecimal numbers. Specify the maximum size of
   the columns with n. A varbinary column can contain 0 bytes, but n must be
   between 1 and 255. Storage size is the actual size of the data values
   entered, not n. Built-in string functions for concatenation and for
   finding substrings are provided for char, varchar, binary, and varbinary
   data. Since it stores values as variable lengths, choose varbinary when
   you expect variation in the size of binary data.

   Data of type varbinary consists of the characters 0 to 9 and A to F, and
   must be preceded with 0x when it's input.

 image
   A variable-length column that holds between 0 and 2,147,483,648 bytes of
   binary data. Like binary data, image data must be preceded with "0x" when
   it is input. The image data is stored on linked lists of data pages. When
   you initialize an image column with an INSERT or UPDATE statement, SQL
   Server assigns a text pointer and allocates an entire data page, 2K bytes,
   to hold the image value.

   Image data can be converted to char, varchar, binary, and varbinary
   datatypes only.

   You can use the WRITETEXT statement to add image date without saving large
   blocks of text in your transaction log. See the SQL Server Language
   Reference for details.

 bit
   A column that holds either 0 or 1. Integer values other than 0 or 1 are
   accepted but are always interpreted as 1. Storage size is 1 bit. Multiple
   bit types in a table can be collected into bytes. For example, 7 bit
   columns fit into 1 byte; 9 bit columns take 2 bytes. Columns of type bit
   cannot be NULL and cannot have indexes on them. The status column in the
   syscolumns system table indicates the unique offset position for bit
   columns. Use bit for true/false or yes/no types of data.

 money
   A column that stores dollar and cent values between
   ń922,337,203,685,447.5807 dollars with accuracy to a ten-thousandth of a
   dollar. Storage size is 8 bytes. All arithmetic operations and aggregate
   functions can be used with money data. Use money for all decimal currency
   entries. Data of type money must be preceded with a dollar sign ($) when
   it's input. If there is no dollar sign, SQL Server treats the value as a
   float. Depending on the exact input, SQL Server may not accept it or may
   lose some of the value's precision. The minus sign, representing negative
   money values, must be placed after the dollar sign. You cannot enter money
   values with commas, although the default print format for money data
   places a comma after every three digits. When money values are displayed,
   they are rounded up to the nearest cent. All the arithmetic operations
   except modulo are available with money.

 datetime
   A column that holds dates and times of day. Storage size is two 4-byte
   integers: 4 bytes for the number of days since the base date of January 1,
   1900, and 4 bytes for the number of milliseconds after midnight on that
   day. The maximum datetime value is the last day of the year 9999. SQL
   Server stores datetime data to an accuracy of .003 seconds, truncating to
   the nearest three-thousandth. In other words, the entries .001, .002, and
   .003 are stored as .000, while an entry of .004 is stored as .003. SQL
   Server rejects all values it cannot recognize as a date. The earliest
   permissible date is midnight, January 1, 1753.

   You can do some arithmetic calculations on datetime values with the
   built-in date functions. When you enter datetime values, always enclose
   them in single or double quotation marks. Case is always ignored and
   spaces can occur anywhere between date parts. The default display format
   for dates is "Apr 15 1987 10:23PM". SQL Server recognizes a wide variety
   of data entry formats for date and time values. The entry rules are
   discussed in Chapter 9, "Adding, Changing, and Removing Data." You can
   also use the CONVERT function for even more styles of date display.

 timestamp
   A column that is automatically updated when you use browse mode in a
   DB-LIBRARY application (see the SQL Server Language Reference). Every time
   a row containing a timestamp column is inserted or updated, the timestamp
   column is automatically updated. A table can have only one column of
   timestamp type. Its definition is varbinary (8) null.

   When a table is created that contains timestamp data, both the column and
   the datatype must be named "timestamp."


 Entering Character and Date Data

 Remember that you must enclose all character and date dataÄthe char,
 varchar, text, and datetime datatypesÄin single or double quotation marks
 when inputting it or searching for it.

 To specify literal quotes within a character entry, use two consecutive
 single or double quotation marks, or enclose a quote in the opposite kind of
 quotation mark. (For details and examples, see Chapter 2,"Querying
 Databases.")

 To continue a character string for more than one line on your screen when
 using the isql program, enter a backslash (\) before going to the following
 line.


 Converting between Datatypes

 Many conversions from one datatype to another are handled automatically by
 SQL Server. Others are available with the CONVERT function. See Chapter 5,
 "Using Built-In Functions," for more information on CONVERT.


 User-Defined Datatypes

 As a TRANSACT-SQL enhancement to SQL, you can name and design your own
 datatypes to supplement the system-supplied datatypes. A user datatype is
 defined in terms of system datatypes. You can give one name to a frequently
 used type definition. This makes it easy for you to custom fit datatypes to
 columns.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 To use a user-defined datatype in more than one database, create it in the
 model database. The user-defined datatype definition then becomes known to
 all new databases you create. See your System Administrator for more
 details.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 If you look at the table definition for titles given earlier in this
 chapter, you'll see that the title_id column has a tid datatype. This is a
 user-defined datatype based on a char(6).

 The advantage of user-defined datatypes is that you can bind rules and
 defaults to them for use in several tables. (Chapter 8, "Creating Defaults
 and Rules," explains defaults and rules.)

 The sp_addtype system procedure is used to create user datatypes. It takes
 as parameters the name of the user datatype being created, the SQL
 Server-supplied datatype from which it is being built, a length (if one is
 required for SQL Server datatype), and an optional NULL or NOT NULL
 specification.

 The sp_addtype system procedure has the following syntax:

   sp_addtype typename, phystype [(length)] [, nulltype]

 Here's how tid was defined:

   sp_addtype tid, "char(6)", "not null"

 You need to specify a length after the physical datatype only for char,
 varchar, binary, and varbinary. (Double or single quotation marks are
 required around procedure parameters only when the string includes a blank
 or some form of punctuation.) In this example, quotation marks are required
 around char(6) because of the parentheses and around not null because of the
 blank. They are not required around tid.

 If you don't specify NULL or NOT NULL, NOT NULL is assumed. The length
 specification cannot be changed when you include the user-defined datatype
 in a CREATE TABLE statement, but you can override the NULL/NOT NULL
 assignment.

 Once a user datatype is defined, it can be used as the datatype for any
 column in the database. For example, tid is used as the datatype for columns
 in several pubs tables: titles.title_id, titleauthor.title_id,
 sales.title_id, and roysched.title_id.

 A user-defined datatype, sysname, is distributed on the SQL Server
 distribution diskette and used in the system tables. Its definition is
 "varchar(30)" "NOT NULL."

 To drop a user-defined datatype, execute sp_droptype:

   sp_droptype typename

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 You cannot drop a type that is already in use in a table.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 To get a report on a user-defined datatype, execute sp_help. (For complete
 information on the system procedures, see the SQL Server Language
 Reference.)


 Null Values

 For each column, you can indicate whether null values are to be allowed. A
 null value is not the same as "zero" or "blank." NULL means no entry has
 been made and usually implies "value unknown" or "value inapplicable." It
 indicates that the user did not make any entry. For example, a null value in
 the price column of the titles table does not mean that the book is being
 given away free but that the price is not known or has not been set.

 If the user fails to make an entry in a column defined with the keyword
 NULL, SQL Server supplies the value NULL. A column defined with the keyword
 NULL also accepts an explicit entry of NULL from the user, no matter what
 datatype it is. However, be careful when you enter null values in character
 columns: if you put the value NULL inside single or double quotation marks,
 SQL Server interprets the entry as a character string rather than as the
 value NULL.

 In the CREATE TABLE statement, NOT NULL is the default. If neither the NULL
 nor NOT NULL keyword is specified, NOT NULL is assumed. In effect, you need
 to specify NULL whenever a column should accept a nonentry.

 For a column defined with the keywords NOT NULL (or without the NULL
 keyword), SQL Server insists on an entry. If there is no entry for a NOT
 NULL column, you'll get an error message.

 User-defined defaults (a value supplied automatically if no entry is made)
 can be connected with both NULL and NOT NULL columns. A default counts as an
 entry. (Defaults and their relationship to NULL columns are discussed in
 Chapter 8, "Creating Defaults and Rules.")

 Defining columns as NULL provides a placeholder for data you may not yet
 know. For example, in the titles table, price, advance, royalty, and
 ytd_sales are set up to allow NULL.

 However, title_id and title are not because the lack of an entry in these
 columns would be meaningless and confusing. A price without a title would
 make no sense, whereas a title without a price would simply mean the price
 had not been decided upon or was not available.

 In the CREATE TABLE statement, use the keywords NOT NULL (or simply omit the
 NULL keyword) when the information in the column is critical to the meaning
 of the other columns.


 Temporary Tables

 If you use the pound sign (#) before the name of the table in the CREATE
 TABLE statement, the new table is temporary. The new table is created in the
 temporary database, no matter what database you are using when you create
 it. It exists only during the current work session. A temporary table
 disappears either when it is destroyed (with a DROP TABLE statement) or at
 the end of the session, whichever comes first.

 If you do not use the pound sign before the table name, the table is created
 as a permanent table. A permanent table stays in the database until it is
 dropped by its owner.

 The following statement creates a temporary table:

   create table #myjobs
     (task char(30),
     start datetime,
     stop datetime,
     notes varchar(200))

 You can use this table to keep a list of today's chores and errands, along
 with a record of when you start and finish and any comments you may have.
 This table and its data will vanish at the end of the current work session.


 You cannot create a view on a temporary table, nor can you associate rules,
 defaults, or triggers with temporary tables. You can use a user-defined
 datatype when creating a temporary table only if that type is in
 tempdb..systypes.

 There are two ways to add a user-defined datatype (or any other object) to
 the tempdb database. To add an object for the current session only, execute
 sp_addtype while using tempdb. To add a user-defined datatype permanently,
 execute sp_addtype in the model database and then restart SQL Server so that
 model is copied to tempdb.


 Creating a Table in a Different Database

 As the CREATE TABLE syntax shows, you can create a table in a database other
 than the current one by qualifying the table name with the name of the other
 database. However, you must be an authorized user of the database in which
 you are creating the table, and you must have CREATE TABLE permission in it.


 If you are using pubs and there is another database called newpubs, you can
 create a table called newtab in newpubs like this:

   create table newpubs..newtab (col1 int, etc.)

 You cannot create other database objectsÄviews, rules, defaults, stored
 procedures, or triggersÄin a database other than the current one.


 Removing a Table

 Use the DROP TABLE statement to remove a table from a database. The DROP
 TABLE statement has the following syntax:

   DROP TABLE [[database.]owner.]table_name
    [, [[database.]owner.]table_name...]

 When you execute this statement, SQL Server removes the specified table(s)
 from the database, together with its contents and all the indexes and
 permissions associated with it. Rules or defaults bound to the table are no
 longer bound but are otherwise not affected.

 You must be the owner of a table to drop it. However, no one can drop a
 table while it is in useÄbeing read or written by a user or a front-end
 program. The DROP TABLE statement cannot be used on any of the system
 tables, either in the master database or in a user database.

 As the syntax indicates, you can drop a table in another database as long as
 you are the table owner.

 If you DELETE all the rows in a table or use the TRUNCATE TABLE statement on
 it, the table still exists until you drop it.

 DROP TABLE (and TRUNCATE TABLE) permission cannot be transferred to other
 users.


 Space Used

 You can find out how much space a table (or index) uses with the
 sp_spaceused system procedure. The sp_spaceused system procedure has the
 following syntax:

   sp_spaceused [objname]

 This procedure computes and displays the number of rows and data pages used
 by a table or index. Here's how you'd get a report on the space used by the
 titles table:

   sp_spaceused titles

    name  rows reserved  data index_size   unused
    ------  ---- --------  ---- ----------  ------
    titles  18  48 KB   6 KB 4 KB   38 KB

    (0 rows affected)

 If no object name is given as a parameter, a summary of space used by all
 database objects is displayed. (For complete information on the system
 procedures, see the SQL Server Language Reference.)


 Table Creation Process

 This section describes the steps you must take to create a practice table of
 your own. If you do not have CREATE TABLE permission, see your System
 Administrator or the owner of the database in which you're working.

 Creating a table usually implies creating indexes, defaults, and rules to go
 with it. Custom datatypes, triggers, and views are frequently involved, too.


 Of course, you can create a table, input some data, and work with it for a
 while before you create indexes, defaults, rules, triggers, or views. This
 gives you a chance to see what kind of transactions are most common and what
 kind of data is frequently entered.

 On the other hand, it's often most efficient to design a table and all the
 components that go with it at once. Here's an outline of the steps you go
 through. You might find it easiest to sketch your plans on paper before you
 actually create a table and its accompanying objects.


   ž   Decide what columns you need in the table and the datatype (and
       length, if required) for each.

   ž   Create any new user-defined datatypes before you define the table they
       are to be used in.

   ž   Decide which columns should accept null values and which should not.

   ž   Decide whether or not you need defaults and rules, and if so, where
       and what kind. Consider the relationship between the NULL/NOT NULL
       status of a column and defaults and rules.

   ž   Decide what kind of indexes you need and where. (Indexes are discussed
       later in this chapter.)

   ž   Create the table and its index(es) with the CREATE TABLE and CREATE
       INDEX statements.

   ž   Create new defaults and rules you need with the CREATE DEFAULT and
       CREATE RULE statements. (These statements are discussed in Chapter 8,
       "Creating Defaults and Rules.")

   ž   Bind any defaults and rules you need (new or old) with the
       sp_bindefault and sp_bindrule system procedures. If there were any
       defaults or rules on a user-defined datatype that you used in a CREATE
       TABLE statement, they are automatically in force. (These system
       procedures are discussed in Chapter 8, "Creating Defaults and Rules.")

   ž   Create triggers with the CREATE TRIGGER statement. (Triggers are
       discussed in Chapter 13, "Creating Triggers.")

   ž   Create views with the CREATE VIEW statement. (Views are discussed in
       Chapter 10, "Creating Views.")



 Sample Table

 This table is called friends_etc. It holds names, addresses, telephone
 numbers, and personal information about your friends.

 If you're planning to follow the examples and create all the objects that go
 with friends_etc yourself, check with your System Administrator or Database
 Owner. He or she will make sure that if the table has already been created
 by another user, it and the indexes, defaults, rules, and triggers that go
 with it have been dropped so that there will be no conflict when you create
 the objects.

 The following table shows the proposed structure of the table and the
 indexes, defaults, and rules that will go with each column.

ÖŚÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄ
 Column   Datatype         Null/Not Null    Index                   Default
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 pname    nm               NOT NULL         nmind
                                            (composite)

 sname    nm               NOT NULL         nmind
                                            (composite)

 address  varchar(30)      NULL

 city     varchar(30)      NOT NULL                                 citydflt

 state    char(2)          NOT NULL                                 statedflt

 zip      char(5)          NULL             zipind                  zipdflt

 phone    p#               NULL
 Column   Datatype         Null/Not Null    Index                   Default
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
phone    p#               NULL

 age      tinyint          NULL

 bday     datetime         NOT NULL                                 bdflt

 sex      bit              NOT NULL                                 sexdflt

 debt     money            NOT NULL                                 debtdflt

 notes    varchar(255)     NULL

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ




 Datatypes

 The first two columns are for personal name and surname. They are defined as
 nm datatype. Before you create the table, you need to create the datatype.
 The same is true of the p# datatype for phone.

 The nm datatype allows for a variable-length character entry with a maximum
 of 30 characters. The p# datatype allows for a char datatype with a
 fixed-length of 10 characters.

 Enter the user datatype definitions for nm and p# like this:

   execute sp_addtype nm, "varchar(30)"

    execute sp_addtype p#, "char(10)"


 Null Values

 Except for columns that are assigned user-defined datatypes, each column has
 an explicit NULL or NOT NULL entry. Recall that you don't need to specify
 NOT NULL in the table definition because it is the default. This table
 design specifies NOT NULL for readability.

 The NOT NULL default means that some entry is required, for example, for the
 two name columns in this table. The other data is meaningless without the
 names. In addition, the sex column must be NOT NULL because you cannot use
 NULL with bit datatypes.

 If a column is designated NULL and a default is bound to it, the default
 value (rather than NULL) is entered when no other value is given on input.
 Binding a rule to a column, on the other hand, has no effect on the way null
 values are handled. This is because a rule does not input any data; rather,
 it checks whatever is entered to make sure it conforms to the limits given
 in the rule. (Columns can have both defaults and rules. The relationship
 between these two is discussed in Chapter 8, "Creating Defaults and Rules.")



 Table Definition

 You are ready to write the CREATE TABLE statement:

   create table friends_etc
     (pname nm not null,
     sname nm not null,
     address varchar(30) null,
     city varchar(30) not null,
     state char(2) not null,
     zip char(5) null,
     phone p# null,
     age tinyint null,
     bday datetime not null,
     sex bit not null,
     debt money not null,
     notes varchar(255) null)

 You have columns defined for personal name and surname, address, city,
 state, zip code, telephone number, age, birthday, sex, debt information, and
 notes. The following section shows how to create indexes. (Defaults and
 rules are discussed in Chapter 8, "Creating Defaults and Rules.")


 Changing an Existing Table

 If you change your mind about a table's structure after you have used it for
 a while and decide you need to modify the way the table is put together, you
 have these options:


   ž   You can add columns (except bit datatype columns) with the ALTER TABLE
       statement.

   ž   You can change the name of a table, a column, or any other database
       object with the sp_rename system procedure.



 Adding Columns: ALTER TABLE

 You can add new columns (except columns with the bit datatype) to a table at
 any time with the ALTER TABLE statement.

 The ALTER TABLE statement has the following syntax:

   ALTER TABLE [[database.]owner.]table_name
    ADD column_name datatype NULL [, column_name datatype NULL...]

 For example, to add a column to the friends_etc table, type

   alter table friends_etc
    add first_met datetime NULL

 The number of columns in a table cannot exceed 250, whether they are added
 with an ALTER TABLE statement or defined with the original CREATE TABLE
 statement.

 Columns added with the ALTER TABLE statement must allow null values because
 when the new column is added to the existing rows, there must be some value
 for it.


 Renaming Tables and Other Objects

 To rename tables and other database objectsÄviews, indexes, rules, defaults,
 procedures, and triggersÄuse the sp_rename stored procedure. You must be the
 owner of an object to rename it.

 The sp_rename system procedure has the following syntax:

   sp_rename objname, newname

 For example, to change the name of friends_etc to infotable, type

   sp_rename friends_etc, infotable

 You can use sp_rename to rename other objects as well: columns, defaults,
 rules, procedures, views, triggers, and user datatypes. If you are renaming
 a column, use the form "table.column".

 Here's how you'd change the name of the user datatype tid to t_id:

   exec sp_rename tid, "t_id"

 You cannot change the name of databases, system datatypes, or most system
 objects. The object whose name you are changing must be in the current
 database.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 WARNING

 Although it is possible to rename system procedures and the model and master
 databases, doing so will cause serious system problems.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 A user can change the names only of those objects he or she owns. The
 Database Owner can change the name of any user's objects.

 Procedures, triggers, and views that depend on an object whose name has been
 changed work fine until they are recompiled. However, recompilation takes
 place for many reasons and without notification to the user. When the
 procedure, trigger, or view is recompiled by SQL Server, it will no longer
 work. The user must change its text to reflect the new name of the object on
 which it depends. The safest course is to change the definitions of any
 dependent objects when you execute sp_rename.


 Indexes

 Indexes are created on column(s) to speed retrieval of data. A simplified
 syntax of the CREATE INDEX statement is

   CREATE INDEX index_name
    ON table_name (column_name)

 To create an index on the au_id column of the authors table, type

   create index au_id_ind
    on authors(au_id)

 The index name must conform to the rules for identifiers. The column and
 table name specify the column you want indexed and the table that contains
 it.

 You must be the owner of a table to CREATE (or DROP) an index. The owner of
 a table can CREATE (or DROP) an index at any time, whether or not there is
 data in the table. Indexes can be created on tables in another database by
 qualifying the table name. Columns of the bit datatype cannot be indexed.

 The complete syntax of the CREATE INDEX statement is

   CREATE [UNIQUE] [CLUSTERED | NONCLUSTERED]
    INDEX index_name
    ON [[database.]owner.]table_name (column_name [, column_name...])
    [WITH {FILLFACTOR = x , IGNORE_DUP_KEY ,
    [IGNORE_DUP_ROW | ALLOW_DUP_ROW]}]

 The following subsections explain the various options of the CREATE INDEX
 statement.


 How, What, and Why to Index

 Indexes speed the retrieval of data. Putting an index on a column often
 makes the difference between a nearly immediate response to a query and a
 long wait.

 So why not index every column? The most significant reason is that building
 an index takes time and disk space.

 Note that nonclustered indexes are automatically re-created when a clustered
 index is rebuilt.

 A second reason is that inserting, deleting, or updating data in indexed
 columns takes a little longer than in unindexed columns. But this cost is
 usually outweighed by the extent to which indexes improve retrieval
 performance.

 Here are some general guidelines on when to index:


   ž   A column that is often accessed in sorted order (that is, specified in
       the ORDER BY clause) probably should be indexed so that SQL Server can
       take advantage of the indexed order.

   ž   Columns that are regularly used in joins should always be indexed
       since the system can perform the join faster if the columns are in
       sorted order.

   ž   The column that stores the primary key of the table often has a
       clustered index, especially if it is frequently joined to columns in
       other tables. (There is only one clustered index per table. See
       "Clustered and Nonclustered Indexes.")

   ž   A column that is often searched for ranges of values might be a good
       choice for a clustered index. Once the row with the first value in the
       range is found, rows with subsequent values are guaranteed to be
       physically adjacent. A clustered index does not offer as much of an
       advantage for searches on single values.


 There are some cases where indexes are not useful:


   ž   Columns that are seldom or never referenced in queries don't benefit
       from indexes since the system seldom or never has to search for rows
       on the basis of values in these columns.

   ž   Columns that can have only two or three values (for example, male,
       female, or unknown) get no real advantage from indexes.


 If the system does have to search an unindexed column, it does so by looking
 at the rows one by one. The length of time it takes to perform this kind of
 scan is directly proportional to the number of rows in the table.


 Indexing More Than One Column: Composite Indexes

 You can specify two or more column names if you want to create a composite
 index on the combined values in all the specified columns.

 Composite indexes are used when two or more columns are best searched as a
 unit. For example, the friends_etc table has a composite index on pname and
 sname. List all the columns to be included in the composite index (in
 sort-priority order) inside the parentheses after the table name, like this:


   create index nmind
    on friends_etc(pname, sname)

 The columns in a composite index don't have to be in the same order as the
 columns in the CREATE TABLE statement. The order of pname and sname could be
 reversed in the preceding index creation statement.

 Up to 16 columns can be combined into a single composite index. All the
 columns in a composite index must be in the same table. The maximum
 allowable size of the combined index values is 256 bytes. (That is, the sum
 of the lengths of the columns that make up the composite index cannot exceed
 256.)


 The UNIQUE Keyword

 A unique index is one in which no two rows are permitted to have the same
 index value. The system checks for duplicate values when the index is
 created (if data already exists) and checks each time data is added with an
 INSERT or UPDATE statement.

 Specifying a unique index makes sense only when uniqueness is a
 characteristic of the data itself. For example, you would not want a unique
 index on a last_name column since there is likely to be more than one Smith
 or Wong in tables of even a few hundred rows.

 On the other hand, a unique index on a column holding social security
 numbers is a good idea. Uniqueness is a characteristic of the dataÄeach
 person has a different social security number. Furthermore, a unique index
 serves as an integrity check: a duplicate social security number probably
 reflects some kind of error in data entry or on the part of the government.


 If you try to create a unique index on existing data that includes duplicate
 values, the statement is aborted, and SQL Server displays an error message
 that gives the first duplicate.

 If you try to change data on which there is a unique index in such a way as
 to generate duplicate values (either with an INSERT or UPDATE statement),
 the entire transaction is rolled back and SQL Server displays an error
 message. This is true even if the transaction would have changed many rows
 but would have caused only one duplicate.

 You can use the UNIQUE keyword on composite indexes. This was not done for
 the friends_etc index we just created.


 Clustered and Nonclustered Indexes

 With a clustered index, SQL Server sorts rows on an ongoing basis so that
 their physical order is the same as their logical (indexed) order. The
 bottom, or leaf level, of a clustered index contains the actual data pages
 of the table. Create the clustered index before creating any nonclustered
 indexes, since nonclustered indexes are automatically rebuilt when a
 clustered index is created.

 By definition, there can be only one clustered index per table. It is often
 created on the primary keyÄthe column or columns that uniquely identify the
 row.

 Logically, a primary key is determined by the design of the database.
 However, you can define primary keys, foreign keys, and common keys (pairs
 of keys that are frequently joined) with the sp_primarykey, sp_foreignkey,
 and sp_commonkey system procedures. You can display information about keys
 with sp_helpkey and about frequently joined columns with sp_helpjoins. (For
 a definition of primary and foreign key, see Chapter 13, "Creating
 Triggers." For complete information on the system procedures, see the SQL
 Server Language Reference.)

 With a nonclustered index, the physical order of the rows is not the same as
 their indexed order. The leaf level of a nonclustered index contains
 pointers to rows on data pages. More precisely, each leaf page contains an
 indexed value and a pointer to the row with that value. In other words, a
 nonclustered index has an extra level between the index structure and the
 data itself.

 You can have up to 250 nonclustered indexes per table; however, you cannot
 exceed a total of 250 indexes, both clustered and nonclustered.

 Using a clustered index to find data is almost always faster than using a
 nonclustered index. In addition, a clustered index is advantageous when many
 rows with contiguous key values are being retrievedÄthat is, on columns that
 are often searched for ranges of values. Once the row with the first key
 value is found, rows with subsequent indexed values are guaranteed to be
 physically adjacent, and no further disk accesses are necessary.

 If neither the CLUSTERED nor the NONCLUSTERED keyword is used, a
 nonclustered index is created.

 The following example shows how the index on the title_id column of the
 titles table is created. (If you want to try this statement, you must drop
 the index first with DROP INDEX.)

   create clustered index titleidind
    on titles(title_id)

 Since you think you'll often want to sort the people in friends_etc by zip
 code, you should create a nonclustered index on the zip column, like this:

   create nonclustered index zipind
    on friends_etc(zip)

 A unique index would not make sense here since some of your contacts are
 likely to have the same zip code. A clustered index wouldn't be appropriate
 since the zip code is not the primary key.

 The clustered index in friends_etc should be a composite index on the
 personal name and surname columns. To create this clustered index, you must
 drop the nmind index first:

   drop index friends_etc.nmind

 Then create the clustered index:

   create clustered index nmind
    on friends_etc(pname, sname)


 The FILLFACTOR Option

 With the FILLFACTOR option, the user can specify how full SQL Server should
 make each index page. The amount of empty space on an index page is a matter
 of concern because when an index page fills up (after enough rows are
 added), the system must take the time to split it to make room for new rows.


 It is seldom necessary to include the FILLFACTOR option in your CREATE INDEX
 statement. This option is provided for fine-tuning of performance. It is
 useful only when you are creating a new index on existing data, and even
 then only when you can accurately predict future changes in that data.

 Allowable FILLFACTOR values are between 0 and 100. The default is 0. The
 System Administrator can change the default with the sp_configure system
 procedure. (See the SQL Server System Administrator's Guide.)

 Here's a CREATE INDEX statement that uses the FILLFACTOR option:

   create nonclustered index zip_ind
    on authors(zip)
    with fillfactor = 100

 A FILLFACTOR of 100 fills every page completely and makes sense only when
 you know that no index values in the table will ever change.


 The IGNORE_DUP_KEY Option

 The IGNORE_DUP_KEY option is used very infrequently. Its most common
 function is to sort through a batch of data that includes errors you want to
 eliminate.

 IGNORE_DUP_KEY controls what happens when you attempt to enter duplicate
 keys in a unique clustered index. The phrase is not allowed with a
 nonclustered index. IGNORE_DUP_KEY is meaningful only when the UPDATE or
 INSERT that would generate the duplicate affects multiple rows.

 If IGNORE_DUP_KEY is not set and an attempt is made to enter data that would
 create duplicate keys, the entire UPDATE or INSERT statement failsÄif the
 statement would have affected more than one row, no rows are changed. (If
 there are other statements in the same transaction, they execute normally.)


 If IGNORE_DUP_KEY is set and you give an UPDATE or INSERT statement that
 would create duplicate keys, the row that would cause duplicate keys is not
 added or changedÄin fact, in the case of UPDATE, the row is discarded. (See
 the warning below.) Other changes to the database caused by the UPDATE or
 INSERT attempt (for example, changes to index pages) are also backed out.
 However, if the UPDATE or INSERT affects multiple rows, the other rows are
 added or changed as usual.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 WARNING

 With IGNORE_DUP_KEY set, if you try to update a row in such a way that a
 duplicate key would be created, that row is discarded. Neither the new value
 nor the original value of the row that would have produced the duplicate
 exists in the updated table. For example, if you try to update "Smith" to
 "Jones" and "Jones" already exists, you wind up with one "Jones" and no
 "Smith." The disappearance of the row for "Smith" is unavoidable because an
 UPDATE operation is actually a DELETE followed by an INSERT. SQL Server has
 no way to know about the disallowed duplicate when it deletes the row, and
 the whole transaction can't be rolled back because the point of
 IGNORE_DUP_KEY (and of the IGNORE_DUP_ROW option) is to allow a transaction
 to proceed despite the presence of duplicates.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 You cannot create a unique index on a column that already includes duplicate
 values, whether or not IGNORE_DUP_KEY is set. If you attempt to do so, SQL
 Server displays an error message and a list of the duplicate values. You
 must eliminate duplicates before you create a unique index on the column.

 Here's an example of using the IGNORE_DUP_KEY option:

   create unique clustered index stor_id_ind
    on stores(stor_id)
    with ignore_dup_key


 The IGNORE_DUP_ROW and ALLOW_DUP_ROW Options

 IGNORE_DUP_ROW and ALLOW_DUP_ROW are allowed only with a nonunique clustered
 index. (From the point of view of a nonclustered index, there are never any
 duplicate rowsÄeven for identical data valuesÄbecause nonclustered indexes
 include a unique row identification number along with the index value.)

 Only one or the other of the IGNORE_DUP_ROW and ALLOW_DUP_ROW options can be
 set.

 The ALLOW_DUP_ROW option allows duplicate rows in a table. If ALLOW_DUP_ROW
 is set, you can create a new clustered index on a table that includes
 duplicate rows, and you can insert or update rows so that they would
 duplicate already existing rows.

 If ALLOW_DUP_ROW is not set, the handling of duplicate rows depends on
 whether IGNORE_DUP_ROW is set. You cannot set ALLOW_DUP_ROW if
 IGNORE_DUP_ROW is set.

 If any index in the table is unique, the requirement for uniquenessÄthe most
 stringent requirementÄtakes precedence over the ALLOW_DUP_ROW option. Thus,
 ALLOW_DUP_ROW applies only to tables with nonunique indexes: you cannot use
 this keyword if a unique clustered index exists on any column in the table.


 (However, you might create a table with a nonunique clustered index and the
 ALLOW_DUP_ROW option, and then create a unique nonclustered index on the
 same table. If at that time the existing data does not include any duplicate
 rows, the CREATE UNIQUE NONCLUSTERED INDEX statement will succeed, but
 subsequent attempts to enter duplicate rows will fail because of the
 requirement for uniqueness on the new nonclustered index. If duplicate rows
 do exist when you attempt to create a unique nonclustered index, the
 statement fails.)

 The IGNORE_DUP_ROW option (like IGNORE_DUP_KEY) is used infrequently. Its
 most common function is to sort through a batch of data that includes
 duplicates you wish to eliminate.

 IGNORE_DUP_ROW controls what happens when you attempt to enter duplicate
 rows into a table in which they're not allowed. IGNORE_DUP_ROW applies only
 to tables with nonunique indexes: you cannot use this keyword if a unique
 index exists on any column in the table. If ALLOW_DUP_ROW is set,
 IGNORE_DUP_ROW is meaningless.

 If IGNORE_DUP_ROW is not set (and ALLOW_DUP_ROW is not set) and you try to
 insert a duplicate row, the entire statement fails. (If there are other
 statements in the same transaction, they are executed normally.)

 If IGNORE_DUP_ROW is set, the row that would duplicate another row is not
 added or changed, and you get an error message. Changes to the database
 caused by the attempt to duplicate a row (for example, changes to index
 pages) are also backed out. Other rows are added or changed, or the rest of
 the index is created as usual.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 WARNING

 The same warning about discarded rows applies to IGNORE_DUP_ROW with UPDATE
 as to IGNORE_DUP_KEY.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 In the default situation (neither ALLOW_DUP_ROW nor IGNORE_DUP_ROW is set),
 attempting to insert duplicate rows or create a unique clustered index on a
 table with duplicate rows causes the entire statement to fail.

 Here's an example:

   create unique clustered index salesind
    on sales(stor_id)

 This statement fails on the duplicate key.

 This table illustrates how ALLOW_DUP_ROW and IGNORE_DUP_ROW affect attempts
 to create a nonunique index on a table that includes duplicate rows and to
 enter duplicate rows into a table.

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Option              Has Duplicates                Enter Duplicates
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Neither option set  CREATE INDEX statement        Enter duplicate rows
                     fails.                        statement fails.
 Option              Has Duplicates                Enter Duplicates
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                    fails.                        statement fails.

 ALLOW_DUP_ROW set   Command is completed.         Command is completed.

 IGNORE_DUP_ROW set  Index created but duplicate   All rows accepted except
                     rows thrown out; error        duplicates; error message.
                     message.                      See earlier warning.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ




 Help on Indexes

 To see the indexes that exist on a table, you can use the sp_helpindex
 system procedure. Here's a report on the authors table:

   sp_helpindex authors

    index_name   index_description   index_keys
    ----------   -----------------   ------------------
    auidind    clustered,unique   au_id
    aunmind    nonclustered    au_lname, au_fname

    (2 rows affected)

 The sp_help system procedure also reports indexes on a table.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 WARNING

 Only eight indexes are displayed when you execute sp_helpindex. The
 sysindexes system table contains information on all indexes.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Removing an Index: DROP INDEX

 The DROP INDEX statement is used to remove an index from the database. The
 DROP INDEX statement has the following syntax:

   DROP INDEX table_name.index_name [, table_name.index_name...]

 When you execute this statement, SQL Server removes the specified index(es)
 from the database, reclaiming its storage space.

 Only the owner of the index can drop it. DROP INDEX permission cannot be
 transferred to other users. The DROP INDEX statement cannot be used on any
 of the system tables in the master database or in the user database.

 You might want to drop an index if it is not used for most or all of your
 queries.

 To drop the index auidind in the authors table, type

   drop index authors.auidind






 Chapter 8  Creating Defaults and Rules
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 A default is a value that SQL Server inserts into a column if the user does
 not explicitly enter one. A rule specifies what you are or are not allowed
 to enter in a particular column or in any column with a given user-defined
 datatype.

 This chapter explains how to create and drop defaults and rules, and how to
 associate defaults and rules with a column or with a user-defined datatype,
 using the sp_bindefault, sp_bindrule, sp_unbindefault, and sp_unbindrule
 system procedures.


 Defaults

 In a relational database management system, every data element (that is, a
 particular column in a particular row) must contain some valueÄeven if that
 value is NULL. As discussed in the last chapter, some columns do not accept
 the null value. Some other value must be entered, either a value entered by
 the user or a default entered by SQL Server.

 Defaults allow you to specify a value that SQL Server inserts if no value is
 entered (in either a NULL or NOT NULL column). For example, you can create a
 default that has the value "???" or the value "fill in later", and then
 instruct SQL Server to enter it if a user does not make an entry.

 Defaults can be created or dropped at any time, before or after data has
 been entered in a table. A default is created with the CREATE DEFAULT
 statement and dropped with the DROP DEFAULT statement.

 A default can be connected to a particular column, to a number of columns,
 or to all the columns in the database having a given user-defined datatype.
 A default is associated with a column or user-defined datatype with the
 sp_bindefault system procedure. The association is removed with the
 sp_unbindefault system procedure.

 Here are some things you should check when you are creating and binding
 defaults:


   ž   Make sure the column is large enough for the default. A char(2) column
       will not hold a 17-character string like "Nobody knows yet."

   ž   Be careful when you put different defaults on a user datatype and on
       an individual column of that type. If you bind the user datatype
       default first and then the column default, the column default replaces
       the user datatype default for the named column only. The user datatype
       default is bound to all the other columns having that datatype.
       However, once you bind another default to a column that had a default
       because of its type, that column ceases to be influenced by defaults
       bound to its datatype. This issue is discussed in more detail later in
       this chapter.

   ž   Watch out for conflicts between defaults and rules. Be sure that the
       default value is allowed by the rule; otherwise, the default can be
       eliminated by the rule.

       If a rule allows entries between 1 and 100, for example, and the
       default is set to 0, the rule will force the default entry to be
       rejected every time and you'll get an error (unless the column accepts
       NULL, in which case "NULL" will be entered). Either the default or the
       rule will have to be changed.



 Creating Defaults

 The CREATE DEFAULT statement has the following syntax:

   CREATE DEFAULT [owner.]default_name
    AS constant_expression

 Default names must follow the rules for identifiers. You can create a
 default in the current database only.

 Within a database, default names must be unique. You cannot create two
 defaults called phonedflt. However, as guest you can create a phonedflt even
 if dbo.phonedflt already exists because the owner names make each one
 distinct.

 The next example shows how to create a default value of "Oakland" to use
 with the city column of friends_etc (the table whose creation was discussed
 in Chapter 7, "Creating Databases, Tables, and Indexes") and possibly with
 other columns or user datatypes. (As you continue to follow this example,
 you can use any city name that works for the demographic distribution of the
 people you're planning to enter in your personal table.) To create the
 default, type

   create default citydflt
    as "Oakland"

 After the AS keyword, you can use any constant. Enclose character and date
 constants in quotation marks; money, integer, and floating-point constants
 do not require them. Binary data must be preceded with "0x", and money data
 must be preceded by a dollar sign ($). The default value must be compatible
 with the datatype of the column. For example, you cannot use "none" as a
 default for a numeric column, but "0" (zero) is appropriate.

 If you specify NOT NULL when you create a column and do not associate a
 default with it, SQL Server will produce an error message whenever anyone
 fails to make an entry in that column.

 Often, default values are created when a table is created. However, in a
 session in which you want to enter many rows having the same values in one
 or more columns, it may be convenient to create a default tailored to that
 session before you begin.


 Binding Defaults

 After you've created a default, use the sp_bindefault system procedure to
 bind the default to a column or user-defined datatype.

 The default applies to new rows only. It does not change already existing
 rows. It takes effect only when no entry is made. If the user supplies a
 value for the column, the default has no effect.

 Here's how to bind citydflt to the city column in friends_etc:

   sp_bindefault citydflt, "friends_etc.city"

 Notice that the table and column name are enclosed in quotation marks
 because of the embedded punctuation (the period).

 You can't bind a default to a system datatype (the target would be too
 broad), but you can bind it to a user-defined datatype.

 If you create a special datatype for all city columns in every table in your
 database, you can bind citydflt to that datatype, and "Oakland" will show up
 only where city names are appropriate. For example, if the user datatype is
 called citytype, here's how you'd bind citydflt to it:

   sp_bindefault citydflt, citytype

 Here's how you create and bind a default for the state column of
 friends_etc:

   create default statedflt
    as "CA"

    sp_bindefault statedflt, "friends_etc.state"

 If most of the people in your table live in the same zip code area, you can
 create a default to save data-entry time. Here's one that's appropriate for
 a section of Oakland along with its binding:

   create default zipdflt
    as "94609"

    sp_bindefault zipdflt, "friends_etc.zip"

 The sp_bindefault system procedure has the following syntax:

   sp_bindefault defname, objname, [, futureonly]

 The defname is the name of the default created with CREATE DEFAULT. The
 objname is the name of the table and column, or of the user-defined datatype
 to which the default is to be bound. If the parameter is not of the form
 "table.column", it is assumed to be a user datatype.

 The default must be compatible with the datatype of the column. For example,
 you cannot use "N/A" as a default for a numeric column. If the default is
 not compatible with the column to which you've bound it, SQL Server will
 generate an error message when it tries to insert the default value (not
 when you bind it).

 All columns of a specified user datatype become associated with the
 specified default unless


   ž   You use the optional third parameter, futureonly, which prevents
       existing columns of that user datatype from inheriting the default

   ž   The column's default has previously been changed, in which case the
       changed default is maintained


 For example, you create a table called foes with a column named city of type
 citytype (a user-defined datatype). Initially, the user-defined datatype
 citytype has no default. After creating a default called citydflt, you bind
 it to foes.city. Now you bind another default, newcitydflt, to the user
 datatype citytype. Although foes.city is a citytype column, the new default
 does not bind to it since its default has previously been changed.


 Unbinding Defaults

 Unbinding a default means disconnecting it from a particular column or
 user-defined datatype. An unbound default is still stored in the database
 and is available for future use.

 There are two ways to unbind a default:


   ž   Use the sp_unbindefault system procedure to remove the binding between
       a default and a column or datatype.

   ž   Use the sp_bindefault system procedure to bind a new default to that
       column or datatype. The old one is automatically unbound.


 Here's how you unbind the current default from the city column of the
 friends_etc table:

   execute sp_unbindefault "friends_etc.city"

 At this point, the default still exists, but it has no effect on the city
 column because it isn't connected to that column.

 To unbind a default from the user-defined datatype nm, type

   sp_unbindefault nm

 The sp_unbindefault system procedure has the following syntax:

   sp_unbindefault objname [, futureonly]

 If the objname parameter you supply is not of the form "table.column", SQL
 Server assumes it is a user-defined datatype.

 When you unbind a default from a user-defined datatype, the default is
 unbound from all columns of that type unless:


   ž   You give the optional second parameter, futureonly, which prevents
       existing columns of that datatype from losing their binding with the
       default

   ž   The default on a column of that user-defined datatype has been changed
       so that its current value is different from the default being unbound


 Here's an example that demonstrates the second case:


   1.  Create a user-defined datatype called nm.

   2.  Use nm in the CREATE statements for the tables friends_etc and enemies
       to create the columns friends_etc.pname, friends_etc.sname, and
       enemies.nickname.

   3.  Create a default called nmdflt and bind it to nm.

   4.  Change the default on enemies.nickname by creating a new default
       called nastydflt and binding it to enemies.nickname.

   5.  Now, if you unbind nmdflt from nm, only friends.pname and
       friends.sname are affected. Since the original default on
       enemies.nickname has been changed, that column's default is not
       unbound even though it is defined as type nm.



 Dropping Defaults

 If you want to remove a default from the database entirely, use the DROP
 DEFAULT statement. The default must be unbound from all columns and user
 datatypes before you can drop it. If you try to drop a default that is still
 bound, SQL Server displays an error message, and the DROP DEFAULT statement
 fails. However, you need not unbind and then drop a default to bind a new
 one. Simply bind another default in its place.

 Here's how to remove citydflt:

   drop default citydflt

 The DROP DEFAULT statement has the following syntax:

   DROP DEFAULT [owner.]default_name
    [, [owner.]default_name...]

 A default can be dropped only by its owner.


 Defaults and Null Values

 If you specify NOT NULL when you create a column and do not create a default
 for it, SQL Server will produce an error message whenever anyone inserts a
 row and fails to make an entry in that column.

 When you drop a default for a NULL column, NULL will from then on be
 inserted in that position by SQL Server when you add rows without entering
 any value for that column. When you drop a default for a NOT NULL column,
 you'll get an error message when rows are added but no value for that column
 is entered.

 The following table illustrates the relationship between the existence of a
 default and the definition of a column as NULL or NOT NULL. The entries in
 the table show the result:

ÖŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
           No entry
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NULL      null      default  null   null

 NOT NULL  error     default  error  error

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ




 Rules

 Rules are integrity constraints that go beyond those implied by a column's
 datatype. They can be connected to a specific column, to several specific
 columns, or to all the columns in a database with a specified user-defined
 datatype.

 Every time a user enters a value (with an INSERT or UPDATE statement), SQL
 Server checks it against any rules that have been created for the specified
 column. Data entered prior to the creation and binding of a rule is not
 checked.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 You can bind a character type rule to a numeric type column even though it
 makes no sense to do so. Rules are checked when an INSERT or UPDATE is
 attempted, not at the time of binding.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Rules are created with the CREATE RULE statement, and then bound to a column
 or user datatype with the sp_bindrule system procedure.


 Creating Rules

 The CREATE RULE statement has the following syntax:

   CREATE RULE [owner.]rule_name
    AS condition_expression

 Rule names must follow the rules for identifiers. You can create a rule in
 the current database only.

 Within a database, rule names must be unique for each user. A user cannot
 create two rules called socsecrule. However, two different users can create
 a rule named socsecrule because the owner names make each one distinct.

 Here's how the rule permitting five different pub_id numbers and one dummy
 value (99 followed by any two digits) was created:

   create rule pubidrule
    as @pub_id in("1389", "0736", "0877", "1622", "1756")
    or @pub_id like "99[0-9][0-9]"

 The AS clause contains the name of the rule's parameter (prefixed with @)
 and the definition of the rule itself. The parameter refers to the column
 value that is affected by the UPDATE or INSERT statement.

 In the preceding example, the parameter is @pub_id, a convenient name since
 this rule is to be bound to the pub_id column. You can use any name for the
 parameter, but the first character must be "@." Using the name of the column
 or datatype to which the rule will be bound can help you remember what it is
 for.

 The rule definition can contain any expression that is valid in a WHERE
 clause and can include arithmetic operators, comparison operators, LIKE, IN,
 BETWEEN, and so on. However, it cannot reference any column or other
 database object directly. Built-in functions that do not reference database
 objects can be included. (See the SQL Server Language Reference for a full
 definition of WHERE syntax.)


 Binding Rules

 After you've created a rule, use the sp_bindrule system procedure to link
 the rule to a column or user-defined datatype.

 This is how pubidrule was bound to publishers.pub_id:

   sp_bindrule pubidrule, "publishers.pub_id"

 Here's a rule to use when you are sure all the zip codes you enter will have
 946 as their first three digits:

   create rule ziprule946
    as @zip like "946[0-9][0-9]"

 Bind it to the zip column in friends_etc like this:

   sp_bindrule ziprule946, "friends_etc.zip"

 The following example creates a rule that forces the values you enter to
 comply with a particular format. In this case, each value entered in the
 column must begin with the digits "415" followed by seven more characters:

   create rule phonerule
    as @phone like '415_______'

 You can't bind a rule to a system datatype, but you can bind one to a
 user-defined datatype. To bind phonerule to a user-defined datatype called
 p#, type

   sp_bindrule phonerule, "p#"

 To make sure that the ages you enter for your friends are between 1 and 120
 but never 17, type

   create rule agerule
    as @age between 1 and 120 and @age !=17

 When you are entering data that requires special constraints on some
 columns, you can unbind any existing rules for those columns and create new
 rules to help check the data. For example, suppose that you are adding data
 to the debt column of the friends_etc table. You know that all the debts you
 want to record today are between $5 and $200. To avoid accidentally typing
 an amount outside these limits, create a rule like this one. (It allows for
 an entry of $0.00 because that is the default for this column.)

   create rule debtrule
    as @debt = $0.00 or @debt between $5.00 and $200.00

 Bind it to the debt column like this:

   sp_bindrule debtrule, "friends_etc.debt"

 The sp_bindrule system procedure has the following syntax:

   sp_bindrule rulename, objname [, futureonly]

 The rulename is the name of the rule created with CREATE RULE. The objname
 is the name of the table and column, or of the user-defined datatype to
 which the rule is to be bound. If the parameter is not of the form
 "table.column", it is assumed to be a user datatype.

 All columns of a specified user datatype become associated with the
 specified rule unless you use the optional third parameter, futureonly,
 which prevents existing columns of that user datatype from inheriting the
 rule, or unless their defaults have previously been changed. (See the
 "Defaults" section for examples of this second situation.)


 Unbinding Rules

 Unbinding a rule means disconnecting it from a particular column or
 user-defined datatype. An unbound rule's definition is still stored in the
 database and is available for future use.

 There are two ways to unbind a rule:


   ž   Use the sp_unbindrule system procedure to remove the binding between a
       rule and a column or datatype.

   ž   Use the sp_bindrule system procedure to bind a new rule to that column
       or datatype. The old one is automatically unbound.


 Here's how you'd disassociate debtrule or any other currently bound rule
 from friends_etc.debt:

   sp_unbindrule "friends_etc.debt"

 The rule is still in the database but has no connection to friends_etc.debt.


 To unbind a rule from the user-defined datatype p#, type

   sp_unbindrule "p#"

 The sp_unbindrule system procedure has the following syntax:

   sp_unbindrule objname [, futureonly]

 If the objname parameter you give is not of the form "table.column", SQL
 Server assumes it is a user-defined datatype.

 As is the case with defaults, when you unbind a rule from a user-defined
 datatype, the rule is unbound from all columns of that type unless


   ž   You give the optional second parameter, futureonly, which prevents
       existing columns of that datatype from losing their binding with the
       rule

   ž   The rule on a column of that user-defined datatype has been changed so
       that its current value is different from the rule being unbound



 Dropping Rules

 If you want to remove a rule from the database entirely, use the DROP RULE
 statement. The rule must be unbound from all columns and user datatypes
 before you can drop it. If you try to drop a rule that is still bound, SQL
 Server displays an error message and the DROP RULE statement fails. However,
 you need not unbind and then drop a rule to bind a new one. Simply bind a
 new one in its place.

 Here's how to remove phonerule:

   drop rule phonerule

 The DROP RULE statement has the following syntax:

   DROP RULE [owner.]rule_name
    [, [owner.]rule_name...]

 After you drop a rule, new data entered into the columns that were
 previously governed by it goes in without these constraints. Already
 existing data is not affected in any way.

 A rule can be dropped only by its owner.






 Chapter 9  Adding, Changing, and Removing Data
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 Once you've created a database, tables, and indexes, you'll want to put data
 into the tables and work with itÄadding, changing, and removing data as
 necessary.

 The INSERT statement lets you add new rows to the database. The UPDATE
 statement lets you change existing rows in the database. The DELETE
 statement lets you remove rows from the database.

 These three operations are collectively called data modification statements.
 The TRUNCATE TABLE statement, which deletes all the rows in a table, is also
 discussed in this chapter. Another method of adding data to a table is to
 transfer it from a file, using the bulk copy program, bcp. (For information
 about this utility program, see the SQL Server System Administrator's
 Guide.)

 You can modify (INSERT, UPDATE, or DELETE) data in only one table per
 statement. However, the modifications you make can be based on data in other
 tables, even those in other databases. (This is a TRANSACT-SQL enhancement
 to standard versions of SQL.)

 The data modification statements work on views as well as on tables, with
 some restrictions. (See Chapter 10, "Creating Views," for details.)


 Permissions

 Data modification statements are not necessarily available to everyone. The
 Database Owner and the owners of database objects can use the GRANT and
 REVOKE statements to decide who has access to which data-modification
 functions.

 Permissions can be granted to individual users, groups, or the public to use
 any combination of the data modification statements. (Permissions are
 discussed in Chapter 14, "Advanced Topics for Database Owners," and in
 greater detail in the SQL Server System Administrator's Guide.)


 Referential Integrity

 INSERT, UPDATE, DELETE, and TRUNCATE TABLE statements all allow you to
 change the data in the database. However, if you change data in one table
 without changing related data in other tables, disparities can develop.

 For example, if you discover that the au_id entry for Sylvia Panteley is
 incorrect and change it in the authors table, you must also change it in the
 titleauthor table (and in any other table in the database with a column
 containing that value). If you don't, you'll never be able to find
 information such as the names of Ms. Panteley's books, because it'll be
 impossible to make joins on her au_id column.

 The general problem of keeping data modifications consistent throughout all
 tables in a database is called referential integrity. One way to deal with
 it is to create special stored procedures called triggers that automatically
 go into effect when you give INSERT, UPDATE, and DELETE statements for
 particular tables or columns. A TRUNCATE TABLE statement is not caught by a
 DELETE trigger. (Triggers are discussed in Chapter 13, "Creating Triggers.")



 Transactions

 A copy of the old and new state of each row affected by each data
 modification statement is written to the transaction log. This means that if
 you begin a transaction by executing a BEGIN TRANSACTION statement, realize
 you have made a mistake, and roll the transaction back, the database can be
 restored to its previous condition. (A more complete explanation of
 transactions appears in Chapter 14, "Advanced Topics for Database Owners.")



 Using the Sample Database

 If you follow the examples in this chapter on your own screen, you will
 probably want to start with a clean copy of the pubs database. You should
 return it to its original state when you are finished. See your System
 Administrator for help in getting a clean copy of pubs.

 If you are starting with a clean copy of pubs, you can prevent any changes
 you make from becoming permanent by enclosing all the statements you enter
 inside a transaction and then aborting the transaction when you've finished
 with this chapter. Start the transaction by typing

   begin tran ch9

 This transaction is named ch9 for Chapter 9, "Adding, Changing, and Removing
 Data." You can cancel the transaction at any time and return the database to
 the condition it was in before you began the transaction by typing

   rollback tran ch9


 Datatype Entry Rules

 Several of the SQL Server-supplied datatypes have special rules for entering
 and searching for data. These rules are reviewed in the following
 subsections. (For more information on datatypes, see Chapter 7, "Creating
 Databases, Tables, and Indexes.")


 Char, Varchar, and Text

 Don't forget that all char, text, and datetime data must be enclosed in
 single or double quotation marks when it's input and when you're searching
 for it. See the SQL Server Language Reference for details on inserting text
 data.

 If you enter strings that are longer than the specified length of a char or
 varchar column, the entry is truncated without warning.

 There are two ways to specify literal quotes within a character entry. The
 first method is to use two quotation marks. For example, if you have begun a
 character entry with a single quotation mark and want to include a single
 quotation mark as part of the entry, use two single quotation marks:

   'I don"t understand.'

 With double quotation marks:

   "He said, ""It's not really confusing."""

 The second method is to enclose a quote in the opposite kind of quotation
 mark. In other words, surround an entry containing a double quotation mark
 with single quotation marks (or vice versa). For example:

   'George said, "There must be a better way."'
    "Isn't there a better way?"
    'George asked, "Isn't there a better way?"'

 To enter a character string longer than the width of your screen, enter a
 backslash (\) before going to the following line.

 The LIKE keyword and wildcards can be used when searching for char, text,
 and datetime data. (See Chapter 2, "Querying Databases.")


 Datetime

 When you enter datetime values, always enclose them in single or double
 quotation marks.

 SQL Server recognizes a wide variety of data entry formats. Case is always
 ignored, and spaces can occur anywhere between date parts. The default
 display format for dates is Apr 15 1987 10:23PM.

 When time data is entered, the order of time components is significant.
 Enter hours; then minutes; then seconds; then milliseconds; and then AM, am,
 PM, or pm (12AM is midnight; 12PM is noon). To be recognized as time, a
 value must contain either a colon or an AM/PM signifier.

 Milliseconds can be preceded either with a colon or a period. If preceded
 with a colon, the number means thousandths of a second. If preceded by a
 period, a single digit means tenths of a second, two digits mean hundredths
 of a second, and three digits mean thousandths of a second. For example,
 12:30:20:1 means twenty and one-thousandth of a second past 12:30;
 12:30:20.1 means twenty and one-tenth of a second past 12:30.

 The following formats are acceptable for time data:

   14:30
    14:30[:20:999]
    14:30[:20.9]
    4am
    4 PM
    [0]4[:30:20:500]AM

 When date data is entered, the order of components can vary. Months can be
 in alphabetic or numeric format. Alphabetic format is either the full name
 of the month or its first three letters. Case is not significant.

 If the month is given in numeric format, you can use any of the separators.
 Use a slash (/), hyphen (-), period (.), or space ( ). You can also give a
 date without separators. You can enter January 1, 1998, as 19980101 or
 980101.

 If the month is given in alphabetic format, the day and year can be in any
 location either before or after the month. In addition to these separators,
 the day can optionally be followed by a comma (,). When the month is
 alphabetic and the values for day and year are grouped together, the first
 value is assumed to be the day. For example, 2 3 mar is stored as Mar 2 2003
 12:00:00AM.

 If the year is given with two digits, <50 is the century 2000 and >=50 is
 the century 1900. So 25 is 2025 while 50 is 1950.

 The following formats are acceptable for datetime data:

   Apr[il] 15[,] [19]87
    [15] Apr[il][,] [19]87
    1987 APR[IL]
    5/15/1987
    5-15-87
    [19]87[0415]

 A datetime value can be entered either as time followed by date or as date
 followed by time, or the year can be separated from the rest of the date,
 like this:

   10:23PM Apr 15 1987
    Apr 15 1987 10:23PM
    Apr 15 10:23PM 1987

 If time is missing, it defaults to midnight. For example, if you insert Apr
 15 1987, SQL Server stores this as Apr 15 1987 12:00AM.

 If the day of the month is missing, it defaults to the first day of the
 month. If the month is missing, it defaults to January. If the entire date
 is missing, it defaults to the base date, January 1, 1900. For example, if
 you insert 4:33, SQL Server stores it as Jan 1 1900 4:33AM.

 You can use the LIKE keyword and wildcard characters with datetime data as
 well as with char, varchar, and text. When you use LIKE with datetime
 values, SQL Server converts the dates to the standard datetime format and
 then to varchar. Since the standard storage format doesn't include seconds
 or milliseconds, you cannot search for seconds or milliseconds with LIKE and
 a match pattern.

 It is a good idea to use LIKE when you search for datetime values since
 datetime entries can contain a variety of date parts. For example, if you
 insert the value 9:20 into a column named arrival_time, the clause where
 arrival_time = 9:20 would not find it because SQL Server converts the entry
 into Jan 1, 1900 9:20AM. However, the clause where arrival_time like
 `%9:20%' would find it.

 Use the STYLE parameter of the CONVERT statement to get several additional
 date display formats by converting datetime data to char or varchar.


 Binary, Varbinary, and Image

 When binary, varbinary, or image data is entered or searched for, it must be
 preceded with "0x". For example, to enter FF, type 0xFF.

 If you enter strings that are longer than the specified length of a binary
 or varbinary column, the entry is truncated without warning.

 A length of 10 for a binary or varbinary column means 20 characters.

 When you create a default on a binary or varbinary column, precede it with
 "0x".


 Money

 Money data should be preceded with a dollar sign ($) when you enter or
 search for it. If there is no $, SQL Server treats the value as a float.
 Depending on the exact input value, SQL Server may not accept it or may lose
 some of the value's precision. The minus sign, representing negative money
 values, must be placed after the dollar sign. You cannot enter money values
 with commas, although the default print format for money data places a comma
 after every three digits. When money values are displayed, they are rounded
 off to the nearest cent. All the arithmetic operations except modulo are
 available with money.


 Float

 The exact format for float data depends on the precision and number of
 significant digits that your machine supports. See your System Administrator
 for details.

 On all machines, you can enter float data with or without a decimal point
 and a positive or negative sign. An exponential component of float data is
 also optional. For float data with an exponential component, enter a number,
 followed by e or E, followed by a number that can include a sign but not a
 decimal point.

 Here are some examples of float data as you would enter it:

   10E2
    15.3e1
    2.e5
    2.2e-1
    +56E+2

 The value represented by a float is the product of the first number and the
 power of 10 of the second number. This value is computed and displayed by
 SQL Server.


 Int, Smallint, and Tinyint

 You can insert numeric values into int, smallint, and tinyint columns with
 the E-notation described in the preceding section.


 Adding New Rows: INSERT

 Use the INSERT statement to add rows to the database in two ways: with the
 VALUES keyword or with a SELECT statement.

 The VALUES keyword is used to specify values for some or all of the columns
 in a new row. A simplified syntax for the INSERT statement using the VALUES
 keyword is

   INSERT table_name
    VALUES (constant1, constant2, ...)

 Use a SELECT statement in an INSERT statement to pull values from one or
 more other tables. A simplified syntax for the INSERT statement using a
 SELECT statement is

   INSERT table_name
    SELECT column_list
    FROM table_list
    WHERE search_conditions

 The full syntax for the INSERT statement is

   INSERT [INTO]
    [[database.]owner.]{table_name | view_name} [(column_list)]
    {VALUES (constant_expression [, constant_expression...]) |
    SELECT_statement}

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 When text and image values are added with the INSERT statement, all of the
 data is written to the transaction log. The WRITETEXT statement allows you
 to add these values without logging the large amounts of data that may
 consist of text or image values. See "Inserting Data into Some Columns" and
 "Adding or Changing Data with WRITETEXT" later in this chapter.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Adding New Rows with VALUES

 This INSERT statement adds a new row to the publishers table, giving a value
 for every column in the row:

   insert into publishers
    values ('1622', 'Jardin, Inc.', 'Camden', 'NJ')

 Notice that the data values are typed in the same order as the column names
 in the original CREATE TABLE statement (that is, first the ID number, then
 the name, the city, and finally the state). The VALUES data is surrounded by
 parentheses and all character data is enclosed in single or double quotation
 marks.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 Use a separate INSERT statement for each row you add.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Inserting Data into Some Columns

 You can add data to some, but not all, of the columns in a row by specifying
 those columns. For example, adding data in only two columns (say pub_id and
 pub_name) requires a statement like this:

   insert into publishers (pub_id, pub_name)
    values ('1756', 'The Health Center')

 The order in which you list the column names must match the order in which
 you list the values. The following example produces the same results as the
 previous one:

   insert publishers (pub_name, pub_id)
    values('The Health Center', '1756')

 Either of the INSERT statements puts "1756" in the identification number
 column and "The Health Center" in the publisher name column. Since the
 pub_id column in publishers has a unique index, you can't execute both of
 these INSERT statements; the second attempt to insert a pub_id value of
 "1756" produces an error message.


 Null Values, Defaults, and Errors

 The following SELECT statement shows the row that was added to publishers:

   select *
    from publishers
    where pub_name = 'The Health Center'

    pub_id   pub_name    city   state
    ------   ----------------  ----   -----
    1756   The Health Center   NULL   NULL

 SQL Server enters null values in the city and state columns because no value
 was given for these columns in the INSERT statement, and the publisher table
 allows null values in these columns.

 When you specify values for only some of the columns in a row, one of three
 things can happen to the columns with no values:


   ž   A default value is entered if one exists for the column or
       user-defined datatype. (See Chapter 8, "Creating Defaults and Rules,"
       or the CREATE DEFAULT entry in the SQL Server Language Reference for
       details.)

   ž   NULL is entered if NULL was specified for the column when the table
       was created and no default value exists for the column or datatype.
       (See the CREATE TABLE entry in the SQL Server Language Reference.)

   ž   An error message is displayed and the row is not added if NULL was not
       specified and no default exists.


 The following table shows you what you would see under these circumstances:


ÖŚÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
             No NULL        NULL
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Default     the default    the default

 No Default  error message  NULL

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 You can use the sp_help system procedure to get a report on a specified
 table or default (or on any other object listed in the system table
 sysobjects). To see the definition of a default, use the sp_helptext system
 procedure.


 Rules

 As explained in Chapter 8, "Creating Defaults and Rules," you can create a
 rule and bind it to a column or user-defined datatype. Rules govern the kind
 of data that may be added.

 The pub_id column in the pubs database is an example: a rule called
 pub_idrule, which specifies acceptable publisher identification numbers, is
 bound to the column. The acceptable IDs are 1389, 0736, 0877, 1622, and
 1756, or any 4-digit number the first two digits of which are 99. If you try
 to enter any other number, you get an error message.

 When you get this kind of error message, you may want to look at the
 definition of the rule. Use the sp_helptext system procedure:

   sp_helptext pub_idrule

    ----------
    1

    (1 row affected)

    text
    ------------------------------------------------------
    create rule pub_idrule
    as @pub_id in ("1389", "0736", "0877", "1622", "1756")
    or @pub_id like "99[0-9][0-9]"

    (1 row affected)

 For more general information on a rule, use sp_help. Or use sp_help with a
 table and column name as parameters to find out whether the specified column
 has a rule.


 Adding New Rows with SELECT

 To pull values into a table from one or more other tables, use a SELECT
 clause in the INSERT statement. The SELECT clause can insert values into
 some or all of the columns in a row.

 Inserting values for only some columns can come in handy when you want to
 take some values from an existing table. Then you can use UPDATE to add the
 values for the other columns.

 Before inserting values for some (but not all) of the columns in a table,
 make sure that a default exists or NULL has been specified for the columns
 for which you are not inserting values. Otherwise, you'll get an error
 message.

 When you insert rows from one table into another, the two tables must have
 compatible structuresÄthat is, the matching columns must be of the same
 datatypes or datatypes between which SQL Server automatically converts.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 You cannot insert data from a table that allows null values into a table
 that does not, even though none of the data being inserted is null.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 If the columns are in the same order in their CREATE TABLE statements, you
 don't need to specify column names in either table. Suppose newauthors
 contains some rows of author information in the same format as authors. To
 add to authors all the rows in newauthors, type

   insert authors
    select *
    from newauthors

 To insert rows into a table based on data in another table, the columns in
 the two tables do not have to be listed in the same sequence in their
 respective CREATE TABLE statements. You can use either the INSERT or the
 SELECT statement to order the columns so that they match.

 For example, say the CREATE TABLE statement for the authors table contained
 the columns au_id, au_fname, au_lname, and address in that order, while
 newauthor contained au_id, address, au_lname, and au_fname. You'd have to
 make the column sequence match in the INSERT statement. You could do this in
 either of these two ways:

   insert authors (au_id, address, au_lname, au_fname)
    select * from newauthors

    insert authors
    select au_id, au_fname, au_lname, address
    from newauthors

 If the column sequence in the two tables fails to match, SQL Server cannot
 complete the INSERT operation or completes it incorrectly, putting data in
 the wrong column. For example, you might get address data in the au_lname
 column.


 Computed Columns

 You can use computed columns in a SELECT statement inside an INSERT
 statement. For example, imagine that a table named tmp contains some new
 rows for the titles table with some out-of-date data; the price figures need
 to be doubled. A statement to increase the prices and insert the tmp rows
 into titles looks like this:

   insert titles
    select title_id, title, type, pub_id, price*2,
    advance, royalty, ytd_sales, notes, pubdate
    from tmp

 When you perform computations on a column, you cannot use the SELECT *
 syntax. Each column must be named individually in the select list.


 Inserting Data into Some Columns

 You can use the SELECT statement to add data to some, but not all, of the
 columns in a row just as you do with the VALUES clause. Simply specify the
 columns to which you want to add data in the INSERT clause.

 For example, there are some authors in the authors table who do not have
 contracts and hence do not have entries in the titleauthor table. To pull
 their au_id numbers out of the authors table and insert them into the
 titleauthor table as placeholders, you might try to use this statement:

   insert titleauthor (au_id)
    select au_id
    from authors
    where contract = 0

 However, this statement is not legal because a value is required for the
 title_id column (null values are not permitted and no default is specified).
 You can put in xxxxxx as a dummy value for title_id like this, using it as a
 constant:

   insert titleauthor (au_id, title_id)
    select au_id, "xxxxxx"
    from authors
    where contract = 0

 The titleauthor table now contains four new rows with entries for the au_id
 column, dummy entries for the title_id column, and null values for the other
 two columns.


 Inserting Data from the Same Table

 You can insert data into a table based on other data in the same table.
 Essentially, this means copying all or part of a row.

 For example, you can insert a new row in the publishers table that is based
 on the values in an already existing row in the same table. (Make sure you
 follow the rule on the pub_id column.) Here's how:

   insert publishers
    select "9999", "test", city, state
    from publishers
    where pub_name = "New Age Books"

    (1 row affected)

    select * from publishers

    pub_id  pub_name     city   state
    ------  --------------------  ----------  -----
    0736  New Age Books    Boston   MA
    0877  Binnet & Hardley   Washington  DC
    1389  Algodata Infosystems  Berkeley  CA
    9999  test      Boston   MA

    (4 rows affected)


 Changing Existing Data: UPDATE

 Use the UPDATE statement to change single rows, groups of rows, or all the
 rows in a table.

 The UPDATE statement specifies the row or rows you want changed and the new
 data. The new data can be a constant or expression that you specify, or data
 pulled from other tables.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 The UPDATE statement is logged; if you are changing large blocks of text or
 image data, you may wish to use the WRITETEXT statement, which is not
 logged. See "Adding or Changing Data with WRITETEXT" later in this chapter.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Here's a simplified version of the UPDATE syntax for updating specified rows
 with an expression:

   UPDATE table_name
    SET column_name = expression
    WHERE search_conditions

 For example, if Reginald Blotchet-Halls decides to change his name to
 Goodbody Health, here's how to change his row in the authors table:

   update authors
    set au_lname = "Health", au_fname = "Goodbody"
    where au_lname = "Blotchet-Halls"

 This simplified syntax statement updates a table based on data from another
 table:

   UPDATE table_name
    SET column_name = expression
    FROM table_name
    WHERE search_conditions

 Here's an example that updates the ytd_sales column of the titles table to
 reflect the most recent sales recorded in the sales table:

   update titles
    set ytd_sales = ytd_sales + qty
    from titles, sales
    where titles.title_id = sales.title_id
    and sales.date in (select max(sales.date) from sales)

 The preceding example assumes that only one set of sales is recorded for a
 given title on a given date and that updates are up to date.

 The full syntax for the UPDATE statement is

   UPDATE [[database.]owner.]{table_name | view_name}
    SET [[[database.]owner.]{table_name. | view_name.}]
    column_name1 = {expression1 | NULL}
    [, column_name2 = {expression2 | NULL}...]
    [FROM [[database.]owner.]{table_name | view_name}
    [, [[database.]owner.]{table_name | view_name}]...]
    [WHERE search_conditions]


 The UPDATE Clause

 The UPDATE statement is followed by the name of a table or view. As in all
 the data modification statements, you can change the data in only one table
 at a time.

 If an UPDATE statement violates an integrity constraint (if one of the
 values being added is the wrong datatype, or violates a rule that has been
 defined for one of the columns or datatypes involved), the update does not
 take place and an error message is displayed.

 (See Chapter 10, "Creating Views," for restrictions on updating views.)


 The SET Clause

 The SET clause specifies the column(s) and the changed value(s). The WHERE
 clause determines which row or rows will be updated. Note that if you don't
 have a WHERE clause, the specified columns of all rows will be updated with
 the values given in the SET clause.

 For example, if all the publishing houses in the publishers table move their
 head offices to Atlanta, Georgia, this is how you update the table:

   update publishers
    set city = "Atlanta", state = "GA"

 In the same way, you can change the names of all the publishers to NULL with
 this statement:

   update publishers
    set pub_name = null

 You can also use computed column values in an update. To double all the
 prices in the titles table, use this statement:

   update titles
    set price = price * 2

 Since there is no WHERE clause, the change in prices is applied to every row
 in the table.


 The WHERE Clause

 The WHERE clause specifies which rows are to be updated. For example, in the
 unlikely event that northern California is renamed Pacifica (abbreviated PC)
 and the people of Oakland vote to change the name of their city to something
 exciting (like Big Bad Bay City), here is how you can update the authors
 table for all former Oakland residents whose addresses are now out of date:


   update authors
    set state = "PC", city = "Big Bad Bay City"
    where state = "CA" and city = "Oakland"

 You need to write another statement to change the name of the state for
 residents of other northern California cities.


 The FROM Clause

 Use the FROM clause to pull data from one or more tables into the table
 you're updating.

 For example, earlier in this chapter, an example was given for inserting
 some new rows into the titleauthor table for authors without contracts,
 filling in the au_id column and giving dummy or null values for the other
 columns. When one of these authors, Dirk Stringer, gets a contract, a title
 identification number is assigned to his book, The Psychology of Computer
 Cooking, in the titles table. You can modify his row in the titleauthor
 table by adding a title identification number for him:

   update titleauthor
    set title_id = titles.title_id
    from titleauthor, titles, authors
    where titles.title = "The Psychology of Computer Cooking"
     and authors.au_id = titleauthor.au_id
     and au_lname = "Stringer"

 Note that an UPDATE without the au_id join changes all the title_ids in the
 titleauthor table so that they are the same as The Psychology of Computer
 Cooking's identification number.


 Adding or Changing Data with WRITETEXT

 The WRITETEXT statement is used to change text or image values. This
 statement completely overwrites any existing data in the column it affects.
 As a default, WRITETEXT statements are not logged. This prevents you from
 filling up the transaction log with the large amounts of data that often
 comprise these datatypes.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 The System Administrator must use the sp_dboption system procedure to set
 select into/bulk copy to true for the WRITETEXT statement to be used without
 logging changes.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 The following example adds text to an existing row in the blurbs table:

   declare @val varbinary(30)
    select @val = texptr(blurb) from blurbs
    where title_id = "TC4203"
    writetext blurbs.blurb @val
    "This book is a must for Anglophile food freaks"

 For this statement to work, the column must already contain a valid text
 pointer. This means that some value, even an explicit NULL, must already
 have been inserted. You might create the row with the following INSERT
 statement that inserts only the title_id and NULL for the text column:

   insert blurbs
    values ("TC4203", NULL)

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 You cannot use WRITETEXT on text and image columns in views.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Removing Data: DELETE

 Like INSERT and UPDATE, DELETE works for single-row as well as multiple-row
 operations but is more suitable for the latter. As for the other data
 modification statements, you can delete rows based on data in other tables.


 If you decide to remove one row from publishers, the row added for Jardin,
 Inc., type

   delete publishers
    where pub_name = "Jardin, Inc."

 A simplified syntax for the DELETE statement is

   DELETE table_name
    WHERE column_name = expression

 Here is the complete syntax statement, which shows that you can remove rows
 either on the basis of specified expressions or based on data from other
 tables:

   DELETE [FROM] [[database.]owner.]{table_name | view_name}
    [FROM [[database.]owner.]{table_name | view_name}
    [, [[database.]owner.]{table_name | view_name}]...]
    [WHERE search_conditions]


 The DELETE Clause

 The optional FROM directly after DELETE is included for compatibility with
 other versions of SQL. (The FROM on the second line is a TRANSACT-SQL
 enhancement that allows you to make deletions based on data in other
 tables.)


 The WHERE Clause

 The WHERE clause specifies which rows are to be removed. When no WHERE
 clause is given in the DELETE statement, all rows in the table are removed.



 The FROM Clause

 The FROM clause in the second position of a DELETE statement is a special
 TRANSACT-SQL feature that allows you to select data from a table or tables
 and delete corresponding data from the first-named table. The rows you
 select in the FROM clause specify the conditions for the DELETE.

 Suppose that a complex corporate deal results in the acquisition of all the
 Big Bad Bay City (formerly Oakland) authors and their books by another
 publisher. You need to remove all these books from the titles table right
 away, but you don't know their titles or identification numbers. The only
 information you have is the authors' names and addresses.

 You can delete the rows in titles by finding the author identification
 numbers for the rows that have Big Bad Bay City as the town in the authors
 table and using these numbers to find the title identification numbers of
 the books in the titleauthor table. In other words, a three-way join is
 required to find the rows you want to delete in the titles table.

 The three tables are all included in the FROM clause of the DELETE
 statement. However, only the rows in the titles table that fulfill the
 conditions of the WHERE clause are deleted. You would have to do separate
 deletes to remove relevant rows in tables other than titles.

 Here's the statement you need:

   delete titles
    from authors, titles, titleauthor
    where titles.title_id = titleauthor.title_id
     and authors.au_id = titleauthor.au_id
     and city = "Big Bad Bay City"

 The deltitle trigger in the pubs database prevents you from actually
 performing this deletion since it won't allow you to delete any titles that
 have sales recorded in the sales table.


 Deleting All Rows: TRUNCATE TABLE

 Use TRUNCATE TABLE as a fast, nonlogged method of deleting all the rows in a
 table. TRUNCATE TABLE immediately frees all the space that the table's data
 and indexes had occupied. The freed space can then be used by any object.
 The distribution pages for all indexes are also freed. Remember to run
 UPDATE STATISTICS after adding new rows to the table.

 As with DELETE, a table emptied with the TRUNCATE TABLE statement remains in
 the database (along with its indexes and other associated objects) unless
 you enter a DROP TABLE statement.

 The TRUNCATE TABLE statement has the following syntax:

   TRUNCATE TABLE [[database.]owner.]table_name

 For example, to remove all the data in sales, type

   truncate table sales

 Permission to use the TRUNCATE TABLE statement, like DROP TABLE, defaults to
 the table owner and cannot be transferred.

 A TRUNCATE TABLE statement is not caught by a DELETE trigger. (See Chapter
 13, "Creating Triggers," for details.)






 Chapter 10  Creating Views
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 A view is an alternative way of looking at data in one or more tables. You
 can think of a view as a shifting frame through which you can see the
 particular data in which you're interested. That's why one speaks of looking
 at data or changing data through a view.

 A view is derived from one or more tables whose data is physically stored in
 the database. The tables or tables from which a view is derived are called
 base tables. A view can also be derived from another view.

 The definition of a view (in terms of the base tables from which it is
 derived) is stored in the database. No separate copies of data are
 associated with this stored definition. The data that you view is stored in
 the base tables.

 A view looks exactly like any other database table. You can display it and
 operate on it almost exactly as you can any other table. TRANSACT-SQL has
 been enhanced so that there are no restrictions on querying through views
 and fewer than usual on modifying them. (The exceptions are explained later
 in this chapter.)

 When you modify the data you see through a view, you are actually changing
 the data in the underlying base tables. Conversely, changes to data in the
 underlying base tables are automatically reflected in the views derived from
 them.

 Views can be used to focus, simplify, and customize each user's perception
 of the database. They also provide a security mechanism. These advantages
 are discussed later in this chapter.

 This chapter discusses:


   ž   The advantages of views

   ž   Creating views

   ž   Retrieving data through views

   ž   Updating data through views



 Advantages of Views

 The examples in this chapter demonstrate that views can be used to focus,
 simplify, and customize each user's perception of the database. Views also
 provide an easy-to-use security measure. In addition, they can be helpful
 when changes are made to the structure of the database and users prefer to
 work with the database in the style to which they have become accustomed.


 Focus

 Views allow users to focus in on the particular data that interests them and
 on the particular tasks for which they're responsible. Data that is not of
 interest to a particular user or for a particular task can be left out of
 the view.


 Simpler Data Manipulation

 Not only the users' perception of the data but also their manipulation of it
 can be simplified with views. Frequently used joins, projections, and/or
 selections can be defined as views so that users don't have to specify all
 the conditions and qualifications each time a further operation on that data
 is performed.

 For example, consider how the view hiprice would simplify a query to find
 information about high-priced, recently published books of specified types.



 Customizing

 Views allow different users to see the same data in different ways, even
 when they're using the same data at the same time. This advantage is
 particularly important when users of many different interests and skill
 levels share the same database.


 Security

 Through a view, users can query and modify only the data they can see. The
 rest of the database is neither visible nor accessible.

 With the GRANT and REVOKE statements, each user's access to the database can
 be restricted to specified database objectsÄincluding views. If the view and
 all the tables and views from which it was derived are owned by the same
 user, that owner can grant permission to others to use the view while
 denying permission to use its underlying tables and views. This is a simple
 but effective security mechanism. (See Chapter 14, "Advanced Topics for
 Database Owners," or the SQL Server System Administrator's Guide for details
 on the GRANT and REVOKE statements.)

 By defining different views and selectively granting permissions on them, a
 user (or any combination of users) can be restricted to different subsets of
 data. The following examples show how to use views for security:


   ž   Access can be restricted to a subset of the rows of a base table (a
       value-dependent subset). For example, you might define a view that
       contains only the rows for business and psychology books to keep
       information about other types of books hidden from some users.

   ž   Access can be restricted to a subset of the columns of a base table (a
       value-independent subset). For example, you might define a view that
       contains all the rows of the titles table but omits the royalty and
       advance columns since this information is sensitive.

   ž   Access can be restricted to a row-and-column subset of a base table.

   ž   Access can be restricted to the rows that qualify for a join of more
       than one base table. For example, you might define a view that joins
       the titles, authors, and titleauthor table to display the names of the
       authors and the books they have written. This view would hide personal
       data about authors and financial information about the books.

   ž   Access can be restricted to a statistical summary of data in a base
       table. For example, through the view category_price, defined later in
       this chapter, users can access only the average price of each type of
       book.

   ž   Access can be restricted to a subset of another view or of some
       combination of views and base tables. For example, through the view
       hiprice_computer, defined later in this chapter, users can access the
       title and price of computer books that meet the qualifications in the
       view definition of hiprice.


 To create a view, a user must be granted CREATE VIEW permission by the
 Database Owner and must have appropriate permissions on any tables or views
 referenced in the view definition.

 As the owner of an object on which other users have created views, you must
 be aware of who can see what data through what views. Consider this
 situation: the Database Owner has granted Harold CREATE VIEW permission, and
 a user named Margaret has granted Harold permission to SELECT from a table
 she owns. Given these permissions, Harold can create a view that selects all
 columns and rows from Margaret's table. If Margaret subsequently revokes
 permission for Harold to SELECT from her table, he can still look at her
 data through the view he has created.


 Logical Data Independence

 Views help to shield users from changes in the structure of the real tables
 if such changes become necessary.

 For example, say the database is restructured by using SELECT INTO to split
 the titles table into these two new (base) tables (and dropping titles):

   titletext (title_id, title, type, notes)

    titlenumbers (title_id, pub_id, price, advance, royalty,
    ytd_sales, pub_date)

 Notice that the old titles table can be "regenerated" by joining on the
 title_id columns of the two new tables. To shield the changed structure of
 the database from users, you can create a view that is the join of the two
 new tables. You can even name it titles.

 Any query or stored procedure that previously referred to the base table
 titles now refers to the view titles. As far as the users are concerned,
 SELECT statements continue to work exactly as before. Users who only
 retrieve from the new view need not even know that the restructuring has
 occurred.

 Unfortunately, views provide only partial logical independence. Some data
 modification statements on the new titles will not be allowed because of
 certain restrictions, which are explained later in this chapter.


 View Examples

 The first example is a view derived from the titles table. Suppose you are
 interested only in books priced higher than $15.00 and for which an advance
 of more than $5000 was paid. This SELECT statement would find the rows that
 qualify:

   select *
    from titles
    where price > $15
     and advance > $5000

 Now suppose you have numerous retrieval and update operations to do on this
 collection of data. You could, of course, combine the conditions shown in
 the previous query with any statement that you execute. However, for
 convenience, you can create a view in which just the records of interest are
 visible:

   create view hiprice
    as select *
    from titles
    where price > $15
     and advance > $5000

 When SQL Server receives this statement, it does not actually execute the
 SELECT statement that follows the keyword AS. Instead, it stores the SELECT
 statement (which is in fact the definition of the view hiprice) in the
 system table syscomments. Entries are also made in sysobjects and in
 syscolumns for each column included in the view.

 Now, when you display or operate on hiprice, SQL Server combines your SQL
 statement with the stored definition of hiprice. For example, you can change
 all the prices in hiprice just as you can change any other table:

   update hiprice
    set price = price * 2

 SQL Server actually finds the view definition in the system tables and
 converts this update statement into the statement

   update titles
    set price = price * 2
    where price > $15
     and advance > $5000

 In other words, SQL Server knows from the view definition that the data to
 be updated is in titles. It also knows that it should increase the prices
 only in those rows that meet the conditions on the price and advance columns
 given in the view definition and those in the update statement.

 Having executed the first update statementÄthe update to hipriceÄyou can see
 its effect either in the view or in the titles table. Conversely, if you had
 created the view and then executed the second update statement, which
 operates directly on the base table, the changed prices would also be
 visible through the view.

 Updating a view's underlying table in such a way that different rows qualify
 for the view affects the view. For example, say you increase the price of
 the book You Can Combat Computer Stress to $25.95. Since this book now meets
 the qualifying conditions in the view definition statement, it is considered
 part of the view.

 However, if you alter the structure of a view's underlying table by adding
 columns, the new columns will not appear in a view defined with a SELECT *
 clause unless the view is dropped and redefined. This is because the
 asterisk is interpreted and expanded when the view is first created.


 Help on Views

 Several system procedures provide information from the system tables about
 views.

 You can get a report on a view with the sp_help system procedure. For
 example:

   sp_help hiprice

    Name    Owner   Type
    --------   -----   ----
    hiprice    dbo    view

    Data_located_on_segment   When_created
    -----------------------   -----------------------
    default       Feb 12 1987 11:57AM

    (0 rows affected)

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         Column_name     Type            Length   Null    Default_name    Rule

         -----------     --------------  -------  ----    ------------    ----

         title_id        tid nocase      6        0       NULL            NULL

         title           varchar nocase  80       0       NULL            NULL

         type            char nocase     12       0       NULL            NULL

         pub_id          char nocase     4        1       NULL            NULL

         price           moneyn nocase   8        1       NULL            NULL

         advance         moneyn nocase   8        1       NULL            NULL

         royalty         intn nocase     4        1       NULL            NULL

         ytd_sales       intn nocase     4        1       NULL            NULL

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         notes           varchar nocase  200      1       NULL            NULL

         pubdate         datetime        8        0       NULL            NULL
                         nocase



         (0 rows
         affected)



         No defined
         keys for this
         object.



 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 This example shows output for a non-case-sensitive database.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 To display the text of the CREATE VIEW statement, execute the sp_helptext
 system procedure:

   sp_helptext hiprice

    -----------
    1

    (1 row affected)


    text
    -----------------------
    create view hiprice

    as select *
    from titles
    where price > $15
    and advance > $5000

    (1 row affected)

 The sp_depends system procedure lists all the objects that the view or table
 references in the current database, and it lists all the objects that
 reference that view or table. Here's an example:

   sp_depends titles
    Things inside the current database that reference the object.
    object    type
    ------------  --------------------
    dbo.hiprice   view
    dbo.titleview  view
    dbo.reptq1   stored procedure
    dbo.reptq2   stored procedure
    dbo.reptq3   stored procedure

    (0 rows affected)

 (For complete information about the system procedures, see the SQL Server
 Language Reference.)


 Creating and Dropping Views

 The CREATE VIEW statement has the following syntax:

   CREATE VIEW [[database.]owner.]view_name
    [(column_name [, column_name...])]
    AS SELECT_statement

 The remainder of this section discusses the CREATE VIEW clause, the SELECT
 statement, and the DROP VIEW statement.


 The CREATE VIEW Clause

 View names must follow the rules for identifiers and must be unique for each
 user among the already existing tables and views.

 You can build indexes on views, views on other views, and procedures that
 reference views. You can define primary, foreign, and common keys on views.
 You cannot associate rules, defaults, or triggers with a view. Temporary
 views cannot be created, nor can views be created on temporary tables.

 As illustrated in the example given in the previous section, you need not
 specify any column names in the CREATE VIEW clause of a view definition
 statement. SQL Server gives the columns of the view the same names and the
 same datatypes as the columns referred to in the select list of the SELECT
 statement. The select list can be an "*", as in the example, or a full or
 partial list of the column names in the base table(s).

 It's always legal to specify column names. However, column names must be
 specified in the CREATE VIEW clause for every column in the view if


   ž   Any of the view's columns are derived from an arithmetic expression, a
       built-in function, or a constant.

   ž   Two or more of the view's columns would otherwise have the same name
       (usually because the view definition includes a join, and the columns
       being joined have the same name).

   ž   You wish to give any column in the view a different name than the
       column from which it is derived. (You can also rename columns in the
       SELECT statement.)  Whether or not you rename a view column, it
       inherits the datatype of the column from which it is derived.


 Here's a view definition statement that makes the name of a column in the
 view different from its name in the underlying table:

   create view pub_view (Publisher, city, state)
    as select pub_name, city, state
    from publishers

 An alternate method of creating the same view but renaming the columns in
 the SELECT statement would be:

   create view pub_view2
    as select Publisher = pub_name, city, state
    from publishers

 The examples of view definition statements given in a later section
 illustrate the rest of the rules for including column names in the CREATE
 VIEW clause.


 The SELECT Statement

 The SELECT statement in the CREATE VIEW statement defines the view. You must
 have permission to SELECT from any objects referenced in the SELECT
 statement of a view you are creating.

 A view need not be a simple subset of the rows and columns of one particular
 table, as in our example. You can create a view using more than one table
 and/or other views with a SELECT statement of any complexity.

 There are a few restrictions on the SELECT statements in a view definition:



   ž   You cannot include ORDER BY or COMPUTE clauses.

   ž   You cannot include the DISTINCT or INTO keywords.

   ž   You cannot reference a temporary table.



 View Definition with Projection

 The following statement creates a view with all the rows of the titles table
 but with only a subset of its columns:

   create view titles_view
    as select title, type, price, pubdate
    from titles

 Note that no column names are included in the CREATE VIEW clause. The view
 titles_view inherits the column names given in the select list.


 View Definition with a Computed Column

 Here is a view definition statement that creates a view with a computed
 column generated from the columns price, royalty, and ytd_sales:

   create view accounts (title, advance, amt_due)
    as select title, advance, (price * royalty / 100) * ytd_sales
    from titles
    where price > $15
    and advance > $5000

 In this example, a list of columns must be included in the CREATE VIEW
 clause since there is no name that can be inherited by the column computed
 by multiplying together price, royalty, and ytd_sales. The computed column
 is given the name amt_due. It must be listed in the same position in the
 CREATE VIEW clause as the expression from which it is computed is listed in
 the SELECT clause.


 View Definition with a Built-In Function

 Similarly, a view definition that includes a built-in function must include
 column names in the CREATE VIEW clause:

   create view categories (category, average_price)
    as select type, avg(price)
    from titles
    group by type


 View Definition with a Join

 You can create a view derived from more than one base table. Here's an
 example of a view derived from both the authors and the publishers tables.
 The view contains the names and cities of the authors that live in the same
 city as a publisher, along with each publisher's name and city.

   create view cities (authorname, acity, publishername, pcity)
    as select au_lname, authors.city, pub_name, publishers.city
    from authors, publishers
    where authors.city = publishers.city


 Views Derived from Other Views

 You can define a view in terms of another view:

   create view hiprice_computer
    as select title, price
    from hiprice
    where type = 'popular_comp'


 Dropping Views

 To delete a view from the database, use the DROP VIEW statement. The DROP
 VIEW statement has the following syntax:

   DROP VIEW [[database.]owner.]view_name
    [, [[database.]owner.]view_name...]

 As indicated, you can drop more than one view at a time.

 Here's how to drop the view hiprice:

   drop view hiprice

 When you execute the DROP VIEW statement, information about the named
 view(s) is deleted from the system tables sysprocedures, sysobjects,
 syscolumns, syscomments, sysprotects, and sysdepends. All permissions on
 that view are deleted too.

 If a view depends on a table (or on another view) that has been dropped, SQL
 Server displays an error message if anyone tries to use the view. If a new
 table or view with the same name is created to replace the one that has been
 dropped, the view again becomes usable (as long as the columns referenced in
 the view definition exist).


 Retrieving Data through Views

 When you retrieve data through a view, SQL Server checks to make sure that
 all the database objects referenced anywhere in the statement exist and that
 they are valid in the context of the statement. If the checks are
 successful, SQL Server combines the statement with the stored definition of
 the view and translates it into a query on the view's underlying table(s),
 as explained in an earlier section. This process is called view resolution.


 Consider the view definition statement given earlier in this chapter and a
 query against it:

   create view hiprice
    as select *
    from titles
    where price > $15
    and advance > $5000

    select title, type
    from hiprice
    where type = 'popular_comp'

 Internally, SQL Server combines the query of hiprice with its definition,
 converting the query to

   select title, type
    from titles
    where price > $15
    and advance > $5000
    and type = 'popular_comp'

 You can query any view in any way just as if it were a real table. You can
 use joins, GROUP BY clauses, subqueries, and other query techniques on
 views, in any combination.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 You can use the SELECT statement on text and image columns in views, but the
 READTEXT statement is not allowed.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 View Resolution

 When you define a view, SQL Server checks to make sure that all the tables
 or views listed in the FROM clause exist and gives you an error message if
 there's a problem. Similar checks are performed when you query through the
 view.

 Between the time a view is defined and the time it is used in an SQL
 statement, things can change. For example, one or more of the tables or
 views listed in the FROM clause of the view definition may have been
 dropped. Or one or more of the columns listed in the SELECT statement of the
 view definition may have been renamed.

 To fully resolve a view, SQL Server checks to make sure that


   ž   All the tables, views, and/or columns from which the view was derived
       still exist.

   ž   The datatype of each column on which a view column depends has not
       been changed to an incompatible type.

   ž   If the statement is an UPDATE, INSERT, or DELETE, it does not violate
       the restrictions on modifying views. (Restrictions are discussed in a
       later section of this chapter.)


 If any of these checks fail, SQL Server displays an error message.


 Redefining Views

 SQL Server allows you to redefine a view without forcing you to redefine
 other views that depend on it (unless the redefinition makes it impossible
 for SQL Server to translate the dependent view).

 As an example, the authors table and three possible views are shown here.
 Each succeeding view is defined using the view that preceded it: view2 is
 created from view1, and view3 is created from view2. In this way, view2
 depends on view1, and view3 depends on both of the preceding views.

 The columns in the views and their selection criteria for the CREATE VIEW
 statements are listed in parentheses following the name of the view.

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         view3    (select au_lname, phone
                  from view2
                  where au_lname = "MacFeather")

         view2    (select au_lname, phone
                  from view1
                  where au_lname like "[M-Z]%")

         view1    (select au_lname, phone
                  from authors
                  where zip like "94___")

         authors  (au_id, au_lname, au_fname, phone,
                  address, city, state, zip, contract)



 You can replace view2 with another view (also named view2) that contains
 slightly different selection criteria, such as (au_lname, phone from view_1
 where au_lname like "[M-P]"). View3 (which depends on view2) is still valid
 and need not be redefined. When you use a query that references either view2
 or view3, view resolution takes place as usual.

 If you redefine view2 so that view3 cannot be derived from it, view3 becomes
 invalid. For example, if a new version of view2 contains a single column,
 au_lname, rather than the two columns that view3 expects, view3 can no
 longer be used since it cannot derive the phone column from the object on
 which it depends.

 However, view3 still exists and can be used again by dropping the offending
 view2 and re-creating view2 with both the au_lname and the phone columns.

 In short, you can change the definition of an intermediate view without
 affecting dependent views so long as the select list of the dependent views
 remains valid. If this rule is violated, a query that references the invalid
 view displays an error message.


 Renaming Views

 You can rename a view with the sp_rename system procedure. The sp_rename
 system procedure has the following syntax:

   sp_rename objname, newname

 For example, to rename titleview to bookview:

   sp_rename titleview, bookview

 Of course, the new name must follow the rules for identifiers. You can only
 change the name of views that you own. The Database Owner can change the
 name of any user's view. The view must be in the current database.


 Altering or Dropping Underlying Objects

 Problems can arise if you change the name of a view's underlying object.
 Views that depend on a table or view whose name has been changed may work
 fine for a while. In fact, they work until SQL Server recompiles them. Since
 recompilation takes place for many reasons and without notification to the
 user, attempts to query or modify the view may suddenly cause SQL Server to
 return error messages.

 At that point, you must drop the view and re-create it so that its text
 reflects the new name of the object on which it depends. To avoid such
 problems, the safest course is not to rename any tables or views that are
 referenced by a view. If you do rename tables or views, be sure to change
 the definitions of their dependent views when you rename them.

 A similar situation arises if a view depends on a table or view that has
 been dropped. When someone tries to use the view, SQL Server produces an
 error message. However, if a new table or view is created to replace the one
 that was dropped, the view will again become usable.

 If you define a view with a SELECT * clause and then alter the structure of
 its underlying table(s) by adding columns, the new columns will not appear.
 This is because the asterisk shorthand is interpreted and expanded when the
 view is first created. To see the new columns through the view, drop the
 view and re-create it.


 Restrictions for Modifying Data through Views

 There are several kinds of data modification operations not allowed through
 views:


   ž   UPDATE, INSERT, or DELETE statements that refer to any column in the
       view that is a computation (a computed column or a built-in function)
       are not allowed.

   ž   UPDATE, INSERT, or DELETE statements that refer to a view that
       includes aggregates or row aggregates (built-in functions and a GROUP
       BY clause or a COMPUTE BY clause) are not allowed.

   ž   INSERT statements are not allowed unless all NOT NULL columns in the
       underlying table(s) or view(s) are included in the view through which
       you are inserting new rows. (SQL Server has no way to supply values
       for NOT NULL columns in the underlying objects.)

   ž   UPDATE and INSERT statements are not allowed unless the columns being
       modified all belong to the same base table. (It's illegal in SQL to
       use data modification statements on more than one table in a single
       statement.) However, if a view contains columns from more than one
       object, but the data modification statement references columns from
       only one of them, the statement is legal. This is a TRANSACT-SQL
       enhancement.

   ž   The WRITETEXT statement is not allowed in the text and image columns
       in a view.


 When you write a data modification statement for a view, SQL Server checks
 to make sure that none of the restrictions is violated and no data integrity
 rules are violated.

 Why can some views be updated and some not? You can best understand the
 restrictions by examining an example of each kind of view that cannot be
 updated.


 Computed Columns in View Definition

 The first restriction applies to columns of views that are derived from
 computed columns or built-in functions. For example, the amt_due column in
 the accounts view is a computed column.

   create view accounts (title, advance, amt_due)
    as select title_id, advance, (price * royalty/100) * ytd_sales
    from titles
    where price > $15
    and advance > $5000

 The rows visible through accounts are

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄŚÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         select * from accounts



         title_id                                               advance   amt_

         --------                                               --------  ----

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         PC1035                                                 7,000.00  32,2

         PC8888                                                 8,000.00  8,19

         PS1372                                                 7,000.00  809.

         TC3218                                                 7,000.00  785.



         (4 rows affected)



 Updates and inserts to the amt_due column are not allowed because there is
 no way to deduce the underlying values for price, royalty, or year-to-date
 sales from any value you might enter in amt_due column. DELETE statements
 make no sense because there is no underlying value to delete.


 GROUP BY or COMPUTE BY in View Definition

 The second restriction applies to all columns in views that contain
 aggregate valuesÄthat is, views whose definition includes a GROUP BY or
 COMPUTE BY clause. Here is a view defined with a GROUP BY clause and the
 rows seen through it:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         create view categories (category, average_price)

         as select type, avg(price)

         from titles

         select * from categories

         category                                                     average_

         --------                                                     --------
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        --------                                                     --------

         UNDECIDED                                                    NULL

         business                                                     13.73

         mod_cook                                                     11.49

         popular_comp                                                 21.48

         psychology                                                   13.50

         trad_cook                                                    15.96

         (6 rows affected)



 It would not make sense to insert rows into the categories view. To what
 group of underlying rows would an inserted row belong? Updates on the
 average_price column cannot be allowed because there is no way to know from
 any value you might enter there how the underlying prices should be changed.



 Null Values in Underlying Objects

 The third restriction applies to INSERT statements if there are some NOT
 NULL columns in the table(s) or view(s) from which the view is derived.

 For example, suppose null values are not allowed in a column of a table that
 underlies a view. Normally, when you insert new rows through a view, any
 columns in underlying tables that aren't included in the view are given null
 values. If null values are not allowed in one or more of these columns, no
 inserts can be allowed through the view.

 Consider this view:

   create view titleview
    as select title_id, price, ytd_sales
    from titles
    where type = 'business'

 Null values are not allowed in the title column of the underlying table
 titles, so no INSERT statements can be allowed through titleview. Although
 the title column doesn't even exist in the view, its prohibition of null
 values makes any inserts into the view illegal.

 Similarly, if the title_id column has a unique index, updates or inserts
 that would duplicate any values in the underlying table are rejected, even
 if the entry doesn't duplicate any value in the view.


 Changing More Than One Underlying Object

 The fourth restriction is self-explanatory. It disallows updates and inserts
 through views if the statement would modify columns that are derived from
 more than one object. This is because updates and inserts on more than one
 object at a time are never allowed.

 However, SQL Server allows view modification statements that would not be
 allowed in other systems. For example, you can modify one underlying object
 of a view that contains columns from more than one underlying object.

 In other words, if a view contains three columns from titles and two columns
 from publishers, you can do an update operation on the view that changes
 only the columns from titles. You can't do an update on the view that
 changes one column from titles and one column from publishers.






 Chapter 11  Using Control-of-Flow Language
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 This chapter discusses how to define and use multi-statement, or batch,
 files and control-of-flow language.

 Stored procedures and system procedures (procedures supplied by SQL Server
 for querying and updating the system tables) are discussed in Chapter 12,
 "Using Stored and System Procedures." A special kind of stored procedure
 called a trigger is used to enforce referential integrity. Triggers are
 discussed in Chapter 13, "Creating Triggers."


 Batches

 Up to this point, each example in this guide has consisted of an individual
 SQL statement. You submit individual SQL statements to SQL Server one at a
 time, entering statements and receiving results interactively. SQL Server
 can also process multiple SQL statements submitted as a batch, either
 interactively or from a file.

 A batch of SQL statements is terminated by an end-of-batch signal that
 instructs SQL Server to go ahead and execute the statements. The
 end-of-batch signal for the stand-alone SQL utility, isql, is the go command
 on a line by itself. (For details, see the SQL Server Language Reference.)

 Technically speaking, a single SQL statement can constitute a batch, but it
 is more common to think of a batch as containing multiple SQL statements.
 Frequently, a batch of statements is written to an operating system file
 before being submitted to isql.


 Batches and Batch Rules

 Batch rules govern which SQL statements can be combined into a single batch.
 The rules include the following:


   ž   CREATE PROCEDURE, CREATE RULE, CREATE DEFAULT, CREATE TRIGGER, and
       CREATE VIEW statements must be submitted to SQL Server in isolation;
       they cannot be combined with other SQL statements in a batch. CREATE
       DATABASE, CREATE TABLE, and CREATE INDEX can be combined with other
       statements in a single batch.

   ž   USE must be submitted in a prior batch before statements that
       reference objects in that database.

   ž   You cannot drop an object and then reference or re-create it in the
       same batch.

   ž   Any options set with a SET statement take effect at the end of the
       batch. You can combine SET statements and queries in the same batch,
       but the SET options won't apply to the queries in that batch.



 Batch Examples

 The examples in this section illustrate batches using the format of the isql
 utility, which has a clear end-of-batch signal (the go command on a line by
 itself). Here's a batch that contains two SELECT statements:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   select count(*) from titles

                                   select count(*) from authors


 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


                                   /*Two SELECT statements in a single
                                   batch.*/

                                   ------------

                                   18

                                   (1 row affected)

                                   ------------

                                   23

                                   (1 row affected)



 This batch creates a table, inserts a row into it, and then selects
 everything from it:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         create table test

         column1 char(10), column2 int

         insert test

         values ("hello", 598)

         select * from test

         go



         /*You can CREATE a table and reference it in the same batch.*/

         (1 row affected)
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        (1 row affected)

         column1                                                          colu

         -------                                                          ----

         hello                                                            598

         (1 row affected)



 This batch contains a single statement which creates a view:

   create view testview as
    select column1 from test
    go

    /*A CREATE VIEW statement must be the only statement
    in a batch.*/

 The next batch selects from a table in the master database and then opens
 the pubs database. (It assumes that you are in the master database.)

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   select count(*) from sysdatabases

                                   use pubs

                                   go



                                   /*You can combine a USE statement with
                                   other statements so

                                   long as objects you reference in
                                   subsequent statements are

                                   in the database in which you started. This
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                  in the database in which you started. This
                                   example assumes

                                   you started in the Master Database. After
                                   the batch is

                                   executed, pubs is the current database.*/



                                   ------------

                                   9



                                   (1 row affected)



 The final batch example combines a DROP statement with a SELECT statement.

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   drop table test

                                   select count(*) from titles

                                   go



                                   /*You can combine a DROP statement with
                                   other statements as

                                   long as you don't reference or re-create
                                   the dropped object

                                   in the same batch.*/


 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


                                   ------------

                                   18



                                   (1 row affected)



 If there is a syntax error anywhere in the batch, none of the statements is
 executed. For example, here is a batch with a typing error in the last
 statement and the results:

   select count(*) from titles
    select count(*) from authors
    slect count(*) from publishers
    go

    Msg 101, Level 15, State 1:
    Line 3: SQL syntax error.
    Msg 156, Level 15, State 1:
    Incorrect syntax near the keyword 'count'.

 Batches that violate a batch rule also generate error messages. Here are
 some examples of illegal batches:

   A. create table test
    (column1 char(10), column2 int)
    insert test
    values ("hello", 598)
    select * from test
    create view testview as select column1 from test
    go
    /* ILLEGAL BATCH - CREATE VIEW must be the only statement in a batch.*/

   B.  create view testview as select column1 from test
    insert testview values ("goodbye")
    go
    /* ILLEGAL BATCH - CREATE VIEW must be the only statement in a batch.*

   C. use pubs
    select * from titles
    go
    /* ILLEGAL BATCH - You cannot switch databases and
    reference the new one in the same batch.*/

   D. drop table test
    create table test
    (column1 char(10), column2 int)
    go
    /* ILLEGAL BATCH - You cannot drop an object and
    re-create it in the same batch.*/


 Batches Submitted as Files

 You can submit one or more batches of SQL statements to isql from an
 operating system file. A file can include more than one batchÄmore than one
 collection of SQL statements, each terminated by the go command.

 For example, an operating system file might contain the following three
 batches:

   use pubs
    go
    select count(*) from titles
    select count(*) from authors
    go
    create table test
    (column1 char(10), column2 int)
    insert test
    values ("hello", 598)
    select * from test
    go

 Here are the results of submitting this file to the isql program:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   -----------
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   -----------

                                   18

                                   (1 row affected)

                                   -----------

                                   23

                                   (1 row affected)

                                   (1 row affected)



ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         column1           column2

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         -------           ----------

         hello             598



         (1 row affected)




 Control-of-Flow Language

 TRANSACT-SQL provides special keywords called control-of-flow language that
 allow the user to control the flow of execution of SQL statements.
 Control-of-flow language can be used in single SQL statements, in batches,
 in stored procedures, and in triggers.

 Without control-of-flow language, separate SQL statements are performed
 sequentially, as they occur. (Correlated subqueries, discussed in Chapter 6,
 "Building Subqueries," are a partial exception.) Control-of-flow language
 permits statements to be connected, related to each other, and made
 interdependent using programming-like constructs.

 Control-of-flow languageÄsuch as IF-ELSE for conditional performance of
 statements and WHILE for repetitive executionÄlets you refine and control
 the operation of SQL statements. TRANSACT-SQL's control-of-flow language
 transforms standard SQL into a high-level programming language. The
 control-of-flow keywords and their functions are as follows:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Keyword                           Function
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 IF                                Defines conditional execution

 ...ELSE                           Defines alternate execution when condition
                                   is false

 BEGIN                             Begins a statement block

 Keyword                           Function
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 ...END                            Ends a statement block

 WHILE                             Repeats performance of statements while
                                   condition is true

 BREAK                             Exits from the end of the next outermost
                                   WHILE loop

 ...CONTINUE                       Restarts WHILE loop

 DECLARE                           Declares local variables

 GOTO label:                       Goes to label:, a position in a statement
                                   block

 RETURN                            Exits unconditionally

 WAITFOR                           Sets delay for statement execution
 Keyword                           Function
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
WAITFOR                           Sets delay for statement execution

 PRINT                             Prints a user-defined message or local
                                   variable on user's screen

 RAISERROR                         Prints a user-defined message or local
                                   variable on user's screen and sets a
                                   system flag in the global variable @ERROR

 /* comment */                     Inserts a comment anywhere in an SQL
                                   statement

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ




 IF...ELSE

 The IF keyword (used with or without its companion ELSE) is used to
 introduce a condition that determines whether the following SQL statement is
 executed. The SQL statement is executed if the condition is satisfied (that
 is, if it returns true).

 The ELSE keyword introduces an alternate SQL statement that is executed when
 the IF condition is not satisfied (when it returns false).

 The IF and ELSE keywords have the following syntax:

   IF
    boolean_expression
    statement
    [ELSE
    [boolean_expression]
    statement ]

 A boolean expression is an expression that returns true or false. It can
 include a column name, a constant, any combination of column names and
 constants connected by arithmetic or bitwise operators, or a subquery (as
 long as it returns a single value). If the boolean expression contains a
 SELECT statement, the SELECT statement must be enclosed in parentheses, and
 it must return a single value.

 Here's an example of using IF alone:

   if exists (select zip from authors
    where zip = '94705')
    print "Berkeley author"

 If one or more of the zip codes in the authors table has the value 94705,
 the message "Berkeley author" is printed. The SELECT statement in this
 example returns a single value (either true or false) because it is
 introduced with the keyword EXISTS. The EXISTS keyword functions here just
 as it does in subqueries. (See Chapter 6, "Building Subqueries.")

 The following example uses both IF and ELSE to test for the presence of
 user-created objects (all of which have ID numbers that are larger than 50).
 If user objects exist, the ELSE clause selects their names, types, and ID
 numbers.

   if (select max(id) from sysobjects) < 50
    print "There are no user-created objects in this database."
    else
    select name, type, id from sysobjects
    where id > 50 and type = "U"

    (0 rows affected)

    name    type  id
    ----------   ----  ----------
    authors    U   1088006907
    publishers   U   1120007021
    roysched   U   1152007135
    sales    U   1184007249
    titleauthor   U   1216007363
    titles    U   1248007477
    stores    U   1280007591
    discounts   U   1312007705
    test    U   1648008902

    (9 rows affected)

 IF and ELSE control the execution of a single SQL statement unless you use
 the BEGIN and END keywords (discussed later) to group multiple statements
 into a statement block. The expression in the IF test must return a single
 value, whether it is a single statement or a statement block.

 IF...ELSE constructs are frequently used in stored procedures where they
 test for the existence of some parameter.

 IF tests can nest within other IF tests, either within another IF or
 following an ELSE. There is no limit on the number of levels of nesting.


 BEGIN...END

 The BEGIN and END keywords are used to enclose a series of SQL statements so
 that they are treated as a unit by control-of-flow constructs like
 IF...ELSE. A series of statements enclosed by BEGIN and END is called a
 statement block.

 BEGIN and END have the following syntax:

   BEGIN
     statement block
    END

 Here's an example:

   if (select avg(price) from titles) < $15
    begin
     update titles
     set price = price * 2

     select title, price
     from titles
     where price > $28
    end

    (0 rows affected)

 Without BEGIN and END, the IF condition would cause only one SQL statement
 to be executed.

 BEGIN...END blocks can be nested within other BEGIN...END blocks.


 WHILE and BREAK...CONTINUE

 The WHILE keyword is used to set a condition for the repeated execution of a
 statement or statement block. The statement is executed repeatedly as long
 as the specified condition is true.

 The WHILE keyword has the following syntax:

   WHILE
    boolean_expression
    statement

 In this example, the SELECT and UPDATE statements are repeated as long as
 the average price remains less than $30:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         while (select avg(price) from titles) < $30
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         while (select avg(price) from titles) < $30

         begin

         select title_id, price

         from titles

         where price > $20



         update titles

         set price = price * 2

         end



 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         (0 rows affected)



         title_id                                                        price

         --------                                                        -----

         PC1035                                                          22.95

         PS1372                                                          21.59

         TC3218                                                          20.95



         (3 rows affected)

         (18 rows affected)
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        (18 rows affected)

         (0 rows affected)



         title_id                                                        price

         --------                                                        -----

         BU1032                                                          39.98

         BU1111                                                          23.90

         BU7832                                                          39.98

         MC2222                                                          39.98

         PC1035                                                          45.90

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         PC8888                                                          40.00

         PS1372                                                          43.18

         PS2091                                                          21.90

         PS3333                                                          39.98

         TC3218                                                          41.90

         TC4203                                                          23.90

         TC7777                                                          29.98



         (12 rows affected)

         (18 rows affected)
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        (18 rows affected)

         (0 rows affected)



 The BREAK and CONTINUE keywords control the operation of the statements
 inside a WHILE loop. BREAK causes an exit from the WHILE loop. Any
 statements that appear after the END keyword that marks the end of the loop
 are executed. CONTINUE causes the WHILE loop to restart, skipping any
 statements after CONTINUE but inside the loop. BREAK and CONTINUE are often
 activated by an IF test.

 BREAK and CONTINUE have the following syntax:

   WHILE
    boolean expression
    statement
    BREAK
    statement
    CONTINUE

 Here's an example using WHILE, BREAK, CONTINUE, and IF that reverses the
 inflation caused in the previous examples. As long as the average price
 remains more than $20, the prices are all cut in half. The maximum price is
 then selected. If it is greater than or equal to $40, the WHILE loop is
 restarted. If the maximum price is less than $40, the WHILE loop is exited,
 and a message (along with the highest prices) is printed.

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         while (select avg(price) from titles) > $20

         begin

         update titles

         set price = price / 2

         select max(price) from titles

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         if (select max(price) from titles) < $40

         break

         else

         continue

         end

         begin

         select title_id, price

         from titles

         where price > $20

         print "Not Too Expensive"
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        print "Not Too Expensive"

         end



         (18 rows affected)

         -----------

         45.90



         (1 row affected)

         (0 rows affected)

         (0 rows affected)

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

         (18 rows affected)

         ------------

         22.95



         (1 row affected)

         (0 rows affected)








 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


         title_id                                                        price

         --------                                                        -----

         PC1035                                                          22.95

         PS1372                                                          21.59

         TC3218                                                          20.95

         (3 rows affected)



         Not Too Expensive



 If two or more WHILE loops are nested, BREAK exits to the next outermost
 loop. First all the statements after the END of the inner loop run. Then the
 outer loop restarts.


 DECLARE and Local Variables

 Variables are defined entities that are assigned values. Local variables are
 declared (and named and typed) using the DECLARE keyword and assigned an
 initial value with a SELECT statement. They must be declared, assigned a
 value, and used all within the same batch or procedure.

 Local variables are often used in a batch or stored procedure as counters
 for WHILE loops or IF...ELSE blocks. When they are used in stored
 procedures, they are declared for automatic, noninteractive use by the
 procedure when it is executed.

 The name of a local variable must begin with the "@" sign and follow the
 rules for identifiers except that it can contain from 1 to 29 characters.
 Each local variable is given either a system datatype or a user-defined
 datatype.

 Local variables have the following syntax:

   DECLARE @variable_name datatype
    [, @variable_name datatype...]

 Values are assigned to local variables with a SELECT statement. The syntax
 is

   SELECT @variable_name = expression
    [, @variable_name = expression...]
    [FROM clause] [WHERE clause]
    [GROUP BY clause] [HAVING clause]
    [ORDER BY clause] [COMPUTE clause]

 Local variables must be declared and used in the same batch or procedure.

 The SELECT statement that assigns a value to the local variable usually
 returns a single value. Here are some examples:

   declare @veryhigh money
    select @veryhigh = max(price)
    from titles
    if @veryhigh > $20
    print "Ouch!"

    declare @one varchar(18), @two varchar(18)
    select @one = "this is one", @two = "this is two"
    if @one = "this is one"
    print "you got one"
    if @two = "this is two"
    print "you got two"
    else print "nope"

 If the SELECT statement returns more than one value, the variable is
 assigned the last value returned.

 It is more efficient in terms of both memory usage and performance to write


   select @a = 1, @b = 2, @c = 3

 than to write

   select @a = 1
    select @b = 2
    select @c = 3

 A similar thing applies to DECLARE statements. It is more efficient to write


   declare @a int, @b char(20), @c float

 than to write

   declare @a int
    declare @b char(20)
    declare @c float

 The SELECT statement that assigns values to variables is used only for that
 purpose; it cannot also be used to return data to the user. The first SELECT
 statement in the following example assigns the maximum price to the local
 variable @veryhigh; the second SELECT statement displays the value:

   declare @veryhigh money
    select @veryhigh = max(price)
    from titles
    select @veryhigh

 Local variables can be used as parameters to PRINT or RAISERROR.


 DECLARE and Global Variables

 Global variables are system-supplied, predeclared variables. They are
 distinguished from local variables by having two "@" signs preceding their
 namesÄfor example, @ERROR.

 The global variables and their contents are as follows:

 @@CONNECTIONS
   The number of logins or attempted logins.

 @@CPU_BUSY
   The amount of time, in ticks, that the CPU has spent doing SQL Server work
   since the last time SQL Server was started.

 @@ERROR
   The last error number generated by the system. The @ERROR global variable
   is commonly used to check the error status (succeeded or failed) of the
   most recently executed statement. A statement such as IF @ERROR != 0
   return causes an exit on error.

 @@IDLE
   The amount of time, in ticks, that SQL Server has been idle.

 @@IO_BUSY
   The amount of time, in ticks, that SQL Server has spent doing input and
   output operations.

 @@MAX_CONNECTIONS
   The maximum number of simultaneous connections that can be made with SQL
   Server in this computer environment. The user can configure SQL Server for
   fewer connections with sp_configure.

 @@NESTLEVEL
   The level of current execution (initially zero). Each time a stored
   procedure calls another stored procedure, the nesting level is
   incremented. If the maximum of 32 is exceeded, the transaction aborts.

 @@PACK_RECEIVED
   The number of input packets read by SQL Server.

 @@PACK_SENT
   The number of output packets written by SQL Server.

 @@PACKET_ERRORS
   The number of errors that have occurred while SQL Server was sending and
   receiving packets.

 @@PROCID
   The stored procedure ID of the currently executing procedure.

 @@ROWCOUNT
   The number of rows affected by the last query.

 @@TEXTSIZE
   The number of bytes of text data a SELECT returns.

 @@TIMETICKS
   The number of microseconds per tick. Each tick on the Microsoft(R)
   Operating System/2 (MS(R) OS/2) is 312.5 milliseconds (1/32 seconds).

 @@VERSION
   The date of the current version of SQL Server.

 @@TOTAL_ERROR
   The number of errors that have occurred while SQL Server was reading or
   writing.

 @@TOTAL_READ
   The number of disk reads by SQL Server.

 @@TOTAL_WRITE
   The number of writes by SQL Server.

 @@TRANCOUNT
   The number of currently active transactions for the current user.

 @@TOTAL_ERROR
   The number of errors that have occurred while SQL Server was reading or
   writing.

 For information on the contents of many of these global variables, execute
 the sp_monitor system procedure. (For complete information on the system
 procedures, see the SQL Server Language Reference.)


 GOTO

 The GOTO statement causes unconditional branching to a user-defined label.
 GOTO and labels can be used in stored procedures and batches. A label's name
 must follow the rules for identifiers and must be followed by a colon (:)
 when it is first given. It is not followed by a colon when it is used with
 GOTO.

 The GOTO statement has the following syntax:

   label:

    GOTO label

 Here's an example that uses GOTO and a label, a WHILE loop, and a local
 variable as a counter:

   declare @count smallint
    select @count = 1
    restart:
    print "yes"
    select @count = @count + 1
    while @count <=4
    goto restart

 As in this example, GOTO is usually made dependent on a WHILE or IF test or
 some other condition to avoid an endless loop between GOTO and the label.


 RETURN

 The RETURN statement exits from a query or procedure unconditionally. It can
 be used at any point in a query or a procedure. Statements after RETURN are
 not executed.

 The RETURN statement has the following syntax:

   RETURN

 Here's an example of a stored procedure that uses RETURN (as well as
 IF...ELSE and BEGIN...END):

   create procedure findrules @nm varchar(30) = null as
    if @nm is null
    begin
     print "You must give a username"
     return
    end
    else
    begin
     select sysobjects.name, sysobjects.id, sysobjects.uid
     from sysobjects, master..syslogins
     where master..syslogins.name = @nm
     and sysobjects.uid = master..syslogins.suid
     and sysobjects.type = "R"
    end

 If no username is given as a parameter, the RETURN statement causes the
 procedure to exit after a message has been sent to the user's screen. If a
 username is given, the names of the rules owned by the user are retrieved
 from the appropriate system tables.

 RETURN is similar to BREAK used with WHILE procedures.


 WAITFOR

 The WAITFOR statement specifies a specific time of day, a time interval, or
 an event at which the execution of a statement block, stored procedure, or
 transaction is to occur.

 The WAITFOR statement has the following syntax:

   WAITFOR {DELAY "time" | TIME "time" | ERROREXIT | PROCESSEXIT}

 The DELAY option instructs SQL Server to wait until the specified amount of
 time has passed. TIME instructs SQL Server to wait until the specified time,
 given in one of the acceptable formats for datetime data.

 However, you cannot specify datesÄthe date portion of the datetime value is
 not allowed. The time you specify with WAITFOR TIME or WAITFOR DELAY can
 include hours, minutes, and secondsÄup to a maximum of 24 hours. Use the
 format hh:mm:ss. For example, WAITFOR time "16:23" instructs SQL Server to
 wait for 4:23 pm. The statement WAITFOR DELAY "01:30" instructs SQL Server
 to wait one hour and 30 minutes. For a review of the acceptable formats for
 TIME values, see Chapter 9, "Adding, Changing, and Removing Data."

 ERROREXIT instructs SQL Server to wait until a process terminates
 abnormally; PROCESSEXIT instructs SQL Server to wait until a process
 terminates for any reason. You can use WAITFOR ERROREXIT with a procedure
 that kills the abnormally terminated process to free system resources that
 would otherwise be taken up by an infected process. To find out which
 process is infected, check the sysprocesses table with the sp_who system
 procedure.

 This example instructs SQL Server to wait until 2:20 p.m., then to update
 the chess table with the next move and execute a stored procedure called
 sendmessage (which inserts a message into one of Judy's tables notifying her
 that a new move now exists in the chess table):

   begin
     waitfor time "14:20"
     insert chess(next_move)
     values('Q-KR5')
     execute sendmessage 'judy'
    end

 To send the message to judy after 10 seconds instead of waiting until 2:20,
 substitute this WAITFOR statement in the preceding example:

   waitfor delay "0:00:10"

 After you give the WAITFOR statement, you cannot use your connection to SQL
 Server until the time or event that you specified occurs.


 PRINT and RAISERROR

 The PRINT statement, used in the previous example, displays a user-defined
 message or the contents of a local variable (or global variable of type char
 or varchar) on the user's screen. The local variable must be declared within
 the same batch or procedure in which it is used. The message can be up to
 255 characters long.

 The PRINT statement has the following syntax:

   PRINT {"any ASCII text" | local variable |
    @version global variable}

 Here's an example:

   if exists (select zip from authors
     where zip = '94705')
    print "Berkeley author"

 Here's how you'd use PRINT to display the contents of a local variable:

   declare @msg char(50)
    select @msg = "What's up doc?"
    print @msg

 The RAISERROR statement both displays a user-defined error or local variable
 message on the user's screen, and sets a system flag to record the fact that
 an error has occurred. As with PRINT, the local variable must be declared
 within the same batch or procedure in which it is used. The message can be
 up to 255 characters long.

 The RAISERROR statement has the following syntax:

   RAISERROR number {"text of message" | local variable}

 The number is placed in the global variable @ERROR, which stores the error
 number most recently generated by SQL Server (whether it is associated with
 a system-supplied error message or a user-defined one.) Error numbers for
 user-defined error messages must be greater than 20,000. The message can be
 up to 255 characters long.

 Use RAISERROR instead of PRINT when you want an error number stored in
 @ERROR. For example, here's how you'd use RAISERROR in the indrules system
 procedure:

   raiserror 99999 "You must give a user name"


 Comments

 The comment notation is used to attach comments to SQL statements, batches,
 and stored procedures. Comments have the following syntax:

   /* text of comment */

 There is no maximum length for comments, and they can be inserted anywhere,
 on a line by themselves or at the end of a line. Multiple-line comments are
 fine, too, so long as each comment starts with a slash and an asterisk, and
 ends with an asterisk and a slash. Everything between "/*" and "*/" is
 treated as part of the comment. Comments can be nested.

 A stylistic convention that's often used for multiple-line comments is to
 begin the first line with "/*" and subsequent lines with "**". The comment
 is ended with "*/" as usual. Here's what it looks like:

   select * from titles
     /* A comment here might explain the rules
     ** associated with using an asterisk as
     ** shorthand in the select list.*/
    where price > $5

 Here's a procedure that includes a couple of comments:

   /* this procedure finds rules by username*/

    create procedure findrules2 @nm varchar(30) = null
    as if @nm is null/*if no parameter is given*/
    print "You must give a username"
    else
    begin
     select sysobjects.name, sysobjects.id, sysobjects.uid
     from sysobjects, master..syslogins
     where master..syslogins.name = @nm
     and sysobjects.uid = master..syslogins.suid
     and sysobjects.type = "R"
    end






 Chapter 12  Using Stored and System Procedures
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 Stored procedures are collections of SQL statements and control-of-flow
 language. They are compiled the first time they are executed and their
 execution plans are stored so that subsequent execution is very fast. Stored
 procedures take parameters and can call other procedures.

 Stored procedures greatly enhance the power, efficiency, and flexibility of
 SQL. Compiled procedures dramatically improve the performance of SQL
 statements and batches.

 Stored procedures differ from ordinary SQL statements and from batches of
 SQL statements in that they are precompiled. The first time you run a
 procedure, SQL Server's query processor analyzes it and prepares an
 execution plan that is stored in a system table. Subsequently, the
 precompiled procedure is executed according to the stored plan. Since most
 of the query-processing work has already been performed, stored procedures
 are executed almost instantaneously.

 SQL Server supplies a variety of stored procedures as convenient tools for
 the user. These stored procedures are called system procedures.

 This chapter includes


   ž   Explanations of creating and executing your own stored procedures.

   ž   A brief discussion of the system procedures. (For complete
       information, see the SQL Server Language Reference.)


 You create stored procedures with the CREATE PROCEDURE statement. To execute
 a stored procedure (either a system procedure or a user-defined procedure),
 use the EXECUTE statement. (Or you can use the name of the stored procedure
 alone as long as it is the first word of a statement or batch.)

 A simple stored procedure (without special features such as parameters) has
 the following syntax:

   CREATE PROCedure procedure_name
    AS SQL_statements

 Stored procedures are database objects, and their names must follow the
 rules for identifiers.

 Any number and kind of SQL statements can be included with the exception of
 CREATE statements. (See "Stored Procedure Rules" later in this chapter.) A
 procedure can be as simple as a single statement that lists the names of all
 the users in a database:

   create procedure namelist
    as select name from sysusers

 To execute a stored procedure, use the keyword EXECUTE and the name of the
 stored procedure, or just give the procedure's name (as long as it is
 submitted to SQL Server by itself or is the first statement in a batch). You
 can execute namelist in any of these ways:

   namelist
    execute namelist
    exec namelist

 A procedure can include more than one statement:

   create procedure showall as
    select count(*) from sysusers
    select count(*) from sysobjects
    select count(*) from syscolumns

 When the procedure is executed, the results of each statement are displayed
 in the order that the statement appears in the procedure:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   showall

                                   ------------

                                   5

                                   (1 row affected)

                                   ------------

                                   88

                                   (1 row affected)

                                   ------------

                                   349

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

                                   (1 row affected)



 When a CREATE PROCEDURE statement is successfully executed, the procedure's
 name is stored in sysobjects, its execution plan in sysprocedures, and its
 text in syscomments. Display the text of a procedure with the sp_helptext
 system procedure:

   sp_helptext showall

    text
    ----------------------------------------
    create procedure showall as
    select count(*) from sysusers
    select count(*) from sysobjects
    select count(*) from syscolumns

    (1 row affected)


 Creating and Executing Stored Procedures

 The CREATE PROCEDURE has the following syntax:

   CREATE PROCedure [owner.]procedure_name [;number]
    [[(]@parameter_name datatype [= default]
    [, @parameter_name datatype [= default]...][)]]
    [WITH RECOMPILE]
    AS SQL_statements

 You can create a procedure in the current database only.

 Permission to execute CREATE PROCEDURE defaults to the Database Owner, who
 can transfer it to other users.

 There are two formats for specifying parameters when you execute a stored
 procedure. Here are the two complete syntax statements:

   [EXECute] procedure_name[;number] [value [, value...]]
     [WITH RECOMPILE]

    [EXECute] procedure_name[;number]
     [@parameter = ] value [, [@parameter =] value]...]
     [WITH RECOMPILE]


 Parameters

 A parameter is a value supplied to the stored procedure. One or more
 parameters can optionally be declared in a CREATE PROCEDURE statement. The
 value of each declared parameter must be supplied by the user when the
 procedure is executed.

 A parameter name must be preceded by the "@" symbol and must conform to the
 rules for identifiers except that it can contain from 1 to 29 characters.
 Parameters must be given a system datatype or a user-defined datatype, and a
 length if required for the datatype. Parameter names are local to the
 procedure that creates them; the same parameter names can be used in other
 procedures.

 In a CREATE PROCEDURE statement, parameters can only take the place of
 constants. The name of a database object cannot be used to define a
 parameter; however, the stored procedure can look up an object name in the
 appropriate system table based on the value of a parameter given at
 execution time.

 Here's a stored procedure that is useful in the pubs database. Given an
 author's last and first name, it displays the names of any books he or she
 has written and each book's publisher.

   create proc au_info @lastname varchar(40),
     @firstname varchar(20) as
    select au_lname, au_fname, title, pub_name
    from authors, titles, publishers, titleauthor
    where au_fname = @firstname
     and au_lname = @lastname
     and authors.au_id = titleauthor.au_id
     and titles.title_id = titleauthor.title_id
     and titles.pub_id = publishers.pub_id

 Now we execute au_info:

   au_info Ringer, Anne

    au_lname au_fname  title      pub_name
    --------- ---------  ---------------------  ----------------
    Ringer  Anne   The Gourmet Microwave  Binnet & Hardley
    Ringer  Anne   Is Anger the Enemy?   New Age Books

    (2 rows affected)

 The following stored procedure queries the system tables. Given a table name
 as the parameter, the procedure shows the table name, index name, and index
 ID.

   create proc showind @table varchar(30) as
    select TABLE_NAME = sysobjects.name,
    INDEX_NAME = sysindexes.name, INDEX_ID = indid
    from sysindexes, sysobjects
    where sysobjects.name = @table
     and sysobjects.id = sysindexes.id

 The column headings (for example, TABLE_NAME) were added to improve the
 readability of the results.

 Here are the acceptable syntax forms for executing this (or any) stored
 procedure:

   execute showind titles

    execute showind @table = titles

    showind titles

 The last syntax form is acceptable so long as the statement is the only one
 or first one in a batch.

 Here are the results of executing showind in the pubs database when titles
 is given as the parameter:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         TABLE_NAME                                        INDEX_NAME    INDEX
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         TABLE_NAME                                        INDEX_NAME    INDEX

         ------------                                      ------------  -----

         titles                                            titleidind    1

         titles                                            titleind      2



         (2 rows affected)



 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 If you supply the parameters in the form "@parameter = value", you can
 supply the parameters in any order. Otherwise, you must supply parameters in
 the order of their CREATE PROCEDURE statement.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Default Parameters

 You can assign a default value for the parameter in the CREATE PROCEDURE
 statement. This value, which can be any constant, is taken as the parameter
 to the procedure if the user does not supply one.

 Here's a procedure that displays the names of all the authors that have
 written a book published by the publisher given as a parameter. If no
 publisher name is supplied, the procedure shows the authors published by
 Algodata Infosystems.

   create proc pub_info
     @pubname varchar(40) = "Algodata Infosystems" as
    select au_lname, au_fname, pub_name
    from authors a, publishers p, titles t, titleauthor ta
    where @pubname = p.pub_name
     and a.au_id = ta.au_id
     and t.title_id = ta.title_id
     and t.pub_id = p.pub_id

 Note that if the default value is a character string that contains embedded
 blanks or punctuation, it must be enclosed in single or double quotation
 marks.

 When you execute pub_info, you can give any publisher's name as the
 parameter value. If you don't supply any parameter, SQL Server uses the
 default, Algodata Infosystems.

   exec pub_info

    au_lname   au_fname   pub_name
    ---------------- ---------------- --------------------
    Green    Marjorie   Algodata Infosystems
    Bennet    Abraham    Algodata Infosystems
    O'Leary    Michael    Algodata Infosystems
    MacFeather   Stearns    Algodata Infosystems
    Straight   Dick    Algodata Infosystems
    Carson    Cheryl    Algodata Infosystems
    Dull    Ann     Algodata Infosystems
    Hunter    Sheryl    Algodata Infosystems
    Locksley   Chastity   Algodata Infosystems

    (9 rows affected)

 You assign titles as the default value for the @table parameter in this
 procedure, showind2:

   create proc showind2 @table varchar(30) = titles as
    select TABLE_NAME = sysobjects.name,
    INDEX_NAME = sysindexes.name, INDEX_ID = indid
    from sysindexes, sysobjects
    where sysobjects.name = @table
     and sysobjects.id = sysindexes.id

 The column headings (for example, TABLE_NAME) clarify the results display.
 Here's what the procedure shows for the authors table:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         showind2 authors
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         showind2 authors



         TABLE_NAME                                          INDEX_NAME  INDEX

         ----------                                          ----------  -----

         authors                                             auidind     1

         authors                                             aunmind     2



         (2 rows affected)



 If the user does not supply a value, SQL Server uses the default, titles.

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         showind2



         TABLE_NAME                                          INDEX_NAME  INDEX

         ----------                                          ----------  -----

         titles                                              titleidind  1

         titles                                              titleind    2



         (2 rows affected)



 If a parameter is expected but none is supplied, and a default value is not
 supplied in the CREATE PROCEDURE statement, SQL Server displays an error
 message.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 The error message lists names of expected parameters in reverse order to
 their order in the CREATE PROCEDURE statement.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 NULL as Default Parameter

 The default can be the value NULL. In this case, if the user does not supply
 a parameter, SQL Server executes the stored procedure according to its other
 statements. No error message is displayed.

 The procedure definition can also specify that some other action be taken if
 the user does not give a parameter. Here's an example:

   create procedure showind3 @table varchar(30) = null as
    if @table is null
     print "Please give a table name"
    else
     select TABLE_NAME = sysobjects.name,
     INDEX_NAME = sysindexes.name, INDEX_ID = indid
     from sysindexes, sysobjects
     where sysobjects.name = @table
     and sysobjects.id = sysindexes.id

 If the user fails to give a parameter, SQL Server prints the user-supplied
 message on the screen.

 (For other examples of setting the default to NULL, examine the text of
 system procedures, using sp_helptext.)


 Wildcard Characters in the Default Parameter

 The default can include the wildcard characters (%, _, [] and [^]) if the
 procedure uses the parameter with the LIKE keyword.

 For example, showind can be modified to display information about the system
 tables if the user does not supply a parameter, like this:

   create procedure showind4 @table varchar(30) = "sys%" as
    select TABLE_NAME = sysobjects.name,
    INDEX_NAME = sysindexes.name, INDEX_ID = indid
    from sysindexes, sysobjects
    where sysobjects.name like @table
    and sysobjects.id = sysindexes.id


 Using More Than One Parameter

 The following variant of the stored procedure au_info has defaults with
 wildcard characters for both parameters:

   create proc au_info2 @lastname varchar(30) = "D%",
    @firstname varchar(18) = "%" as
    select au_lname, au_fname, title, pub_name
    from authors, titles, publishers, titleauthor
    where au_fname like @firstname
     and au_lname like @lastname
     and authors.au_id = titleauthor.au_id
     and titles.title_id = titleauthor.title_id
     and titles.pub_id = publishers.pub_id

 If au_info2 is executed with no parameters, all the authors with last names
 beginning with "D" are displayed:

   au_info2

    au_lname au_fname title      pub_name
    -------- -------- ------------------------- --------------------
    Dull  Ann   Secrets of Silicon Valley Algodata Infosystems
    DeFrance Michel  The Gourmet Microwave  Binnet & Hardley

    (2 rows affected)

 If defaults are available for parameters, parameters can be omitted at
 execution, beginning with the last parameter. You cannot skip a parameter
 unless NULL is its supplied default.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 If you supply parameters in the form "@parameter = value", you can supply
 parameters in any order. You can also omit a parameter for which a default
 has been supplied.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 As an example of omitting the second parameter when defaults for two
 parameters have been defined, you can find the books and publishers for all
 authors with the last name "Ringer" like this:

   au_info2 Ringer

    au_lname au_fname  title      pub_name
    --------- ---------  ------------------------ ----------------
    Ringer  Anne   The Gourmet Microwave  Binnet & Hardley
    Ringer  Anne   Is Anger the Enemy?   New Age Books
    Ringer  Albert   Is Anger the Enemy?   New Age Books
    Ringer  Albert   Life Without Fear   New Age Books

    (4 rows affected)


 Procedure Groups

 The optional semicolon and (integer) number after the name of the procedure
 in the CREATE PROCEDURE and EXECUTE statements allow you to group procedures
 of the same name so that they can be dropped together with a single DROP
 PROCEDURE statement.

 Procedures used in the same application are often grouped this way. For
 example, you might create a series of procedures called orders;1, orders;2,
 and so on. The command drop proc orders would drop the entire group.

 Once procedures have been grouped by appending a semicolon and number to
 their names, they cannot be dropped individually. For example, the command
 drop proc orders;2 is not allowed.


 WITH RECOMPILE in CREATE PROCEDURE

 In the CREATE PROCEDURE statement, the optional clause WITH RECOMPILE comes
 just before the SQL statements. It instructs SQL Server not to save a plan
 for this procedure. The procedure is recompiled each time it is executed.

 In the absence of WITH RECOMPILE, SQL Server stores the execution plan that
 it created, which is based on the parameter given to the procedure on its
 first execution. Usually, this execution plan is the best one for most of
 the values that are passed to the procedure as parameters.

 However, it's possible that parameter values supplied for subsequent
 executions would have caused SQL Server to come up with an execution plan
 different from the one it created when the parameter was first executed.

 Use WITH RECOMPILE in a CREATE PROCEDURE statement when you expect that the
 parameters you supply to the procedure won't be typicalÄthat is, they would
 not result in the same optimal plan.


 WITH RECOMPILE in EXECUTE

 In the EXECUTE statement, the optional clause WITH RECOMPILE comes after any
 parameters. It instructs SQL Server to compile a new plan for this execution
 of the procedure only. The original plan is used for subsequent executions.


 Use WITH RECOMPILE when you execute a procedure if the parameter you're
 supplying is atypicalÄthat is, if the plan stored with the procedure might
 not be optimal for this parameter.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 If you use SELECT * in your CREATE PROCEDURE statement, the procedure (even
 if you use the WITH RECOMPILE option to EXECUTE) will not pick up any new
 columns you may have added to the table. You must drop the procedure and
 recreate it.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Stored Procedure Rules

 Some additional rules for creating stored procedures are as follows:


   ž   CREATE PROCEDURE statements cannot be combined with other SQL
       statements in a single batch.

   ž   The CREATE PROCEDURE definition itself can include any number and kind
       of SQL statements with the exception of CREATE statements:

       CREATE VIEW

       CREATE DEFAULT

       CREATE RULE

       CREATE TRIGGER

       CREATE PROCEDURE

   ž   Other database objects can be created within a procedure. You can
       reference an object you've created in the same procedure as long as it
       is created before it is referenced. (The CREATE statement for the
       object must come first in the actual order of the statements within
       the procedure.)

   ž   Within a stored procedure, you cannot create an object, drop it, and
       then create a new object with the same name.

   ž   If you execute a procedure that calls another procedure, the called
       procedure can access objects created by the first procedure.

   ž   You can reference temporary tables within a procedure.

   ž   If you create a temporary table inside a procedure, the temporary
       table exists only for the purposes of the procedure; it disappears
       when you exit the procedure.

   ž   The maximum number each for parameters, local variables, and global
       variables in a procedure is 40.



 Qualifying Names Inside Stored Procedures

 Inside a stored procedure, object names used with utility statements must be
 qualified with the object owner's name if other users are to make use of the
 stored procedure. The utility statements are ALTER TABLE, CREATE TABLE, DROP
 TABLE, TRUNCATE TABLE, CREATE INDEX, DROP INDEX, UPDATE STATISTICS, DBCC.
 For example, user mary, who owns table marytab, should qualify the name of
 her table when it is used with one of these utility statements if she wants
 other users to be able to execute the procedure in which the table is used:


   create procedure p1
    as
    create index marytab_ind
    on mary.marytab(col1)

 The reason for this rule is that object names are resolved when the
 procedure is run. If marytab had not been qualified and user John had tried
 to execute the procedure, SQL Server would have looked for a table called
 marytab owned by John. The preceding example shows the correct usage: it
 tells SQL Server to look for a table called marytab owned by Mary.

 Object names used with other statements (for example, SELECT or INSERT)
 inside a stored procedure need not be qualified because the names are
 resolved when the procedure is compiled.


 Dropping Procedures

 The DROP PROCEDURE statement removes procedures from the current database.
 The DROP PROCEDURE statement has the following syntax:

   DROP PROCedure procedure_name [, procedure_name...]

 If a stored procedure that has been dropped is called by another stored
 procedure, SQL Server displays an error message. However, if a new procedure
 of the same name is defined to replace the one that was dropped, other
 procedures that reference it can call it successfully.

 A procedure group (more than one procedure with the same name but different
 ;number suffixes) can be dropped with a single DROP PROCEDURE statement.
 Once procedures have been grouped, procedures within the group cannot be
 dropped individually.


 Renaming Procedures

 You can rename a stored procedure with the sp_rename system procedure. The
 sp_rename system procedure has the following syntax:

   sp_rename objname, newname

 For example, to rename showall to countall:

   sp_rename showall, countall

 Of course, the new name must follow the rules for identifiers. You can only
 change the name of stored procedures that you own. The Database Owner can
 change the name of any user's stored procedure. The stored procedure must be
 in the current database.

 Problems can arise with sp_rename if you change the name of an object
 referenced by a stored procedure. A stored procedure that references a table
 or view whose name has been changed may seem to work fine for a while. In
 fact, it works only until SQL Server recompiles it. Recompilation takes
 place for many reasons and without notification to the user.

 When a procedure stops working because one of its underlying objects has
 been renamed, you must drop the stored procedure and recreate it so that its
 text reflects the new name of the object it references. To avoid such
 problems, the safest course is not to rename any tables or views that are
 referenced by a procedure. If you do rename tables or views, be sure to
 change the definitions of dependent procedures when you rename them.


 Help on Procedures

 Several system procedures provide information from the system tables about
 stored procedures.


 sp_helpsql

 To display information on SQL statements, system procedures, and other
 topics, execute the sp_helpsql system procedure:

   sp_helpsql "begin transaction"

    BEGIN TRANSACTION

    Marks the starting point of a user-specified transaction.

    BEGIN TRANsaction [transaction_name]


 sp_help

 You can get a report on a stored procedure with the sp_help system
 procedure. For example, you can get information on the stored procedure
 byroyalty, which is part of the pubs database, like this:

   sp_help byroyalty

    Name    Owner   Type
    ------------  ---------  -----------------
    byroyalty   dbo    stored procedure

    Data_located_on_segment    When_created
    -----------------------    ---------------------
    default        Feb 9 1987 3:56PM

    (0 rows affected)

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         Parameter_name                         Type         Length     Param_

         --------------                         -----------  ---------  ------

         @percentage                            int          4          1



 You can get help on a system procedure by executing sp_help when using the
 master database.


 sp_helptext

 To display the text of the CREATE PROCEDURE statement, execute the
 sp_helptext system procedure:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                   sp_helptext byroyalty



                                   -----------

                                   1

                                   (1 row affected)



                                   text
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                  text

                                   -------------------------------------------

                                   create procedure byroyalty @percentage int

                                   as

                                   select au_id from titleauthor

                                   where titleauthor.royaltyper = @percentage

                                   (1 row affected)



 You can view the text of a system procedure by executing sp_helptext when
 using the master database.


 sp_depends

 The sp_depends system procedure lists all the stored procedures that
 reference the object you specify or all the procedures that are depended
 upon by it. This statement lists all the objects referenced by the
 user-created stored procedure byroyalty:

   sp_depends byroyalty

    Things the object references in the current database.

    object    type    updated   selected
    ---------------- ----------- -  --------  --------
    dbo.titleauthor  user table   no    no

 The following statement uses sp_depends to list all the objects that
 reference the table titleauthor:

   sp_depends titleauthor

    Things inside the current database that reference the object.

    object     type
    -----------------  ---------------------------
    dbo.titleview   view
    dbo.reptq2    stored procedure
    dbo.byroyalty   stored procedure

 The system procedures are briefly discussed in "System Procedures" later in
 this chapter. (For complete information about the system procedures, see the
 SQL Server Language Reference.)


 Stored Procedures and Permissions

 Stored procedures can serve as security mechanisms, since a user can be
 granted permission to execute a stored procedure even if he or she does not
 have permissions on tables or views referenced in it. (For details, see
 Chapter 14, "Advanced Topics for Database Owners.")


 System Procedures

 The system procedures are provided for your convenience as


   ž   Shortcuts for retrieving information from the system tables

   ž   Mechanisms for accomplishing database administration and other tasks
       that involve updating system tables


 Most of the time, system tables are updated only through stored procedures.
 The System Administrator can allow direct updates of system tables by
 changing one of the configuration options and issuing the RECONFIGURE WITH
 OVERRIDE statement. (See the SQL Server System Administrator's Guide for
 details.)

 System procedures are created in the master database and are owned by the
 System Administrator.

 You can run system procedures from any database. If a system procedure is
 executed from a database other than master, any references to system tables
 are mapped to the database from which the procedure is being run. For
 example, if the Database Owner of pubs runs sp_adduser from pubs, the new
 user is added to pubs..sysusers.

 When the parameter for a system procedure is an object name and the object
 name is qualified by a database name or owner name, the entire name must be
 enclosed in single or double quotation marks.

 Since system procedures are located in the master database, their
 permissions are also set there. Some of the system procedures can be run
 only by Database Owners. These procedures make sure that the user executing
 the procedure is the owner of the database from which they are being
 executed.

 Other system procedures can be executed by any user who has been granted
 EXECUTE permission on them, but this permission must be granted in master.
 This situation has two consequences:


   ž   A user can have permission to execute a system procedure either in all
       databases or in none of them.

   ž   The owner of a user database cannot directly control permissions on
       the system procedures within his or her own database.


 (See the SQL Server System Administrator's Guide for details.)

 The system procedures can be divided into three categories. The following
 three sections discuss these categories.


 User Identification and Permissions

 This category includes procedures for


   ž   Adding, dropping, and reporting on login IDs on SQL Server

   ž   Adding, dropping, and reporting on users, groups, and aliases in a
       database

   ž   Changing passwords and default databases

   ž   Changing the owner of a database


 The procedures in this category are sp_addalias, sp_addgroup, sp_addlogin,
 sp_adduser, sp_changedbowner, sp_changegroup, sp_defaultdb, sp_dropalias,
 sp_dropgroup, sp_droplogin, sp_dropuser, sp_helpgroup, sp_helprotect,
 sp_helpuser, sp_password.


 Data Definition and Database Objects

 This category includes procedures for


   ž   Binding and unbinding rules and defaults

   ž   Adding, dropping, and reporting on primary keys, foreign keys, and
       common keys

   ž   Adding, dropping, and reporting on user-defined datatypes

   ž   Renaming database objects and user-defined datatypes

   ž   Reporting on database objects; user-defined datatypes; dependencies
       among database objects, databases, and indexes; and space used by
       tables and indexes


 The procedures in this category are sp_addtype, sp_bindefault, sp_bindrule,
 sp_commonkey, sp_depends, sp_dropkey, sp_droptype, sp_foreignkey, sp_help,
 sp_helpdb, sp_helpindex, sp_helpjoins, sp_helpkey, sp_helptext,
 sp_primarykey, sp_rename, sp_spaceused, sp_unbindefault, sp_unbindrule.


 Other System Procedures

 This category includes procedures for


   ž   Adding, dropping, and reporting on dump devices

   ž   Reporting on locks, the database options that are set, and the users
       currently running processes

   ž   Changing and reporting on configuration options

   ž   Monitoring SQL Server activity


 The procedures in this category are sp_addumpdevice, sp_configure,
 sp_dboption, sp_diskdefault, sp_dropdevice, sp_helpdevice, sp_lock,
 sp_logdevice, sp_markreport, sp_monitor, sp_who.

 In addition, there are system procedures that are called by other system
 procedures; users cannot call them directly. They are sp_abort_xact,
 sp_addsegment, sp_commit_xact, sp_dropsegment, sp_extendsegment,
 sp_fixindex, sp_lookup, sp_markreport, sp_placeobject, sp_remove_xact,
 sp_scan_xact, sp_start_xact, sp_stat_xact.

 More information about the system procedures that accomplish administrative
 tasks is given in the SQL Server System Administrator's Guide. (For complete
 information about the system procedures, see the SQL Server Language
 Reference.)






 Chapter 13  Creating Triggers
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 A trigger is a special kind of stored procedure that goes into effect when
 you modify data in a specified table. Triggers are often created to enforce
 referential integrity or consistency among logically related data in
 different tables.

 The main advantage of triggers is that they are automaticÄthey work no
 matter what caused the data modificationÄa clerk's entry, an application
 action, a report calculation. Each trigger is specific to one or more of the
 data modification operations: UPDATE, INSERT, or DELETE.

 Triggers are useful in the following situations:


   ž   Triggers can cascade changes through related tables in the database to
       ensure referential integrityÄthat is, to guarantee that when a value
       in one table is changed, all other instances of that value in other
       tables are also changed.

       Such a trigger might go into effect when you try to change a primary
       key and you want the change to be reflected in matching foreign keys.
       For example, update triggers on the title_id column of the titles
       tableÄa primary keyÄcould go into effect whenever titles.title_id is
       updated, making the same change to titleauthor.title_id,
       sales.title_id, and roysched.title_id.

   ž   Triggers can disallow or roll back changes that would violate
       referential integrity, canceling the attempted data modification
       transaction.

       Such a trigger might go into effect when you try to change a foreign
       key and the new value does not match its primary key. For example, you
       could create an insert trigger on titleauthor.title_id that rolled
       back an insert if the new value did not match some value in
       titles.title_id.

   ž   Triggers can keep summary data (ongoing tallies) up to date. For
       example, you can create a trigger that updates the titles.ytd_sales
       column every time a new sale is recorded in sales.

   ž   Triggers can enforce restrictions much more complex than those defined
       with rules. Unlike rules, triggers can reference columns or database
       objects. For example, a trigger

       can roll back updates that attempt to increase a book's price by more
       than 1% of its advance.

   ž   Triggers can also find the difference between the state of a table
       before and after a data modification, and take action(s) based on that
       difference.


 This chapter first summarizes trigger definition syntax and rules. Then it
 discusses and gives examples of triggers for the typical uses listed here.
 You may want to use these examples as templates for your own triggers.

 The triggers discussed in this chapter are not included in the pubs database
 shipped with your copy of SQL Server. To work with the examples shown in
 this chapter, be sure to drop all existing triggers first. Then create each
 trigger example by using the CREATE TRIGGER statement. After you are through
 using a trigger, drop it before creating another trigger.

 The reason for this protocol is explained more fully later in this chapter.
 Triggers on different tables can interact, disabling one other.


 Defining a Trigger: CREATE TRIGGER

 A trigger is a database object. You create a trigger by specifying which
 data modification statement will activate the trigger and which table will
 be the target. Then you specify the action or actions the trigger is to
 take.

 Here is a simple example. This trigger prints a message every time anyone
 tries to add data to or change the data in the titles table:

   create trigger t1
    on titles
    for insert, update
    as
    print "Make an entry in titleauthor to link this book"
    print "to its writer."

 The CREATE TRIGGER statement has the following syntax:

   CREATE TRIGGER [owner.]trigger_name
     ON [owner.]table_name
     FOR {INSERT | UPDATE | DELETE}
      [, {INSERT | UPDATE | DELETE}...]
     AS SQL_statements
     IF UPDATE (column_name)
      [{AND | OR} UPDATE (column_name...)]

 Here's an analysis of the trigger keywords:


   ž   The CREATE clause creates the trigger and names it. A trigger's name
       must conform to the rules for identifiers.

   ž   The ON clause gives the name of the table that activates the trigger.
       This table is sometimes called the trigger table.

       A trigger is created in the current database, although it can
       reference objects in other databases. The owner name that qualifies
       the trigger name must be the same as the owner name on the table: no
       one can create a trigger on a table except the table owner. If the
       table owner is specified in one name, it must also be included in the
       other name.

   ž   The FOR clause specifies which data modification statements applied
       toward the trigger table activate the trigger. In the simple earlier
       example, either an INSERT or an UPDATE to titles makes the message
       print. Triggers can also be defined for the DELETE statement.

   ž   The SQL statements in the AS clause cannot include SELECT statements
       that return results: a trigger does not return data to the user. With
       this exception, a trigger's SELECT statements can be any number and
       kind, and often include control-of-flow language.

       The SQL statements specify trigger conditions and trigger actions.
       Trigger conditions specify additional criteria that determine whether
       the attempted INSERT, DELETE, or UPDATE will cause the trigger
       action(s) to be carried out. Trigger conditions often include a
       subquery preceded by the keyword IF.

       The trigger actions specified in the SQL statements go into effect
       when the user action (UPDATE, INSERT, or DELETE) is attempted. If
       multiple trigger actions are specified, they are grouped with BEGIN
       and END.


 Some special syntax is used by the CREATE TRIGGER statement:


   ž   The name deleted is the name of a logical table. Rows are removed from
       the trigger table and transferred to the deleted table as soon as the
       DELETE statement is executed: deleted and the trigger table ordinarily
       have no rows in common.

       You can operate on rows in deleted just like you can on rows in other
       tables. In fact, the deleted table is often joined to one or more of
       the tables being changed by the trigger. The trigger can examine the
       rows in deleted to determine whether or how the trigger action(s)
       should be carried out. Use deleted with DELETE and UPDATE triggers
       only.

   ž   The name inserted is also the name of a logical table. This table
       holds the rows added to the trigger table by means of an INSERT or
       UPDATE statement.

       When an INSERT or UPDATE statement is executed, rows are added to the
       trigger table and to inserted at the same time. So the rows in
       inserted are always duplicates of one or more rows in the trigger
       table. Use inserted with INSERT and UPDATE triggers only.

   ž   The keywords IF UPDATE (column_name) are used to test whether the
       specified column has been modified in any way. This mechanism allows
       trigger actions to be associated with changes to the data in
       particular columns.



 Removing a Trigger: DROP TRIGGER

 You can remove a trigger by dropping it or as a side effect of dropping the
 trigger table.

 The DROP TRIGGER statement has the following syntax:

   DROP TRIGGER [owner.]trigger_name [, [owner.]trigger_name...]

 DROP TRIGGER permission defaults to the trigger table owner and is not
 transferable.


 Trigger Rules and Considerations

 This section discusses other factors to consider when using triggers.


 Triggers and Permissions

 A trigger is defined on a particular table. Only the owner of the table has
 CREATE TRIGGER and DROP TRIGGER permissions for the table. These permissions
 cannot be transferred to others.

 SQL Server will accept a trigger definition that affects tables you do not
 own, and attempts actions for which you do not have permission. The
 existence of such a trigger aborts any subsequent attempt to modify the
 trigger table. You must rectify the permissions or drop the trigger. For
 example, Joe owns sales and creates a trigger on it. The trigger is supposed
 to update titles.ytd_sales when sales.qty is updated. However, Mary is the
 owner of titles. When Joe tries to update sales, SQL Server detects the
 trigger and Joe's lack of permissions on titles and rolls back the update
 transaction. Joe must either get update permission on titles.ytd_sales from
 Mary or drop the trigger on sales.

 When a table is dropped, any triggers associated with it are automatically
 dropped.

 Using IF UPDATE (column_name) never makes sense in a DELETE trigger.


 Restrictions

 A table can have a maximum of three triggers: one update trigger, one insert
 trigger, and one delete trigger.

 Each trigger can apply to only one table. However, a single trigger may
 apply to all three user actions (UPDATE, INSERT, and DELETE).

 You cannot create a trigger on a view or a temporary object, though triggers
 can reference views or temporary objects.

 Although a TRUNCATE TABLE statement is, in effect, like a DELETE without a
 WHERE clause (it removes all rows), it cannot activate a trigger because it
 is not logged.


 Implicit and Explicit Null Values

 The IF UPDATE (column_name) clause is true for an INSERT statement whenever
 the column is assigned a value in the select list or in the VALUES clause.
 An explicit NULL or a default assigns a value to a column and thus activates
 the trigger. With an implicit NULL, if the value is not specified by the
 query or default, the trigger on that column is not activated. These
 examples clarify the situation:

   CREATE TABLE junk
    (a int null, b int not null)
    INSERT junk (a, b)  /*IF UPDATE is true for either column,*/
    VALUES (1, 2)   /*The trigger is activated.*/
    INSERT junk    /*IF UPDATE is true for either column,*/
    VALUES (1, 2)   /*The trigger is activated.*/
    INSERT junk    /*Explicit NULL: */
    VALUES (NULL, 2)  /*IF UPDATE is true for either column,*/
          /*The trigger is activated.*/
    INSERT junk (b)   /* If default on column a,*/
    VALUES (2)    /*IF UPDATE is true for either column,*/
          /*The trigger is activated.*/
    INSERT junk (b)   /* If no default on column a,*/
    VALUES (2)    /*IF UPDATE is not true for column a,*/
          /*The trigger is not activated.*/


 Nested Triggers

 One trigger cannot call another. If a trigger changes a table on which there
 is another trigger, the second trigger won't be executed. In other words,
 triggers cannot be nested. If you need a trigger to cause two actions, try
 to put both of the actions into a single trigger. (Or consider using a
 stored procedure instead.) For example, you might create an INSERT trigger
 on the sales table that updates titles.ytd_sales. You'd also like a trigger
 on updates to titles.ytd_sales that checks the titles.royalty column and
 changes it in turn, if necessary. The correct method is not to write a
 second trigger that changes titles.royalty when titles.ytd_sales changes.
 Such a trigger would be disabled by the existing trigger that updates
 titles.ytd_sales when sales is updated. Rather, you should write one trigger
 that takes both actionsÄthe update of titles.ytd_sales and the (possible)
 update of titles.royalty.


 Triggers and Performance

 In performance terms, trigger overhead is usually low. The time involved in
 running a trigger is spent mostly in referencing other tables, which may be
 either in memory or on disk. The deleted and inserted tables, often
 referenced by triggers, are always in memory rather than on the disk because
 they are logical tables. The location of other tables referenced by the
 trigger determines the amount of time the operation takes.

 Once a trigger is defined, the user action (UPDATE, DELETE, or INSERT) on
 the table it affects is always implicitly part of a transaction, along with
 the trigger itself. If an error is detected, the transaction rolls back.


 SET Statements in Triggers

 You can use the SET statement inside a trigger. The SET option you invoke
 remains in effect during the execution of the trigger and then reverts to
 its former setting. It is often wise to SET NOCOUNT ON at the beginning of
 the trigger definition so that users won't get messages telling them how
 many rows were affected by a trigger. Some users may not even know the
 trigger exists.


 Trigger Storage

 Triggers, like all objects, are listed in sysobjects by name. The type
 column of sysobjects identifies triggers with the abbreviation "TR." This
 query finds the triggers that exist in a database:

   select *
    from sysobjects
    where type = "TR"

 The CREATE TRIGGER statement for each trigger is stored in syscomments. You
 can display the trigger definition with the sp_helptext system procedure, as
 explained later.

 Execution plans for triggers are stored in sysprocedures.


 Renaming Triggers

 You can rename a trigger with the sp_rename system procedure. The sp_rename
 system procedure has the following syntax:

   sp_rename objname, newname

 There is a trigger in the pubs database named deltitle. Here's how you would
 rename it to goodriddance:

   sp_rename deltitle, goodriddance

 Only the trigger owner and the Database Owner can change the name of a
 trigger. The trigger must be in the current database.

 Now, change this trigger's name back to the original name:

   sp_rename goodriddance, deltitle

 Problems can arise with sp_rename if you change the name of an object
 referenced by a trigger. A trigger that references a table or view whose
 name has been changed may work fine for a while. In fact, it works until SQL
 Server recompiles it. Since recompilation takes place for many reasons and
 without notification to the user, the trigger may cease working without your
 realizing it.

 At that point, you must drop the trigger and re-create it so that its text
 reflects the new name of the object which it references. To avoid such
 problems, do not rename any tables or views that are referenced by a trigger
 or change the definitions of their dependent triggers when you rename them.



 Help on Triggers

 Several system procedures provide information from the system tables about
 triggers.


 sp_help

 You can get a report on a trigger with the sp_help system procedure. For
 example, to get information on deltitle, type

   sp_help deltitle

    Name   Owner  type   Created_on
    --------  -----  -------   -----------------
    deltitle  dbo   trigger   Feb 9 1987 3:56PM

    (1 row affected)


 sp_helptext

 To display the text of the CREATE TRIGGER statement, execute the sp_helptext
 system procedure:

   sp_helptext deltitle

    ----------
    1

    (1 row affected)

    text
    -------------------------------------------
    create trigger deltitle
    on titles
    for delete
    as
    if (select count(*) from deleted, sales
    where sales.title_id = deleted.title_id) >0
    begin
    rollback transaction
    print "You can't delete a title with sales."
    end

    (1 row affected)


 sp_depends

 The sp_depends system procedure lists all the triggers that reference the
 object (for example, table or view) or all the tables or views that the
 trigger affects. This example shows how to use sp_depends to get a list of
 all the objects referenced by the trigger deltitle:

   sp_depends deltitle

    Things the object references in the current database.

    object    type   updated  selected
    ---------   ----------  -------  --------
    dbo.sales   user table  no   no

 This statement lists all the objects that reference the sales table:

   sp_depends sales

    Things inside the current database that reference the object.

    object    type
    ------------  ---------------
    dbo.deltitle  trigger


 Trigger Examples

 This section provides examples of typical triggers, which you may want to
 use as templates for your own triggers. Before looking at the examples, it's
 important to review some referential integrity concepts.

 Referential integrity is based on the use of primary and foreign keys:


   ž   The primary key is the column or combination of columns that uniquely
       identifies a row. It must always be NOT NULL and have a unique index.
       A table with a primary key that is used for joins with other tables is
       called a master table.

       In the pubs database, for example, the title_id column is the primary
       key of titles. It uniquely identifies the books in titles and is often
       joined with title_id in titleauthor, sales, and roysched. The titles
       table is the master table.

   ž   The foreign key is a column or combination of columns in one table
       whose values match the values in the primary key of another table. It
       doesn't have to be NOT NULL nor does it have to be unique. The foreign
       key values are copies of the primary key values, and hence no value in
       the foreign key should ever exist unless the same value exists in the
       primary key. Tables with foreign keys are often called detail or
       dependent tables to the master table.

       The title_id columns in titleauthor, sales, and roysched are foreign
       keys; these tables are detail tables.


 Referential integrity triggers keep the values of foreign keys in line with
 those in primary keys. Each of the following sections looks at a different
 kind of modification to a primary or foreign key value, analyzes what's
 normally required in terms of protection, and suggests a solutionÄoften a
 trigger.


 Deleting a Primary Key

 When you delete a primary key row, you want to delete the entries for that
 key in dependent tables. In other words, to ensure referential integrity,
 detail rows must be removed when the master row is deleted. A trigger that
 performs a cascading delete is required.

 Here's an example. When a DELETE statement on titles is executed, the row is
 removed from titles and added to deleted. A trigger checks the dependent
 tablesÄtitleauthor, sales, and royschedÄto see if they have any rows with a
 title_id that matches the title_id removed from titles (now stored in the
 deleted table). If the trigger finds any such rows, it removes them.

   create trigger delcascadetrig
    on titles
    for delete
    as
    delete titleauthor
    from titleauthor, deleted
     where titleauthor.title_id = deleted.title_id
      /* Remove titleauthor rows
      ** that match deleted (titles) rows.*/
    delete sales
    from sales, deleted
     where sales.title_id = deleted.title_id
      /* Remove sales rows
      ** that match deleted (titles) rows.*/
    delete roysched
    from roysched, deleted
     where roysched.title_id = deleted.title_id
      /* Remove roysched rows
      ** that match deleted (titles) rows.*/

 In actual practice, you may find that you want to keep some of the detail
 rows, either for historical purposes (to check how many sales were made on
 discontinued titles while they were active) or because transactions on the
 detail rows are not yet complete. A trigger would have to take these factors
 into consideration.


 Inserting a Primary Key

 When you insert a row with a primary key into a table, you must make sure
 the entry is unique. In most cases, the unique index on the primary key will
 prevent duplicates. If, for some reason, the primary key does not have a
 unique index, a trigger can provide a check.

 The trigger example that follows enforces uniqueness on a primary key. The
 trigger rolls back (cancels) any insert containing an au_id that already
 exists in the authors table. To test it, first drop the unique index:

   drop index authors.auidind

 You can create a trigger that enforces uniqueness (for an attempted insert
 of a single, non-null value) by testing whether the number of rows in
 authors that join with inserted is greater than one.

 Recall that an INSERT statement adds rows to inserted and to the trigger
 table at the same time so that there will always be one (temporary) join
 between inserted and the trigger table, even if the INSERT subsequently
 fails.

 If a count of the rows that join between inserted and the trigger table is
 greater than one, this means that the key in the inserted table has joined
 with an existing key in the trigger table as well as with the newly inserted
 key.

 If this is the case, this trigger cancels the insert and prints a message.
 If not, it proceeds with the insert and prints no message.

   create trigger uniqinsertrig
    on authors
    for insert
    as if
    (select count(*)
    from authors, inserted
    where authors.au_id = inserted.au_id) >1
    begin
     rollback transaction
     print "At least one au_id is a duplicate: the transaction"
     print "cannot be completed."
    end


 Updating a Primary Key

 When you update (change) a primary key, you want to protect referential
 integrity either by cascading the master row change through all the detail
 rows or by rolling back the update altogether.

 It's best to prohibit any editing changes to a primary key. Rather than
 editing a primary key, delete the whole row and then insert a new one.

 The best way to prohibit updates to a primary key is to revoke all
 permissions on it. However, if you want to prohibit updates only under
 certain circumstances, use a trigger.

 The trigger that follows, for example, prevents updates to titles.title_id
 only on the weekend. (Such a trigger might be desirable for any type of
 column, not just for a primary key.)

 The IF UPDATE clause in stopupdatetrig allows you to focus on a particular
 column, titles.title_id. Modifications to the data in that column cause the
 trigger to go into action. Changes to the data in other columns do not. When
 this trigger detects an update that touches the IF UPDATE column, it cancels
 the update and prints a message.

   create trigger stopupdatetrig
    on titles
    for update
    as
    if update (title_id)
    and datename(dw, getdate())
    in ("Saturday", "Sunday")
    begin
     rollback transaction
     print "We don't allow monkeying with primary keys
     on the weekend!"
    end
      /* If titles.title_id changes on
      ** Saturday or Sunday, cancel the update. */


 Deleting a Foreign Key

 When you delete a foreign key row, you don't need a trigger to maintain
 referential integrity. The primary key row in the master table is not
 affected. Other foreign keys relate to the primary key, not to another
 foreign key.


 Inserting a Foreign Key

 When you insert a new foreign key row, you want to make sure the foreign key
 matches a primary key. The trigger should check for joins between the
 inserted rows and the rows in the primary key table, and then roll back any
 inserts of foreign keys that do not match a key in the primary key table. To
 accomplish this, you can use a NOT EXISTS test. This attempted insert
 differs from the previous one in that you check for joins between inserted
 and an outside table, the primary key table, rather than checking for joins
 between inserted and the trigger table.

 The following trigger compares the title_ids from the inserted table with
 those from the titles table. It assumes that you are making some entry for
 the foreign key and that you are not inserting a null value. If the join
 fails, the transaction is rolled back.

   create trigger forinsertrig1
    on sales
    for insert
    as if
    not exists
    (select * from titles, inserted
    where titles.title_id = inserted.title_id)

   begin
     rollback transaction
     print "No! The title_id doesn't exist in titles."
     end
      /* cancel the insert and print a message.*/
    else
     print "Added, because the title_id did exist in titles."
      /* Otherwise, allow it. */

 To illustrate the use of ELSE, this trigger prints one message if the INSERT
 is rolled back and another if it is accepted. Most of the time, you would
 want a message only in the first case.


 Inserting a NULL Foreign Key

 If the table containing the foreign key column permits null values for the
 foreign key column, you can add a line to the previous trigger that will
 handle an insert of a NULL foreign key. This version of the trigger permits
 the insert of a NULL foreign key, then rejects any invalid foreign keys that
 are not null values.

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 NOTE

 This hypothetical trigger uses a table, salesnull, that is not part of the
 sample database. To test this trigger, you must create the salesnull table,
 which is identical in structure to sales except that its title_id column
 permits null values.
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

   create trigger forinsertrig1
    on salesnull
    for insert
    as if exists (select inserted.title_id from inserted
    where title_id is null)
    print "null key inserted"
    if not exists
    (select * from titles, inserted
    where titles.title_id = inserted.title_id)
    begin
     rollback transaction
     print "No! The title_id doesn't exist in titles."
    end
      /* cancel the insert and print a message.*/
    else
     print "Added, because the title_id did exist in titles."
      /* Otherwise, allow it. */


 Updating a Foreign Key

 A change (update) to a foreign key by itself is probably an error. A foreign
 key is just a copy of the primary key: the two should never be independent.


 If for some reason you want to allow updates of a foreign key, you might
 want to protect its integrity by


   ž   Requiring that updates to the foreign key be done through a stored
       procedure, rather than directly with an UPDATE statement.

   ž   Creating a trigger that checks updates against the master table and
       rolls them back if they don't match the primary key. (It is also
       possible to create a trigger that rolls back the update under any
       circumstances.)


 An example of canceling an update can be found in "Updating a Primary Key,"
 earlier in this chapter. In the following example, the trigger tests for two
 possible sources of failure: either the title_id is not in the sales table
 or it's not in the titles table.

 The example uses three nested IF statements. The first (IF UPDATE) checks to
 see if the title_id column in sales is being updated. If you update a column
 other than sales.title_id, the trigger has no effect.

 The second checks to see that the inserted table contains a row of
 information. If inserted is blank, the title_id does not exist in the sales
 table, and an error message is printed.

 The third checks to see if the inserted and titles tables join on the
 title_id column. If not, the transaction is rolled back, and an error
 message is printed. If the join succeeds, a different message is printed.

   create trigger forupdatetrig
    on sales
    for update as
    if update (title_id)
    begin
     if (select count(*) from inserted) > 0
     begin
      if not exists
       (select titles.title_id from inserted, titles
       where titles.title_id = inserted.title_id)
      begin
       rollback transaction
       print "No! the title_id doesn't exist in titles"
      end
      else
       print "sales table updated"
     end

   else
      print "title_id not in sales"
    end


 Summary Value Triggers

 Another important function of a trigger is to automatically recalculate
 ongoing tallies. Here's one that updates the ytd_sales column in the titles
 table whenever a row is added to the sales table.

   create trigger sumintrig
    on sales
    for insert
      /* When you add a row to sales, */
    as
    update titles
    set titles.ytd_sales = (titles.ytd_sales + inserted.qty)
    from titles, inserted
    where inserted.title_id = titles.title_id
     /* add new sales.qty to titles.ytd_sales
     ** where the title_ids join. */

 This trigger goes into effect whenever you record a sale by adding a row to
 the sales table. The trigger updates the ytd_sales column in titles so that
 it is equal to its previous value plus the value added to sales.qty. This
 keeps the totals up to date for inserts into sales.qty. Prevent updates on
 titles.ytd_sales and on sales.qty by revoking UPDATE permissions.

 You can create a trigger that checks the new value in ytd_sales against the
 roysched table to find whether the current sales total puts a new royalty
 amount into effect. If so, the trigger updates the royalty column in titles.


   create trigger sumuptrig2
    on sales
    for insert
    as
    update titles
    set titles.ytd_sales = (titles.ytd_sales + inserted.qty),
     titles.royalty = roysched.royalty
    from titles, inserted, sales, roysched
    where inserted.title_id = titles.title_id
     and titles.title_id = roysched.title_id
     and (titles.ytd_sales + inserted.qty) >= roysched.lorange
     and (titles.ytd_sales + inserted.qty) <= roysched.hirange


 Other Triggers

 If a table has a unique key, you can prevent updates outside of a certain
 range by comparing the (previous) value of a field in deleted to its
 (current) value in inserted to find the difference. The following example
 prevents all price increases greater than 100%:

   create trigger sumuptrig3
    on titles
    for update
    as
    if update (price)
    if
    (select count(*)
    from inserted, deleted
    where inserted.title_id = deleted.title_id
     and inserted.price > (deleted.price*2) ) > 0
    begin
     print "This increase is greater than 100%, hence illegal!"
     rollback transaction
    end

 In this trigger, you join the rows in inserted and deleted when there is an
 update to the price column in titles. Then you check to see if any of the
 prices in inserted (the new values) are more than twice as large as the
 prices in deleted (the old values). If they are, the update is rolled back.



 Correlated Subqueries in Triggers

 The triggers examined so far have looked at each data modification statement
 as a whole: if one row of a four-row insert was unacceptable, the whole
 insert was unacceptable and the transaction was rolled back.

 This is the way most triggers work. A trigger must reject or accept each
 data modification transaction as a whole.

 However, using a correlated subquery in a trigger can force the trigger to
 examine the modified rows one by one. (See Chapter 6, "Building Subqueries,"
 for more on correlated subqueries.) While the rollback must affect either
 the whole transaction or none of it, the trigger can take different actions
 on different rows.

 The trigger examples in this section assume the existence of a table called
 newsales. Here is its CREATE statement:

   create table newsales
    (stor_id char(4),
    ord_num varchar(20),
    date datetime,
    qty smallint,
    payterms varchar(12),
    title_id tid)

 You should insert four rows in the newsales table, to test the triggers in
 this section. Two of the newsales rows have title_ids that do not match any
 of those already in the titles table. Here is the data:

ÖŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄ
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         newsales

         stor_id        ord_num        date                 qty     payterms

         -------        --------       -------------------  -----   --------

         7066           QA7442.3       Sep 13 1985 12:00AM  75      Net 30

         7066           QA7442.3       Sep 13 1985 12:00AM  75      Net 60

         7067           D4482          Sep 14 1985 12:00AM  10      Net 30
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
        7067           D4482          Sep 14 1985 12:00AM  10      Net 30

         7131           N914008        Sep 14 1985 12:00AM  20      Net 30



 When you insert data from newsales into sales, the statement looks like
 this:

   insert sales
    select * from newsales

 All the triggers in this section are insert triggers on the sales table. The
 first trigger in this section is not a correlated one. It checks to see if a
 count of the rows in inserted is greater than the count of rows that join
 between inserted and titles on the title_id columns. If so, the trigger
 rolls back the entire transactionÄno rows are inserted.

   create trigger anyinsert
    on sales
    for insert
    as
    if (select inserted.title_id from inserted) not in
     (select title_id from titles)
    begin
     rollback tran
     print "Can't add these records to salesÄ"
     print "One or more title_ids not found in titles."
    end

 What if you want to examine each of the records you are trying to insert?
 This calls for a trigger with a correlated subquery. Drop trigger anyinsert
 and create another trigger, which is different in two ways:


   ž   It does not name the outer query in the inner query, and hence is
       correlated.

   ž   It does not roll back the transaction.


 Instead, it adds all the inserted rows but examines each one and gives you a
 message if there are some nonmatching title_ids.

   create trigger conditionalinsert
    on sales
    for insert
    as
    if exists (select * from inserted
    where not exists
    (select *
    from titles
    where titles.title_id = inserted.title_id))
    print "All sales records added, but some title_ids not found
    in the titles table."

 Since trigger conditionalinsert analyzes the insert row by row, it's
 possible to modify it so that it conditionally accepts the rows with
 matching title_ids. Here's how:

   create trigger only_matching
    on sales
    for insert as
    if exists
    (select * from inserted
    where not exists
    (select * from titles
    where titles.title_id = inserted.title_id))
    begin
     delete sales
     where sales.title_id not in
     (select title_id
     from titles)
     print "Only sales records with matching title_ids added."
    end






 Chapter 14  Advanced Topics for Database Owners
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


 Introduction

 This chapter describes some of the statements used for system
 administration. A few SQL statements that can be used only by the System
 Administrator are not mentioned in this guide. They are discussed in the SQL
 Server System Administrator's Guide and the SQL Server Language Reference.

 The topics covered in this chapter are


   ž   Assigning permissions (the GRANT and REVOKE statements)

   ž   Setting query options (the SET statement)

   ž   Keeping SQL Server's information about indexes up to date (the UPDATE
       STATISTICS statement)

   ž   Locking and making system-generated locks more restrictive (the
       HOLDLOCK keyword, used in SELECT statements)

   ž   User-defined transactions (the BEGIN TRANSACTION, COMMIT TRANSACTION,
       SAVE TRANSACTION, and ROLLBACK TRANSACTION statements)

   ž   Backup and recovery (the DUMP DATABASE, LOAD DATABASE, DUMP
       TRANSACTION, LOAD TRANSACTION, and CHECKPOINT statements)

   ž   Checking the physical and logical consistency of the database (the
       DBCC statement)


 The discussion of permissions in this chapter provides database object
 owners with all the information they need to grant and revoke permissions on
 their objects to other users. The other topics in this list are covered
 briefly in this chapter. (For more information, see the SQL Server System
 Administrator's Guide.)


 Assigning Permissions: GRANT and REVOKE

 The GRANT and REVOKE statements help to enforce database security. They
 specify which users can perform which operations on which tables, views, or
 columns. In discussing the GRANT and REVOKE statements, we often speak of
 "granting permissions" or "assigning permissions."

 Permission for many SQL statements can be granted or revoked only by the
 System Administrator or Database Owner, as shown in the table that appears
 later in this chapter. This section focuses on the aspects of the
 permissions system that are of interest to other users, including the owners
 of database objects. (For more information, see the SQL Server System
 Administrator's Guide.)


 Object Permissions and Statement Permissions

 There are two kinds of permissions assigned with GRANT and REVOKE: object
 permissions and statement permissions.

 Object permissions regulate the use of certain statements on certain
 database objects. They are granted and revoked by the owner of the object.

 Object permissions apply to the following statements and objects:

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 Statement                         Object
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 SELECT                            table, view, columns

 UPDATE                            table, view, columns

 INSERT                            table, view

 DELETE                            table, view

 EXECUTE                           stored procedure

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Permissions for other statements are called statement permissions because
 they are not object specific. They can be granted only by the System
 Administrator or a Database Owner.

 The statements to which the statement permissions apply are


   ž   CREATE DATABASE (can be granted only by the System Administrator and
       only to users in the master database)

   ž   CREATE DEFAULT

   ž   CREATE PROCEDURE

   ž   CREATE RULE

   ž   CREATE TABLE

   ž   CREATE VIEW

   ž   DUMP DATABASE

   ž   DUMP TRANSACTION


 Each database has its own independent protection system. In other words,
 having permission to use a certain statement in one database has no effect
 in other databases.

 If you try to use a statement or database object for which you have not been
 assigned permission, SQL Server responds with an error message.


 The Permission Hierarchy

 Permissions are assigned only by users with particular roles. SQL Server's
 protection system recognizes these types of users:


   ž   The System Administrator

   ž   Owners of databases

   ž   Owners of database objects

   ž   Other users (also known as public)


 The different types of users exist in a kind of hierarchy, with the System
 Administrator at the top. At each level of the hierarchy, different
 permissions automatically accrue and different ones can be granted.


 System Administrator

 The System Administrator (login ID sa) is recognized by SQL Server as a
 superuser who has a broad range of permissions and who can assume the
 identity (and thus the permissions) of any other user. The System
 Administrator is responsible for adding and dropping users' login IDs from
 SQL Server, and for granting initial permissions.

 The System Administrator is a function or role rather than a person. That
 is, no user's login ID is permanently associated with the role of System
 Administrator. As far as SQL Server is concerned, if you know the password
 for the System Administrator, you can be the System Administrator. When you
 log in as System Administrator, you are working outside SQL Server's
 protection system.


 Database Owners

 Database Owners are next in the hierarchy. The System Administrator and
 Database Owners are the only users who can grant statement permissions to
 other users. A Database Owner is responsible for granting users access to
 his or her database with the sp_adduser and sp_dropuser system procedures,
 and for setting up user groups with sp_addgroup, sp_changegroup, and
 sp_dropgroup. (See the SQL Server Language Reference for details.)


 Database Object Owners

 At the next level are the owners of database objects (tables, views, and
 stored procedures), who control permission on those objects. For example, a
 user who creates (and therefore owns) a table automatically has all of the
 permissions that apply to that tableÄSELECT, INSERT, UPDATE, and DELETE. No
 other users have any permissions on the table until the owner specifically
 grants them with the GRANT statement.


 Other Users

 At the bottom of the hierarchy is the "public"Äother database users.
 Permissions are granted to or revoked from them by object owners, Database
 Owners, or the System Administrator. As a later section on GRANT and REVOKE
 syntax explains, these users are specified by username, by groupname, or by
 the keyword PUBLIC.


 Permissions of Database Object Owners

 A user who creates a database object (a table, view, or stored procedure) is
 its owner and is automatically granted all object permissions on it. Users
 other than the object owner, including the owner of the database, are
 automatically denied all permissions on that object unless they are
 explicitly granted by the owner.

 As an example, say that Mary is the owner of the pubs database and has
 granted Joe permission to create tables in it. Now Joe creates the table
 authors; he is the owner of this database object. Initially, object
 permissions on authors belong to Joe and Joe alone. Joe can grant object
 permissions for this table to other users, including Mary, the Database
 Owner. (However, as the Database Owner, Mary can access the table even if
 Joe does not execute a GRANT statement by using the SETUSER statement.
 SETUSER allows a Database Owner to impersonate any user in the same
 database.)

 The following statements default to the owner of a table and cannot be
 transferred to other users:


   ž   ALTER TABLE

   ž   CREATE INDEX

   ž   CREATE TRIGGER

   ž   DROP TABLE

   ž   TRUNCATE TABLE

   ž   UPDATE STATISTICS


 And of course, permission to use the GRANT and REVOKE statements cannot be
 transferred.

 The owner of any objectÄa table, view, index, stored procedure, rule, or
 defaultÄcannot transfer permission to use the DROP statement for that
 object.


 Permission Summary

 Table 14.1 summarizes the protection system. The type of user listed as the
 one to whom the statement defaults is the lowest level of user to whom the
 permission automatically accrues. This user can grant or revoke the
 permission to other users, if it is transferable. Users at higher levels are
 either automatically assigned the permission or (in the case of Database
 Owners) can get it with the SETUSER statement. (The System Administrator
 retains full permissions even if he or she executes the SETUSER statement.)


 For example, the owner of a database does not automatically receive
 permissions on objects owned by other users. But a Database Owner can always
 give himself or herself any permission by assuming the identity of the
 object owner with the SETUSER statement and then writing the appropriate
 GRANT or REVOKE statements.

 For permissions that default to public, no permission is requiredÄthat is,
 no GRANT or REVOKE statements need ever be written.

 Table 14.1  Summary of Protection System

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄ
                                                             Can Be
              Defaults to                                    Granted /
                                                             Revoked
              System       Db Owner     Table Owner  Public  Yes          No
              Admin
 Statement:
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                                             Can Be
              Defaults to                                    Granted /
                                                             Revoked
              System       Db Owner     Table Owner  Public  Yes          No
              Admin
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 ALTER        X            Ä            Ä            Ä       (1)          Ä
 DATABASE

 ALTER TABLE  Ä            Ä            X            Ä       Ä            X

 BEGIN        Ä            Ä            Ä            X       Ä            Ä
 TRANSACTION

 CHECKPOINT   Ä            X            Ä            Ä       Ä            X

 COMMIT       Ä            Ä            Ä            X       Ä            Ä
 TRANSACTION

 CREATE       X            Ä            Ä            Ä       X            Ä
 DATABASE
                                                             Can Be
              Defaults to                                    Granted /
                                                             Revoked
              System       Db Owner     Table Owner  Public  Yes          No
              Admin
DATABASE

 CREATE       Ä            X            Ä            Ä       X            Ä
 DEFAULT

 CREATE       Ä            Ä            X            Ä       Ä            X
 INDEX

 CREATE       Ä            X            Ä            Ä       X            Ä
 PROCEDURE

 CREATE RULE  Ä            X            Ä            Ä       X            Ä

 CREATE       Ä            X            Ä            (2)     X(2)         Ä
 TABLE

                                                             Can Be
              Defaults to                                    Granted /
                                                             Revoked
              System       Db Owner     Table Owner  Public  Yes          No
              Admin

 CREATE TRIG  Ä            Ä            X            Ä       Ä            X
 GER

 CREATE VIEW  Ä            X            Ä            Ä       X            Ä

 DBCC         Ä            X            Ä            Ä       Ä            Ä

 DELETE       Ä            Ä            X(3)         Ä       X            Ä

 DISK INIT    X            Ä            Ä            Ä       Ä            X

 DISK REFIT   X            Ä            Ä            Ä       Ä            X

 DISK REINIT  X            Ä            Ä            Ä       Ä            X

                                                             Can Be
              Defaults to                                    Granted /
                                                             Revoked
              System       Db Owner     Table Owner  Public  Yes          No
              Admin

 DROP (any    Ä            Ä            Ä            Ä       Ä            X
 object)(4)

 DUMP         Ä            X            Ä            Ä       X            Ä
 DATABASE

 DUMP         Ä            X            Ä            Ä       X            Ä
 TRANSACTION

 EXECUTE (5)  Ä            Ä            Ä            Ä       X            Ä

 GRANT        Ä            X            Ä            Ä       Ä            X

 GRANT on     Ä            Ä            Ä            Ä       Ä            X
 object (4)
                                                             Can Be
              Defaults to                                    Granted /
                                                             Revoked
              System       Db Owner     Table Owner  Public  Yes          No
              Admin
object (4)

 INSERT       Ä            Ä            X(3)         Ä       X            Ä

 KILL         X            Ä            Ä            Ä       Ä            X

 LOAD         Ä            X            Ä            Ä       Ä            X
 DATABASE

 LOAD         Ä            X            Ä            Ä       Ä            X
 TRANSACTION

 PRINT        Ä            Ä            Ä            X       Ä            Ä

 RAISERROR    Ä            Ä            Ä            X       Ä            Ä

                                                             Can Be
              Defaults to                                    Granted /
                                                             Revoked
              System       Db Owner     Table Owner  Public  Yes          No
              Admin

 READTEXT     Ä            Ä            X(3)         Ä       X            Ä

 RECONFIGURE  X            Ä            Ä            Ä       Ä            X

 REVOKE       Ä            X            Ä            Ä       Ä            X

 REVOKE on    Ä            Ä            Ä            Ä       Ä            X
 object (4)

 ROLLBACK     Ä            Ä            Ä            X       Ä            Ä
 TRANSACTION

 SAVE         Ä            Ä            Ä            X       Ä            Ä
 TRANSACTION

                                                             Can Be
              Defaults to                                    Granted /
                                                             Revoked
              System       Db Owner     Table Owner  Public  Yes          No
              Admin

 SELECT       Ä            Ä            X(3)         Ä       X            Ä

 SET          Ä            Ä            Ä            X       Ä            Ä

 SETUSER      Ä            X            Ä            Ä       Ä            X

 TRUNCATE     Ä            Ä            X            Ä       Ä            X
 TABLE

 UPDATE       Ä            Ä            X(3)         Ä       X            Ä

 UPDATE       Ä            Ä            X            Ä       Ä            X
 STATISTICS

 WRITETEXT    Ä            Ä            X(3)         Ä       X            Ä
                                                             Can Be
              Defaults to                                    Granted /
                                                             Revoked
              System       Db Owner     Table Owner  Public  Yes          No
              Admin
WRITETEXT    Ä            Ä            X(3)         Ä       X            Ä

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 (1) Transferred with CREATE DATABASE permission
 (2) Public can create temporary tables, no permission required
 (3) If a view, permission defaults to view owner
 (4) Defaults to object owner
 (5) Defaults to stored procedure owner



 GRANT and REVOKE Syntax

 The syntax statements for object and statement permissions are slightly
 different. Here are a few statements that grant object permissions. This one
 grants permission to mary and the sales group to insert and delete the
 titles table:

   grant insert, delete
    on titles
    to mary, sales

 This statement grants permission for harold to use the stored procedure
 makelist.

   grant execute
    on makelist
    to harold

 This statement revokes permission for all users to update and select from
 the price and ytd_sales columns of the titles table:

   revoke update, select
    on titles (price, ytd_sales)
    from public

 These examples show how to grant and revoke statement permissions:

   grant create table, create view
    to mary, jane, bob

    grant dump database
    to public

    revoke create table, create rule
    from mary

 The full syntax for granting and revoking object permissions (permission to
 use tables, views, columns, and stored procedures) is as follows:

   GRANT {ALL | permission_list}
    ON {table_name [(column_list)] |
    view_name [(column_list)] |
    stored_procedure_name}
    TO {PUBLIC | name_list}
    REVOKE {ALL | permission_list}
    ON {table_name [(column_list)] |
    view_name [(column_list)] |
    stored_procedure_name}
    FROM {PUBLIC | name_list}

 The full syntax for granting and revoking statement permissions follows:

   GRANT {ALL | statement_list}
    TO {PUBLIC | name_list}
    REVOKE {ALL | statement_list}
    FROM {PUBLIC | name_list}

 Only the System Administrator can use the ALL keyword when granting
 statement permissions because only the System Administrator can grant CREATE
 DATABASE permission.

 Whether you are granting object permissions or statement permissions, you
 can include more than one statement in the statement list. Separate the
 statements with commas.

 When the GRANT or REVOKE statements are being used to assign permissions on
 a table or a view, the statement list can consist of any combination of
 these statements: SELECT, INSERT, DELETE, and UPDATE.

 When permissions are being granted on columns, the statement list can
 include SELECT, UPDATE, or both of them. To use a SELECT * statement, you
 must have SELECT permission on all the columns in a table.

 When permissions are being granted on stored procedures, the statement list
 can include EXECUTE only.

 If more than one statement is included in the statement list, separate them
 with commas. If the keyword ALL is used in the statements for object
 permissions, every one of the statements applicable to the object is granted
 or revoked.

 The ON clause specifies the object for which the permission is being granted
 or revoked. Permissions on tables, views, and stored procedures can be
 granted or revoked for only one object at time. Permissions can be granted
 for more than one column at a time, but all the columns must be in the same
 table or view.

 The keyword PUBLIC refers to all the users of the system. If you grant or
 revoke permissions to public, you are included as well.

 The name list is a list of the names of user groups, the names of individual
 users, or any combination of them. Each name is separated from the following
 one by a comma.


 Combining GRANT and REVOKE Statements

 GRANT and REVOKE statements are order-sensitive: in case of a conflict, the
 most recently executed statement supersedes all others.

 There are two basic styles of setting up permissions in a database or on a
 database object. The most straightforward is to assign specific permissions
 to specific users.

 However, if most users are going to be granted most permissions, it's easier
 to assign all permissions to all users and then revoke specific permissions
 from specific users.

 For example, a Database Owner can grant all permissions on the titles table
 to all users by issuing the following statement:

   grant all
    on titles
    to public

 Then the Database Owner can execute a series of REVOKE statements, for
 example:

   revoke update
    on titles (royalty, advance)
    from public

    revoke delete
    on titles
    from mary, sales, john


 Conflicting GRANT and REVOKE Statements

 As mentioned in the previous section, GRANT and REVOKE statements are
 sensitive to the order in which they are executed. For example, if Joe's
 group has been granted SELECT permission on the titles table and then Joe's
 permission to select the advance column has been revoked, Joe can select all
 the columns except advance, while the other users in his group can still
 select all the columns.

 A GRANT or REVOKE statement that applies to a group changes any conflicting
 permissions that have been assigned to any member of that group. For
 example, if the owner of the titles table has granted different permissions
 to various members of the sales group and wants to standardize, he or she
 might execute the following statements:

   revoke all on titles from sales
    grant select on titles(title, title_id, type, pub_id) to sales

 Similarly, a GRANT or REVOKE statement executed to public will change for
 all users all previously executed permissions that conflict with the new
 statement.

 The same GRANT and REVOKE statements executed in different orders can create
 entirely different situations. For example, this set of statements leaves
 Joe (who belongs to the public group) without any select permission on
 titles:

   grant select on titles(title_id, title) to joe
    revoke select on titles from public

 In contrast, the same statements executed in the opposite order result in
 only Joe having SELECT permission and only on the title_id and title
 columns:

   revoke select on titles from public
    grant select on titles(title_id, title) to joe

 Remember that when you use the keyword PUBLIC, you are including yourself.
 It is possible, and sometimes desirable, to deny yourself permission to use
 your own table, while giving yourself permission to access a view built on
 it or a stored procedure that references it. (You can always change your
 mind and reinstitute the permission with a GRANT statement.) You will
 probably use PUBLIC more frequently as a quick way of revoking permissions
 and then defining some exceptions, as in the earlier example.


 Views as Security Mechanisms

 Permission to access the subset of data in a view must be granted or
 revoked, regardless of the set of permissions in force on the view's
 underlying table(s). Data in an underlying table that is not included in the
 view is hidden from users who are authorized to access the view but not the
 underlying table.

 For example, you might not want some users to be able to access the columns
 in the titles table that have to do with money and sales. You could create a
 view of the titles table that omits those columns and then give all users
 permission on the view, but give only the Sales Department permission on the
 table. Here's how:

   revoke all on titles to public
    grant all on bookview to public
    grant all on titles to sales

 An equivalent way of setting up these permissions without using a view is
 this series of statements:

   A. grant all on titles to public

   B. revoke select, update
    on titles (price, advance, royalty, ytd_sales)
    from public

   C. grant select on titles (price, advance, royalty, ytd_sales)
    to sales

 These statements still allow public to insert and delete rows from the
 titles table; you would need this additional statement to deny them these
 permissions:

   revoke insert, delete on titles
    to public

 One possible problem with the second method is that users not in the sales
 group who enter the following statement might be surprised to see a
 "permission denied" message:

   select * from titles

 SQL Server expands the asterisk into a list of all the columns in the titles
 table, and since permission on some of these columns has been revoked from
 nonsales users, refuses access to them. The error message lists the columns
 for which the user does not have access.

 To see all the columns for which they do have permission, the non-sales
 users would have to name them explicitly.

 (For more information on views, see Chapter 10, "Creating Views.")


 Stored Procedures as Security Mechanisms

 A user who has been granted permission to execute a stored procedure can do
 so even if he or she does not have permissions on tables or views referenced
 in it. For example, a user might be given permission to execute a stored
 procedure that updates a row-and-column

 subset of a specified table, even though that user does not have any other
 permissions on that table.

 (For information on stored procedures, see Chapter 12, "Using Stored and
 System Procedures.")


 Tuning Queries and Stored Procedures: SET

 The query processing options allow you to instruct SQL Server to handle
 queries and stored procedures in a variety of unusual ways. They are turned
 on or off for the duration of the user's work session with the SQL SET
 statement. No permissions are required for using the SET statement.

 The SET statement has the following syntax:

   SET
    {{ARITHABORT | ARITHIGNORE | BACKGROUND | NOCOUNT | NOEXEC|
    OFFSETS {keyword_list} | PARSE ONLY | PROCID | SHOWPLAN |
    STATISTICS IO | STATISTICS TIME |
    {ON | OFF} |
    ROWCOUNT number | TEXTSIZE number}

 (See the SQL Server Language Reference for details.)

 This statement causes SQL Server to return a description of the processing
 plan for each query but not execute it:

   set showplan on
    set noexec on

 This statement causes SQL Server to stop processing each query after it
 returns the first ten rows:

   set rowcount 10

 Although permission to run the SET statement defaults to public, most users
 have occasion to use only some of the SET options. Explanations of the
 options are as follows:

 ARITHABORT
   Aborts a query when an overflow or divide-by-zero error occurs during
   query execution.

 ARITHIGNORE
   Returns NULL when an overflow or divide-by-zero error occurs during a
   query. No warning message is displayed. If neither ARITHABORT nor
   ARITHIGNORE is set, SQL Server returns NULL and prints a warning message
   after the query is executed.

 BACKGROUND
   Puts processes generated by subsequent statements in background and breaks
   the connection to the workstation. Once BACKGROUND is on, you can't do
   anything else. Your session is over; you must interrupt it and log in
   again.

   If you execute a stored procedure that sets the background option, your
   session becomes disconnected from your workstation. The connection is not
   re-established even when the procedure finishes executing.

 NOCOUNT
   Turns off the message returned at the end of each statement that tells you
   how many rows were affected by the statement. The global variable
   @ROWCOUNT is updated even when NOCOUNT is on.

 NOEXEC
   Compiles each query but does not execute it. NOEXEC is often used with
   SHOWPLAN. Once NOEXEC is turned on, no subsequent statements will be
   executed (including other SET statements) until NOEXEC is turned off.

 OFFSETS
   DB-LIBRARY only. Returns the offset (position in relation to the beginning
   of the query) of specified keywords in SQL statements. The keyword list is
   a list, separated with commas, that can include any of the following SQL
   constructs: SELECT, FROM, ORDER, COMPUTE, TABLE, PROCEDURE, STATEMENT,
   PARAM, and EXECUTE.

   The OFFSETS option is used only in an application program using
   DB-LIBRARY.

 PARSEONLY
   Checks the syntax of each query and returns any error messages without
   generating a sequence tree, compiling, or executing the query. Do not use
   PARSEONLY in a stored procedure. PARSEONLY returns offsets if the OFFSETS
   option is on and there are no errors (however, OFFSETS is used only in an
   application program using DB-LIBRARY).

 PROCID
   Returns the ID number of the stored procedure to DB-LIBRARY (not to the
   user) before sending rows generated by that stored procedure.

 SHOWPLAN
   Generates a description of the processing plan for the query and
   immediately processes it unless NOEXEC is set. Do not use SHOWPLAN in a
   stored procedure.

 STATISTICS IO
   Displays the number of scans, the number of logical reads (pages
   accessed), and the number of physical reads (disk accesses) for each table
   referenced in the statement; STATISTICS IO displays the number of pages
   written for each statement.

 STATISTICS TIME
   Displays the time it took to parse and compile each command and the time
   it took to execute each step of the command. Times are given in timeticks,
   the exact value of which is machine-dependent.

 ROWCOUNT number
   Causes SQL Server to stop processing the query after the specified number
   of rows are returned. To turn this option off, so that all rows are
   returned, use "SET ROWCOUNT 0".

 TEXTSIZE n
   Specifies the size in bytes of text type data to be returned with a SELECT
   statement. If you specify a textsize of 0, 32K bytes of data are returned
   with a SELECT statement. The @TEXTSIZE global variable stores the current
   setting.

 If you use the SET statement inside a trigger or stored procedure, the
 option reverts to its former setting after the trigger or procedure
 executes.

 Any options set with a SET statement take effect at the end of the batch.
 You can combine SET statements and queries in the same batch, but the SET
 options won't apply to the queries in that batch. Since this batch contains
 a SET statement and a query, the SET option doesn't take effect:

   set showplan on
    select * from publishers
    go

    pub_id  pub_name    city   state
    ------  -------------------- ---------  -----
    0736  New Age Books   Boston   MA
    0877  Binnet & Hardley  Washington  DC
    1389  Algodata Infosystems Berkeley  CA

    (3 rows affected)

 If the two statements are put in separate batches, here's what happens:

   set showplan on
    go
    select * from publishers
    go

    STEP 1
    The type of query is SELECT
    FROM TABLE
    publishers

   Nested iteration
    Table Scan
    pub_id  pub_name    city   state
    ------  -------------------- ----------  -----
    0736  New Age Books   Boston   MA
    0877  Binnet & Hardley  Washington  DC
    1389  Algodata Infosystems Berkeley  CA

    (3 rows affected)


 UPDATE STATISTICS

 The UPDATE STATISTICS statement helps SQL Server make the best decisions
 about which indexes to use when it processes a query by keeping it up to
 date about the distribution of the key values in the indexes. It should be
 used when you create an index on already existing data or when a large
 amount of data in an indexed column has been added, changed, or deleted.

 Permission to execute the UPDATE STATISTICS statement defaults to the table
 owner and is not transferable. The UPDATE STATISTICS statement has the
 following syntax:

   UPDATE STATISTICS table_name [[database.]owner.] [index_name]

 If you do not specify an index name, the statement updates the distribution
 statistics for all the indexes in the specified table. Giving an index name
 updates statistics for that index only.

 You can find the name of the indexes by using the sp_helpindex system
 procedure. This procedure takes a table name as a parameter.

 Here's how you'd list the indexes for the authors table:

   sp_helpindex authors
    index_name  index_description   index_keys
    ----------  ------------------------ ----------
    auidind   clustered, unique   au_id
    aunmind   nonclustered au_lname,  au_fname

    (2 rows affected)

 To update the statistics for all of the indexes, type

   update statistics authors

 To update the statistics only for the index on the au_id column, type

   update statistics authors auidind

 Since TRANSACT-SQL does not require index names to be unique in a database,
 you must give the name of the table with which the index is associated.


 Locking and the HOLDLOCK Keyword

 To prevent other users from interfering with data being used for an active
 transaction, SQL Server protects the relevant tables or data pages of the
 database by locking them. Locking is a concurrency control mechanism. It is
 necessary in a multiuser environment since several users may be concurrently
 working with the same data.

 SQL Server handles locking decisions on its own. SQL Server always applies
 exclusive locks for update operations (UPDATE, INSERT, or DELETE). When an
 exclusive lock is set, no other transaction can acquire a lock of any kind
 until the original lock is released at the end of the transaction. The data
 modification statements are sometimes called "write" operations because they
 add, remove, or modify data. Even a single-statement update transaction can
 affect many rows.

 For nonupdate or "read" operations, such as SELECT, SQL Server applies
 shared locks. If a shared lock has been applied to a table or data page, a
 second transaction can also acquire a shared lock even though the first
 transaction hasn't completed.

 However, no transaction can acquire an exclusive lock until all shared locks
 on it have been released. Conversely, if a transaction has an exclusive
 lock, another transaction can't acquire a shared lock on it. Exclusive locks
 and shared locks can be applied either at the page level or the table level.



 The HOLDLOCK Keyword

 The HOLDLOCK keyword, used after a table name or view name in the FROM
 clause of a SELECT statement, makes a shared lock more restrictive. It
 applies only to shared locks, only to the table or view for which it is
 specified, and only for the duration of the transaction defined by the
 statement in which it is used.

 HOLDLOCK instructs SQL Server to hold the shared lock that it has set until
 the completion of the transaction, instead of releasing it as soon as the
 required table, view, or data page is no longer needed, whether or not the
 transaction has been completed.

 SQL Server's normal handling of shared locksÄreleasing them as soon as the
 table, view, or data page is no longer neededÄfacilitates continued
 interactive use of the database even while a lengthy transaction is
 proceeding. However, it also means that if the same entry is read twice
 within the same transaction, it won't necessarily be the same both times.


 Deadlocks and Livelocks

 A deadlock occurs when two users each have a lock on a separate object. Each
 wants to acquire a lock on the other user's object. When this happens, the
 first user is waiting for the second to let go of his or her lock, but the
 second user won't let it go until the lock on the first user's object is
 freed.

 SQL Server detects this situation and chooses one of the users. The user's
 process is killed, and everything starts moving.

 It is possible to encounter a deadlock when several long-running
 transactions are being executed at the same time in the same database.

 In a livelock, a request for an exclusive lock is repeatedly denied because
 a series of overlapping shared locks keeps interfering.

 SQL Server detects this situation, too. After four denials to the user
 requesting the exclusive lock, SQL Server refuses further requests from
 shared locks. The request for the exclusive lock is then granted.


 Data Integrity and Transaction Processing

 A transaction is a mechanism for ensuring that a set of one or more SQL
 statements is treated as a single unit of work. SQL Server automatically
 manages all data modification statements, including single-step change
 requests, as transactions.

 In addition, users can group a set of SQL statements into a user-defined
 transaction with the BEGIN TRANSACTION and COMMIT TRANSACTION statements.

 Transactions allow SQL Server to guarantee


   ž   Consistency: simultaneous queries and change requests cannot collide
       with each other, and users never see or operate on data that is
       partway through a change.

   ž   Recovery: in case of system failure, the database is recovered
       completely and automatically.



 Transactions and Consistency

 In a multiuser environment, SQL Server must prevent simultaneous queries and
 data modification requests from interfering with each other. This is
 important because if the data being processed by a query could be changed by
 another user's update while the query was running, the results of the query
 would be ambiguous.

 SQL Server automatically sets the appropriate level of locking for each
 transaction. As discussed earlier in this chapter, users can make shared
 locks more restrictive on a query-by-query basis by including the HOLDLOCK
 keyword in a SELECT statement.

 User-defined transactions allow users to instruct SQL Server to process any
 number of SQL statements as a single unit. They are discussed in a later
 section.


 Transactions and Recovery

 A transaction is both a unit of work and a unit of recovery. The fact that
 SQL Server handles single-step change requests as transactions means that
 the database can be recovered completely in case of failures.

 SQL Server's recovery time is measured in seconds and minutes, rather than
 hours or days. Users can specify the maximum acceptable recovery time.

 The SQL statements related to recovery and backup are discussed in a later
 section.


 User-Defined Transactions

 With the BEGIN TRANSACTION and COMMIT TRANSACTION statements, users can
 instruct SQL Server to process any number of simple transactions as a single
 unit. ROLLBACK TRANSACTION allows the user to undo the transaction back to
 its beginning or back to a savepoint inside the transaction that has been
 defined with the SAVE TRANSACTION statement.

 In addition to giving the user control over transaction management,
 user-defined transactions also improve performance since system overhead is
 incurred only once for a set of transactions, rather than once for each
 individual statement.

 The following subsections discuss general transaction rules and each
 transaction statement in turn. Then an example is given.

 Any user can define a transaction: no permission is required for any of the
 transaction statements.

 Statements that cannot be used inside a user-defined transaction are:


   ž   CREATE DATABASE, CREATE TABLE, CREATE INDEX, all DROP statements,
       SELECT INTO (because it creates a table), GRANT, REVOKE, ALTER
       DATABASE, ALTER TABLE, TRUNCATE TABLE, RECONFIGURE, LOAD DATABASE,
       LOAD TRANSACTION, and DISK INIT.

   ž   Some of the system procedures cannot be used inside user-defined
       transactions because they create temporary tables.



 BEGIN TRANSACTION and COMMIT TRANSACTION

 The BEGIN TRANSACTION and COMMIT TRANSACTION statements can enclose any
 number of SQL statements and/or stored procedures. These statements have the
 following syntax:

   BEGIN TRANsaction [transaction_name]

    COMMIT TRANsaction

 Here is an example:

   begin tran
     statement
     statement
     procedure
     statement
    commit tran

 The transaction name must conform to the rules for identifiers.


 ROLLBACK TRANSACTION and SAVE TRANSACTION

 If a transaction must be canceled before it is committedÄeither because of
 some failure or because of a change by the userÄall of its statements or
 procedures that have been completed must be undone.

 A transaction can be canceled or rolled back with the ROLLBACK TRANSACTION
 statement at any time before the COMMIT TRANSACTION statement has been
 given. You can cancel either an entire transaction or part of it. However,
 you can't cancel a transaction after it has been committed.

 The ROLLBACK TRANSACTION statement has the following syntax:

   ROLLBACK TRANsaction [transaction_name | savepoint_name]

 A savepoint is a marker that the user puts inside a transaction to indicate
 a point to which it can be rolled back (in addition to the beginning of the
 transaction).

 Within a transaction, duplicate savepoint_names are allowed, but only the
 first instance of a savepoint_name is used.

 Savepoints are inserted by putting a SAVE TRANSACTION statement within the
 transaction. The SAVE TRANSACTION statement has the following syntax:

   SAVE TRANsaction savepoint_name

 The savepoint name must conform to the rules for identifiers.

 If no savepoint name or transaction name is given with the ROLLBACK
 TRANSACTION statement, the transaction is rolled back to the previous BEGIN
 TRANSACTION point.

 Here's how the SAVE TRANSACTION and ROLLBACK TRANSACTION statements might be
 used:

   begin tran transaction_name
     statement
     statement
     procedure
     save tran savepoint_name
     statement
    rollback tran savepoint_name
     statement
     statement
    rollback tran

 The first ROLLBACK TRANSACTION statement rolls the transaction back to the
 savepoint inside the transaction. The second ROLLBACK TRANSACTION rolls the
 transaction back to its beginning. If a transaction is rolled back to a
 savepoint, it must still proceed to completion or be canceled altogether.

 Until you execute a COMMIT TRANSACTION, SQL Server considers all subsequent
 statements to be part of the transaction until it encounters another BEGIN
 TRANSACTION statement. At that point, SQL Server cancels the original
 transaction and displays an error message.


 Example

 This example shows how a user-defined transaction might be specified:

   begin transaction royaltychange
     /* A user sets out to change the royalty split for the two
     authors of The Gourmet Microwave. Since the database would
     be inconsistent between the two updates, they must be grouped
     into a user-defined transaction.*/
    update titleauthor
    set royaltyper = 65
    from titleauthor, titles
    where royaltyper = 75
     and titleauthor.title_id = titles.title_id
     and title = "The Gourmet Microwave"
    update titleauthor
    set royaltyper = 35
    from titleauthor, titles

   where royaltyper = 25
     and titleauthor.title_id = titles.title_id
     and title = "The Gourmet Microwave"
    save transaction percentchanged
     /* After having updated the royaltyper entries for the two authors,
     the user inserts the savepoint "percentchanged,"
     and then experiments to see how a 10% increase in the book's price
     would affect the authors' royalty earnings.*/
    update titles
    set price = price * $1.1
    where title = "The Gourmet Microwave"
    select (price * royalty * ytd_sales) * royaltyper
    from titles, titleauthor
    where title = "The Gourmet Microwave"
     and titles.title_id = titleauthor.title_id
    rollback transaction percentchanged
     /* The transaction is rolled back to the savepoint with the
     ROLLBACK TRANSACTION statement.
     If no savepoint name had been given the transaction would have
     been rolled back to the BEGIN TRANSACTION.*/
    commit transaction
     /* End of royaltychange.*/


 Backup and Recovery

 Every change to the database, whether it is the result of a single SQL
 update statement (a system-defined transaction) or a grouped set of SQL
 statements (a user-defined transaction), is automatically recorded in the
 system table syslogs. This table is called the transaction log.

 Some statements that change the database are not loggedÄfor example,
 TRUNCATE TABLE, bulk copy into a table that has no indexes, and DUMP
 TRANSACTION WITH NO_LOG.

 The transaction log records data modification requests (UPDATE, INSERT, or
 DELETE statements) on a moment-to-moment basis. When a transaction begins, a
 BEGIN TRANSACTION event is recorded in the log. As each data modification
 statement is received, it is recorded in the log.

 The change is always recorded in the log before any change is made in the
 database itself. This type of log, called a write-ahead log, ensures that
 the database can be recovered completely in case of a failure.

 Failures can be due to hardware or media problems, system software problems,
 application software problems, program-directed cancellations of
 transactions, or user decisions to cancel a transaction.

 In case of any of these failures, the transaction log can be "played back"
 (with the LOAD statements) against a copy of the database restored from a
 backup (made with the DUMP statements).

 To recover from a failure, transactions that were in progress but not yet
 committed at the time of the failure must be undone (since a partial
 transaction is not an accurate change). Completed transactions must be
 redone if there is no guarantee that they have been written to the disk.

 SQL Server's dynamic dump allows the database and transaction log to be
 backed up while use of the database continues. Making the backup process
 quick and easy encourages frequent backupsÄan important consideration, since
 their frequency determines the amount of work that could be lost if a system
 failure should occur.

 The owner of each database or the System Administrator is usually
 responsible for backing up the database and its transaction log with the
 DUMP statements, though permission to execute them can be transferred to
 other users. Permission to use the LOAD statements, however, defaults to the
 Database Owner and cannot be transferred.

 Once the appropriate LOAD statements are executed, SQL Server handles all
 aspects of the recovery process. SQL Server also automatically controls the
 checkpoint interval (the point at which all data pages that have been
 changed are guaranteed to have been written to the disk). Users can force a
 checkpoint if necessary with the CHECKPOINT statement.

 (For more information, see the SQL Server Language Reference or the SQL
 Server System Administrator's Guide.)


 Database Consistency Checker

 The Database Consistency Checker (DBCC) is a set of utility statements that
 checks the logical and physical consistency of a database. It is used in any
 of these three cases:


   ž   A system error with a severity level of 22 or 23 has been generated.

   ž   The System Administrator does a periodic check.

   ž   You suspect that a database is damaged. (For example, if using a
       particular table generates the message "corrupted data," you can use
       the DBCC to determine if other tables in the database are also
       damaged.)


 The Database Owner is automatically granted permission to use the DBCC
 statement and all of its options. A table owner is automatically granted
 permission to run DBCC CHECKTABLE. These permissions to run DBCC are not
 transferable.

 The DBCC has the following syntax:

   DBCC {CHECKTABLE (table_name) |
    CHECKDB [(database_name)] |
    CHECKALLOC [(database_name)] |
    CHECKCATALOG [(database_name]) |
    DBREPAIR (database_name, DROPDB) }

 DBCC can be run while the database is active, except for the DBREPAIR
 option.

 Only the CHECKTABLE option is discussed here. (For information on other DBCC
 options, see the SQL Server System Administrator's Guide and the SQL Server
 Language Reference.)

 The CHECKTABLE option checks the specified table to see that index and data
 pages are correctly linked, that indexes are in properly sorted order, that
 all pointers are consistent, that the data information on each page is
 reasonable, and that page offsets are reasonable.

 Here's an example:

   dbcc checktable (titles)

    Checking titles
    DBCC execution completed. If DBCC printed error messages,
    see your System Administrator.






 Appendix A  The pubs Sample Database
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 This appendix describes the sample database, pubs. The pubs database has
 eight tables:


   ž   publishers

   ž   titleauthor

   ž   sales

   ž   stores

   ž   discounts

   ž   roysched

   ž   authors

   ž   titles


 Each database table is described by two figures. The first figure documents
 the structure of the table; for each table field, the figure lists its
 datatype, its NULL/NOT NULL status, and any defaults, rules, triggers, and
 indexes. The second figure lists the table contents.

 Table   publishers Ä Structure

ÖŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄ
           pub_id                           pub_name     city         state
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Datatype  char(4)                          varchar(40)  varchar(20)  char(2)

           pub_id                           pub_name     city         state
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Null      not null                         null         null         null

 Rule      pub_idrule(1)                    Ä            Ä            Ä

 Index     clust, uniq                      Ä            Ä            Ä

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 (1) The pub_idrule states that the data must be 1389, 0736, 0877, 1622, or
 1756, or must match the pattern  99[0-9][0-9].


 pub_id                            pub_name    city           state
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 1389                              Algodata    Berkeley       CA
                                   Infosystem
                                   s

 0736                              New Age     Boston         MA
                                   Books

 0877                              Binnet &    Washington     DC
                                   Hardley

 Table   titleauthor Ä Structure

ÖŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
           au_id               title_id  au_ord    royaltyper
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Datatype  id                  tid       smallint  int

 Null      not null            not null  null      null

 Index     nonclust            nonclust  Ä         Ä

           uniq, nonclust,
           composite
           au_id               title_id  au_ord    royaltyper
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
          composite

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Table   titleauthor Ä Contents

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 au_id        title_id  au_ord  royaltyper
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 409-56-7008  BU1032    1       60

 213-46-8915  BU1032    2       40

 238-95-7766  PC1035    1       100

 213-46-8915  BU2075    1       100

 au_id        title_id  au_ord  royaltyper
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 998-72-3567  PS2091    1       50

 899-46-2035  PS2091    2       50

 998-72-3567  PS2106    1       100

 722-51-5454  MC3021    1       75

 899-46-2035  MC3021    2       25

 807-91-6654  TC3218    1       100

 486-29-1786  PS7777    1       100

 486-29-1786  PC9999    1       100

 712-45-1867  MC2222    1       100

 au_id        title_id  au_ord  royaltyper
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 172-32-1176  PS3333    1       100

 274-80-9391  BU7832    1       100

 427-17-2319  PC8888    1       50

 846-92-7186  PC8888    2       50

 756-30-7391  PS1372    1       75

 724-80-9391  PS1372    2       25

 724-80-9391  BU1111    1       60

 267-41-2394  BU1111    2       40

 672-71-3249  TC7777    1       40

 au_id        title_id  au_ord  royaltyper
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 267-41-2394  TC7777    2       30

 472-27-2349  TC7777    3       30

 648-92-1872  TC4203    1       100

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Table   sales Ä Structure

ÖŚÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄ
             stor_id     ord_num      date        qty         payterms     tit
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Datatype    char(4)     varchar(20)  datetime    smallint    varchar(12)  tid

 Null        not null    not null     not null    not null    not null     not
             stor_id     ord_num      date        qty         payterms     tit
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Null        not null    not null     not null    not null    not null     not

 Index       Ä           Ä            Ä           Ä           Ä            non

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Table   sales Ä Contents

ÖŚÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄ
 stor_id  ord_num   date      qty  payterms                          title_id
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 7066     QA7442.3  09/13/85  75   On invoice                        PS2091

 7067     D4482     09/14/85  10   Net 60                            PS2091

 7131     N914008   09/14/85  20   Net 30                            PS2091

 stor_id  ord_num   date      qty  payterms                          title_id
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 7131     N914014   09/14/85  25   Net 30                            MC3021

 8042     423LL922  09/14/85  15   On invoice                        MC3021

 8042     423LL930  09/14/85  10   On invoice                        BU1032

 6380     722a      09/13/85  03   Net 60                            PS2091

 6380     6871      09/14/85  05   Net 60                            BU1032

 8042     P723      03/11/88  25   Net 30                            BU1111

 7896     X999      02/21/88  35   On invoice                        BU2075

 7896     QQ2299    10/28/87  15   Net 60                            BU7832

 7896     TQ456     12/12/87  10   Net 60                            MC2222

 stor_id  ord_num   date      qty  payterms                          title_id
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 8042     QA879.1   05/22/87  30   Net 30                            PC1035

 7066     A2976     05/24/87  50   Net 30                            PC8888

 7131     P3087a    05/29/87  20   Net 60                            PS1372

 7131     P3087a    05/29/87  25   Net 60                            PS2106

 7131     P3087a    05/29/87  15   Net 60                            PS3333

 7131     P3087a    05/29/87  25   Net 60                            PS7777

 7067     P2121     05/15/87  40   Net 30                            TC3218

 7067     P2121     05/15/87  20   Net 30                            TC4203

 7067     P2121     05/15/87  20   Net 30                            TC7777

 stor_id  ord_num   date      qty  payterms                          title_id
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Table   stores Ä Structure

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄŚÄ
              stor_id      stor_name    stor_addres  city         state    zip
                                        s
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Datatype     char(4)      varchar(40)  varchar(40)  varchar(20)  char(2)  cha

 Null         not null     null         null         null         null     nul

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Table   stores Ä Contents

ÖŚÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄŚÄÄ
 stor_id  stor_name              stor_address           city       state  zip
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 7066     Barnum's               567 Pasadena Ave.      Tustin     CA     9278

 7067     News & Brews           577 First St.          Los Gatos  CA     9674

 7131     Doc-U-Mat: Quality     24-A Avrogado Way      Remulade   WA     9801
          Laundry and Books

 8042     Bookbeat               679 Carson St.         Portland   OR     8907

 6380     Eric the Read Books    788 Catamaugus Ave.    Seattle    WA     9805

 7896     Fricative Bookshop     89 Madison St.         Fremont    CA     9001

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ




 discounts Ä Structure

 -         discounttype  stor_id  lowqty    highqty   discount
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Datatype  varchar(40)   char(4)  smallint  smallint  float

 Null      not null      null     null      null      not null


 discounts Ä Contents

 discounttype                      stor_id   lowqty  highqty  discount
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Initial Customer                  Ä         Ä       Ä        10.5

 Volume Discount                   Ä         100     1000     6.7

 Customer Discount                 8042      Ä       Ä        5.0


 roysched Ä Structure

 -                                 title_id  lorange  hirange  royalty
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Datatype                          tid        int      int      int

 Null                              not null   null     null     null

 Index                             nonclust   Ä        Ä        Ä

 Table   roysched Ä Contents

ÖŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄŚÄŚÄÄÄÄÄÄÄÄŚÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ·
 title_id  lorange    hirange    royalty
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 BU1032    0          5000       10

 BU1032    5001       50000      12

 PC1035    0          2000       10
 title_id  lorange    hirange    royalty
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
PC1035    0          2000       10

 PC1035    2001       3000       12

 PC1035    3001       4000       14

 PC1035    4001       10000      16

 PC1035    10001      50000      18

 BU2075    0          1000       10

 BU2075    1001       3000       12

 BU2075    3001       5000       14

 BU2075    5001       7000       16

 BU2075    7001       10000      18
 title_id  lorange    hirange    royalty
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
BU2075    7001       10000      18

 BU2075    10001      12000      20

 BU2075    12001      14000      22

 BU2075    14001      50000      24

 PS2091    0          1000       10

 PS2091    1001       5000       12

 PS2091    5001       10000      14

 PS2091    10001      50000      16

 PS2106    0          2000       10

 PS2106    2001       5000       12
 title_id  lorange    hirange    royalty
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
PS2106    2001       5000       12

 PS2106    5001       10000      14

 PS2106    10001      50000      16

 MC3021    0          1000       10

 MC3021    1001       2000       12

 MC3021    2001       4000       14

 MC3021    4001       6000       16

 MC3021    6001       8000       18

 MC3021    8001       10000      20

 MC3021    10001      12000      22
 title_id  lorange    hirange    royalty
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
MC3021    10001      12000      22

 MC3021    12001      50000      24

 TC3218    0          2000       10

 TC3218    2001       4000       12

 TC3218    4001       6000       14

 TC3218    6001       8000       16

 TC3218    8001       10000      18

 TC3218    10001      12000      20

 TC3218    12001      14000      22

 TC3218    14001      50000      24
 title_id  lorange    hirange    royalty
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
TC3218    14001      50000      24

 TC3218    0          2000       10

 TC3218    2001       4000       12

 TC3218    4001       6000       14

 TC3218    6001       8000       16

 TC3218    8001       10000      18

 TC3218    10001      12000      20

 TC3218    12001      14000      22

 TC3218    14001      50000      24

 PC8888    0          5000       10
 title_id  lorange    hirange    royalty
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
PC8888    0          5000       10

 PC8888    5001       10000      12

 PC8888    10001      15000      14

 PC8888    15001      50000      16

 PS7777    0          5000       10

 PS7777    5001       50000      12

 PS3333    0          5000       10

 PS3333    5001       10000      12

 PS3333    10001      15000      14

 PS3333    15001      50000      16
 title_id  lorange    hirange    royalty
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
PS3333    15001      50000      16

 BU1111    0          4000       10

 BU1111    4001       8000       12

 BU1111    8001       10000      14

 BU1111    12001      16000      16

 BU1111    16001      20000      18

 BU1111    20001      24000      20

 BU1111    24001      28000      22

 BU1111    28001      50000      24

 MC2222    0          2000       10
 title_id  lorange    hirange    royalty
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
MC2222    0          2000       10

 MC2222    2001       4000       12

 MC2222    4001       8000       14

 MC2222    8001       12000      16

 MC2222    8001       12000      16

 MC2222    12001      20000      18

 MC2222    20001      50000      20

 TC7777    0          5000       10

 TC7777    5001       15000      02

 TC7777    15001      50000      14
 title_id  lorange    hirange    royalty
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
TC7777    15001      50000      14

 TC4203    0          2000       10

 TC4203    2001       8000       12

 TC4203    8001       16000      14

 TC4203    16001      24000      16

 TC4203    24001      32000      18

 TC4203    32001      40000      20

 TC4203    40001      50000      22

 BU7832    0          5000       10

 BU7832    5001       10000      12
 title_id  lorange    hirange    royalty
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
BU7832    5001       10000      12

 BU7832    10001      15000      14

 BU7832    15001      20000      16

 BU7832    20001      25000      18

 BU7832    25001      30000      20

 BU7832    30001      35000      22

 BU7832    35001      50000      24

 PS1372    0          10000      10

 PS1372    10001      20000      12

 PS1372    20001      30000      14
 title_id  lorange    hirange    royalty
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
PS1372    20001      30000      14

 PS1372    30001      40000      16

 PS1372    40001      50000      18

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Table   authors Ä Structure

ÖŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄ
           au_id    au_lname     au_fname     phone     address      city

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Datatype  id       varchar(40)  varchar(20)  char(12)  varchar(40)  varchar(2

 Null      not      not null     not null     not null  null         null
           au_id    au_lname     au_fname     phone     address      city

Null      not      not null     not null     not null  null         null
           null

 Default   Ä        Ä            Ä            UNKNOWN1  Ä            Ä

 Index     clust,   nonclust,                 Ä         Ä            Ä
           uniq     composite

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 (1) The default UNKNOWN is inserted if no data is entered.
 (2) The rule ziprule states that the zip code must match the pattern
 [0-9][0-9][0-9][0-9][0-9].


 Table   authors Ä Contents

ÖŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄ
 au_id        au_lname        au_fname     phone     address       city
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 409-56-7008  Bennet          Abraham      415       6223 Bateman  Berkeley
                                           658-9932  St.

 213-46-8915  Green           Marjorie     415       309 63rd St.  Oakland
                                           986-7020  #411

 238-95-7766  Carson          Cheryl       415       589 Darwin    Berkeley
                                           548-7723  Ln.

 998-72-3567  Ringer          Albert       801       67 Seventh    Salt Lake
                                           826-0752  Av.           City

 899-46-2035  Ringer          Anne         801       67 Seventh    Salt Lake
                                           826-0752  Av.           City

 722-51-5454  DeFrance        Michel       219       3 Balding     Gary
                                           547-9982  Pl.

 au_id        au_lname        au_fname     phone     address       city
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 807-91-6654  Panteley        Sylvia       301       1956          Rockville
                                           946-8853  Arlington
                                                     Dr.

 893-72-1158  McBadden        Heather      707       301 Putnam    Vacaville
                                           448-4982

 724-08-9931  Stringer        Dirk         415       5420          Oakland
                                           843-2991  Telegraph
                                                     Av.

 274-80-9391  Straight        Dick         415       5420 College  Oakland
                                           834-2919  Av.

 756-30-7391  Karsen          Livia        415       5720 McAuley  Oakland
                                           534-9219  St.

 724-80-9391  MacFeather      Stearns      415       44 Upland     Oakland
 au_id        au_lname        au_fname     phone     address       city
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
724-80-9391  MacFeather      Stearns      415       44 Upland     Oakland
                                           354-7128  Hts.

 427-17-2319  Dull            Ann          415       3410 Blonde   Palo Alto
                                           836-7128  St.

 672-71-3249  Yokomoto        Akiko        415       3 Silver Ct.  Walnut
                                           935-4228                Creek

 267-41-2394  O'Leary         Michael      408       22 Cleveland  San Jose
                                           286-2428  Av. #14

 472-27-2349  Gringlesby      Burt         707       PO Box 792    Covelo
                                           938-6445

 527-72-3246  Greene          Morningstar  615       22 Graybar    Nashville
                                           297-2723  House Rd.

 172-32-1176  White           Johnson      408       10932 Bigge   Menlo
 au_id        au_lname        au_fname     phone     address       city
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
172-32-1176  White           Johnson      408       10932 Bigge   Menlo
                                           496-7223  Rd.           Park

 712-45-1867  del Castillo    Innes        615       2286 Cram     Ann Arbor
                                           996-8275  Pl. #86

 846-92-7186  Hunter          Sheryl       415       3410 Blonde   Palo Alto
                                           836-7128  St.

 486-29-1786  Locksley        Chastity     415       18 Broadway   San
                                           585-4620  Av.           Francisco

 648-92-1872  Blotchet-Halls  Reginald     503       55 Hillsdale  Corvallis
                                           745-6402  Bl.

 341-22-1782  Smith           Meander      913       10            Lawrence
                                           843-0462  Mississippi
                                                     Dr.

 au_id        au_lname        au_fname     phone     address       city
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 Table   titles Ä Structure

ÖŚÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄŚÄÄÄÄ
           title_id   title        type        pub_id   price   advanc  royalt
                                                                e       y
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 Datatype  tid        varchar(80)  char(12)    char(4)  money   money   int

 Null      not null   not null     not null    null     null    null    null

 Default   Ä          Ä            UNDECIDED1  Ä        Ä       Ä       Ä

 Rule      Ä          Ä            Ä           Ä        Ä       Ä       Ä

           title_id   title        type        pub_id   price   advanc  royalt
                                                                e       y
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 Trigger   deltitle3  Ä            Ä           Ä        Ä       Ä       Ä

 Index     clust,     nonclust     Ä           Ä        Ä       Ä       Ä
           uniq

 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 (1) The default UNDECIDED is inserted if no data is entered in the column.
 (2) The getdate() function inserts the current date as the default if no
 data is entered in the column.
 (3) The deltitle trigger prohibits deleting a title if the title_id is
 listed in the sales table.


 Table   titles Ä Contents

ÖŚÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄŚÄÄÄÄÄÄÄÄÄÄŚÄÄÄÄ
 title_i  title              type          pub_id   price    advance    royalt
 d
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
 BU1032   The Busy           business      1389     $19.99   $5000.00   10
          Executive's
          Database Guide




 PC1035   But Is It User     popular_comp  1389     $22.95   $7000.00   16
          Friendly?




 BU2075   You Can Combat     business      0736     $2.99    $10125.00  24
          Computer Stress!

 title_i  title              type          pub_id   price    advance    royalt
 d






 PS2091   Is Anger the       psychology    0736     10.95    $2275.00   12
          Enemy?




 PS2106   Life Without Fear  psychology    0736     $7.00    $6000.00   10






 title_i  title              type          pub_id   price    advance    royalt
 d






 MC3021   The Gourmet        mod_cook      0877     $2.99    $15000.00  24
          Microwave



 TC3218   Onions, Leeks,     trad_cook     0877     $20.95   $7000.00   10
          and Garlic:
          Cooking Secrets
          of the
          Mediterranean


 MC3026   The Psychology of  UNDECIDED     0877     NULL     NULL       NULL
 title_i  title              type          pub_id   price    advance    royalt
 d
MC3026   The Psychology of  UNDECIDED     0877     NULL     NULL       NULL
          Computer Cooking

 PC8888   Secrets of         popular_comp  1389     $20.00   $8000.00   10
          Silicon
          Valley




 PS7777   Emotional          psychology    0736     $7.99    $4000.00   10
          Security:
          A New Algorithm






 title_i  title              type          pub_id   price    advance    royalt
 d

 PS3333   Prolonged Data     psychology    0736     $19.99   $2000.00   10
          Deprivation:
          Four Case Studies





 BU1111   Cooking with       business      1389     $11.95   $5000.00   10
          Computers:
          Surreptitious
          Balance Sheets


 MC2222   Silicon Valley     mod_cook      0877     $19.99   $0.00      12
          Gastronomic
          Treats

 title_i  title              type          pub_id   price    advance    royalt
 d





 TC7777   Sushi, Anyone?     trad_cook     0877     $14.99   $8000.00   10










 TC4203   Fifty Years in     trad_cook     0877     $11.95   $4000.00   14
          Buckingham Palace
          Kitchens
 title_i  title              type          pub_id   price    advance    royalt
 d
         Kitchens





 BU7832   Straight Talk      business      1389     $19.99   $5000.00   10
          About Computers




 PS1372   Computer Phobic    psychology    0877     $21.59   $7000.00   10
          and Non-Phobic
          Individuals:
          Behavior
          Variations


 title_i  title              type          pub_id   price    advance    royalt
 d



 PC9999   Net Etiquette      popular_comp  1389     NULL     NULL       NULL



 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 The pubs database contains the following database objects:


 Rules


 pub_idrule

   create rule pub_idrule
    as @pub_id in ("1389", "0736", "0877", "1622", "1756")
    or @pub_id like "99[0-9][0-9]"


 ziprule

   create rule ziprule
    as @zip like "[0-9][0-9][0-9][0-9][0-9]"


 Trigger


 deltitle

   create trigger deltitle
    on titles
    for delete
    as
    if (select count(*) from deleted, sales
    where sales.title_id = deleted.title_id) > 0
     begin
     rollback transaction
     print "You can't delete a title with sales."
     end


 Stored Procedure


 byroyalty

   create procedure byroyalty percentage int
    as
    select au_id from titleauthor
    where titleauthor.royaltyper = percentage


 View


 titleview

   create view titleview
    as
    select title, au_ord, au_lname, price, ytd_sales, pub_id
    from authors, titles, titleauthor
    where authors.au_id = titleauthor.au_id
    and titles.title_id = titleauthor.title_id


 Defaults


 datedflt

   create default datedflt
    as getdate( )


 phonedflt

   create default phonedflt
    as "UNKNOWN"


 typedflt

   create default typedflt  as "UNDECIDED"

 Figure A.1 shows the structure of the pubs sample database.

 (This figure may be found in the printed book).







 Glossary
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

 aggregate functions
 Often used with GROUP BY and HAVING clauses, aggregate functions generate
 one summary value from a group of values in a specified column. Aggregate
 functions include AVG, SUM, MIN, MAX, ANY, and COUNT.

 alias
 A temporary name given to a table (in the FROM clause) when it is joined
 with itself in a self join. The temporary table names are then used to
 qualify the column names in the join.

 allocation unit
 A logical unit of SQL Server storage equal to 256 2K data pages, or 1/2
 megabyte. The DISK INIT statement initializes a new database device for SQL
 Server and divides it into 1/2 megabyte pieces called allocation units.

 application
 A customized program that performs tasks required by a specific situation.

 arithmetic operators
 Addition (+), subtraction (-), division (/), and multiplication (*) are
 arithmetic operators that can be used with numeric columns. Modulo (%) can
 be used with int, smallint, and tinyint columns only.

 backup
 Any procedure for bringing a computer system back to where it was prior to a
 system malfunction. In the microcomputer environment, backup refers to the
 process of copying data files to a second, or backup, disk.

 backup file
 A duplicate file maintained on a separate disk and kept in a safe location
 as insurance against loss of the original file.

 base date
 The reference date for SQL Server server, January 1, 1900. If the user does
 not supply a date, the base date is the default date.

 base tables
 The permanent tables on which a view is based. Base tables are also called
 underlying tables.

 batch
 One or more TRANSACT-SQL statements terminated by an end-of-batch signal,
 which submits them to the SQL Server for processing.

 binary string
 A string of binary-encoded data.

 built-in functions
 A wide variety of functions that take one or more parameters and return
 results. The built-in functions include mathematical functions, system
 functions, string functions, text functions, date functions, and a datatype
 conversion function.

 Cartesian product
 All the possible combinations of the rows from each of the tables. The
 number of rows in the Cartesian product is equal to the number of rows in
 the first table times the number of rows in the second table. Once the
 Cartesian product has been formed, the rows that do not satisfy the join
 conditions are eliminated. SQL Server creates the Cartesian product as the
 first step in joining tables.

 cascading delete
 An operation implemented with a trigger that deletes data from a table based
 on a deletion from another table. It is often performed in order to delete
 detail data when master data is deleted.

 character string
 Any group of concatenated characters handled as a single data item by a
 program. Character strings are normally enclosed in quotation marks since
 they may contain punctuation or blank spaces.

 checkpoint
 The point at which all data pages that have been changed are guaranteed to
 have been written to the disk.

 clause
 Part of a statement beginning with a keyword that names the basic operation
 to be performed.

 clustered index
 An index in which the physical order and the logical (indexed) order is the
 same. The leaf level of a clustered index represents the data pages
 themselves.

 column
 The logical equivalent of a field, it contains an individual data item
 within a row or record.

 command
 An instruction that specifies an operation to be performed by the computer.

 command terminator
 The end-of-batch signal that sends the batch to SQL Server for processing.

 comparison operators
 Equal to (=), greater than (>), less than (<), greater than or equal to
 (>>=), less than or equal to (<=), not equal to (!=), not greater than
 (!>>), and not less than (!<) can all be used in the format expression
 comparison_operator expression, where an expression is a column name or a
 constant or any combination of column names and constants.

 composite index
 An index entry composed of two or more columns.

 concatenation
 Combining expressions to form longer expressions. The expressions can
 include binary or character strings, column names, or any combination.

 concurrency
 When more than one user accesses a particular set of records or files at the
 same time.

 context-sensitive protection
 Provides certain permissions depending on the identity of the user. This
 type of protection can be provided by SQL Server using a view and the
 USER_ID system function.

 CONTROL key
 A key that assigns another function to certain keys on the computer
 keyboard. Like the SHIFT key, the CONTROL key is held down while another is
 pressed. This effectively increases the number of keys on the keyboard.

 control-of-flow language
 Programming-like constructs (IF, ELSE, WHILE, GOTO, and so on) provided by
 TRANSACT-SQL so that the user can control the flow of execution of
 TRANSACT-SQL statements.

 conversion function
 A function used to convert expressions of one datatype into another datatype
 whenever these conversions are not performed automatically by SQL Server.

 correlated subquery
 A subquery that cannot be evaluated independently, but depends on the outer
 query for its results. It is also called a repeating subquery, since the
 subquery is executed once for each row that might be selected by the outer
 query. See also subquery and nested query.

 data definition
 The process of setting up databases and creating database objects such as
 tables, indexes, rules, defaults, procedures, triggers, and views.

 data dictionary
 The system tables that contain descriptions of the database objects and how
 they are structured.

 data integrity
 Accuracy and reliability of data provided through transaction logging,
 stored procedures, and triggers in SQL Server.

 data modification
 Adding, deleting, or changing information in the database with the INSERT,
 DELETE, and UPDATE statements.

 data sharing
 Users' ability to transparently share individual pieces of data in a
 database across different applications.

 database
 A set of related data tables and other database objects that are organized
 and presented to serve a specific purpose.

 database device
 A file on which databases and transaction logs are stored. A database device
 has both a physical name and a logical name.

 database management system (DBMS)
 A consistent method for storing, organizing, retrieving, and calculating
 data in database devices.

 database object
 One of the components of a database: a table, view, index, procedure,
 trigger, column, default, or rule.

 Database Owner
 The user who creates a database. A Database Owner has control over all the
 database objects in that database.

 datatype
 Specifies what kind of information each column will hold and how the data
 will be stored. Eleven system-supplied datatypes are available, and
 user-defined datatypes can also be built.

 date function
 A function that displays information about dates and times, or manipulates
 date or time values. The five date functions are GETDATE, DATENAME,
 DATEPART, DATEDIFF, and DATEADD.

 DB-LIBRARY
 A set of C functions and macros that allow your application to interact with
 SQL Server.

 deadlock
 A situation which arises when two users, each having a lock on one piece of
 data, attempt to acquire a lock on the other's piece of data. SQL Server
 detects deadlocks and kills one user's process.

 default
 The option chosen by the system when no other option is specified.

 default database
 The database that a user gets by default when he or she logs in.

 demand lock
 Prevents any more shared locks from being set on a data resource (table or
 data page). Any new shared lock request has to wait for the demand lock
 request to finish.

 dependent
 Data is logically dependent on other data when master data in one table must
 be kept synchronized with detail data in another table to protect the
 logical consistency of the database.

 detail data
 Data in a detail table that logically depends on data in a master table.

 device
 Any part of the computer that is capable of sending or receiving
 information. The keyboard is an example of an input device; the video
 monitor and printer are output devices.

 difference
 In comparing tables, the set of all elements which belong to one table but
 not to another.

 disk allocation pieces
 Disk allocation pieces are the groups of allocation units from which SQL
 Server constructs a new database device. The minimum size for a disk
 allocation piece is one allocation unit or 256 2K pages.

 drive
 The disk drive identifier. When used as part of a filename, the disk drive
 identifier must be followed by a colon.

 dump device
 A diskette file or hard disk file used to store a dumped database. A dump
 device has both a physical name and a logical name.

 dynamic dump
 A dump made while the database is active.

 equijoin
 A join in which the values in the columns being joined are compared on the
 basis of equality, and all the columns in the tables being joined are
 included in the results.

 error message
 A message that SQL Server displays at the user's terminal when it detects an
 error condition.

 error state number
 The number attached to an SQL Server error message that allows unique
 identification of the line of SQL Server code at which the error was raised.

 exclusive lock
 A lock that prevents any other transaction from acquiring a lock until the
 original lock is released at the end of a transaction. An exclusive lock is
 always applied for update operations (INSERT, UPDATE, and DELETE).

 expression
 Returns a value. An expression can be a computation, column data, a built-in
 function, or a subquery.

 extent lock
 A lock held on a group of database pages while they are being allocated or
 freed.

 fatal error
 An error with a severity level of 19 and above. It terminates the user's
 work session so that it is necessary to log in again.

 field
 A single item of information contained in a record. The logical equivalent
 of a column.

 file
 The logical equivalent of a table.

 foreign key
 A column (or combination of columns) whose values are required to match a
 primary key in some other table.

 functions
 See built-in functions.

 global variables
 System-defined variables that SQL Server updates on an ongoing basis. For
 example, @ERROR contains the last error number generated by the system.

 guest account
 If the user name guest exists in the sysusers table of a database, any user
 with a valid SQL Server login ID can use that database, with limited
 permissions.

 identifier
 The name of a database object (table, view, index, procedure, trigger,
 column, default, or rule). An identifier can be from 1 to 30 characters
 long. The first character must be a letter or the # or _ symbol. The
 following characters can be letters, digits, or the symbols #, $, or _. (A
 table name beginning with # denotes a temporary table.) Embedded spaces are
 not allowed.

 index
 A set of pointers that are logically ordered by the values of a key. Indexes
 provide quick access to data and can enforce uniqueness on the rows in a
 table.

 infected process
 A process that terminates abnormally.

 inner query
 Another name for a subquery.

 intent lock
 Indicates the intention to acquire a share or exclusive lock on a data page.

 intersection
 In table comparisons, the set of all elements that belong to both tables.

 join
 A basic operation in a relational system that links the rows in two or more
 tables by comparing the values in specified columns.

 key
 A field used to identify a record, often used as the index field for a
 table.

 key value
 Any value that is indexed.

 keyword
 A word or phrase that is reserved for exclusive use by TRANSACT-SQL. A
 keyword is also known as a reserved word.

 leaf level
 The bottom level of a clustered or nonclustered index. In a clustered index,
 the leaf level contains the actual data pages of the table.

 livelock
 A request for an exclusive lock that is repeatedly denied because a series
 of overlapping shared locks keeps interfering. The SQL Server detects the
 situation after four denials, and refuses further shared locks.

 local area network (LAN)
 A system that enables PCs to have access to common data and peripherals.
 LANs typically consist of PCs with adapter cards, file servers, a network
 operating sytem, printers, and gateways to departmental or corporate
 computers.

 local variable
 A variable declared with the DECLARE keyword and assigned an initial value
 with a SELECT statement.

 locking
 The process of restricting access to resources in a multiuser environment to
 maintain security and prevent concurrent access problems. SQL Server
 automatically applies locks to tables or pages.

 logical database devicename
 The name used by SQL Server for a database device. The filename must follow
 the rules for identifiers.

 logical dump device name
 The name used by SQL Server for a dump device.

 logical operators
 Operators (AND, OR, and NOT) that can be used in WHERE clauses. AND joins
 two or more conditions and returns results when all of the conditions are
 true. OR connects two or more conditions and returns results when any one of
 the conditions is true.

 login ID
 The name a user uses to log in to SQL Server. A login ID is valid if SQL
 Server has an entry for that user in the system table syslogins.

 Master Database
 The Master Database (master) controls the user databases and the operation
 of SQL Server as a whole. It keeps track of such things as user accounts,
 ongoing processes, and system error messages.

 master database device
 A file stored in the master database.

 master table
 A table that contains data on which detail data in another table logically
 depends. The master table frequently has a primary key, and a detail table
 will have a matching foreign key.

 mathematical function
 A function used for operations on numeric data.

 message number
 The number that uniquely identifies an error message.

 modulo
 An arithmetic operator represented by the percent sign (%) that gives the
 integer remainder after a division operation on two integers. For example,
 21 % 9 = 3 because 21 divided by 9 equals 2 with a remainder of 3.

 MS OS/2
 Microsoft Operating System/2, the second-generation operating system
 codeveloped by Microsoft and IBM to exploit the power and speed of personal
 computers based on the Intel 80286 and 80386 microprocessors.

 multitasking
 The concurrent execution of two or more distinct tasks by a computer.

 multiuser
 The ability of a computer to support many users operating at the same time,
 while providing the full range of capabilities of the computer system to
 each user.

 natural join
 A join in which the values of the columns being joined are compared on the
 basis of equality, and all the columns in the tables are included in the
 results, except that only one of each pair of joined columns is included.

 nested query
 A SELECT statement that contains one or more subqueries.

 nested select statement
 See nested query.

 nonclustered index
 An index that stores key values and pointers to data. The leaf level points
 to data pages rather than containing the data itself.

 normalization rules
 The standard rules of database design in a relational database management
 system.

 not-equal join
 A join on the basis of inequality.

 null
 Having no explicitly assigned value. NULL is not equivalent to zero or
 blank. A value of NULL is not considered to be greater than, less than, or
 equivalent to any other value, including another value of NULL.

 object
 See database object.

 object permissions
 Permissions that regulate the use of certain statements (data modification
 statements, plus SELECT, TRUNCATE TABLE, and EXECUTE) to specific tables,
 views, or columns. See also statement permission.

 operating system
 A group of programs that translates your statements to the computer, helping
 you perform such tasks as creating files, running programs, and printing
 documents. SQL Server runs on MS OS/2.

 operators
 See arithmetic operators, comparison operators, and logical operators.

 option
 An optional value supplied to a statement, program, or procedure.

 outer join
 A join in which both matching and nonmatching rows are returned. The
 operators *= and =* are used to indicate that all the rows in the first or
 second table should be returned, regardless of whether or not there is a
 match on the join column.

 outer query
 Another name for the principal query in a statement containing a subquery.

 parameter
 A value supplied to a statement, program, or procedure.

 parse
 The process of breaking down a program statement into its separate
 processing functions.

 permissions
 The authority to perform certain actions on certain database objects or to
 run certain statements.

 physical database devicename
 The operating sytem filename for a database device. The operating system
 filename must include a complete path.

 physical dump device name
 The operating system filename for a dump device. The operating sytem
 filename must include a complete path.

 precompiled
 Analyzed and executed according to a stored plan.

 primary key
 The column or columns whose values uniquely identify a row in a table.

 projection
 One of the basic query operations in a relational system. A projection is a
 subset of the columns in a table.

 qualification
 A search condition.

 qualified
 Preceding the name of a database object with the name of the database and
 the object owner.

 query
 A request for the retrieval of data with a SELECT statement.

 record
 A group of related fields (columns) of information treated as a unit; the
 logical equivalent of a row.

 referential integrity
 The rules governing data consistency, specifically the relationships among
 the primary keys and foreign keys of different tables. SQL Server addresses
 referential integrity with user-defined triggers.

 relation
 Synonym for table.

 relational database management system
  Organizes data into rows and columns.

 repeating subquery
 See correlated subquery.

 reserved word
 A word reserved by SQL Server for use as a keyword. Reserved words cannot be
 used for names of database objects but can be used for names of local
 variables and stored procedure parameters.

 restriction
 One of the basic query operations in a relational system; also called
 selection. A restriction is a subset of the rows in a table.

 rollback transaction
 A TRANSACT-SQL statement used with a user-defined transaction (before a
 COMMIT TRANSACTION has been received) that cancels the transaction and
 undoes any changes that were made to the database.

 row
 A set of related columns that describes a specific entity; also called a
 record.

 row aggregate functions
 Functions (SUM, AVG, MIN, MAX, and COUNT) that generate a new row for
 summary data when used with COMPUTE in a SELECT statement.

 rule
 A specification that controls what data can be entered in a particular
 column or in a column of a particular user-defined datatype.

 SAF
 See SQL Server Administration Facility (SAF).

 savepoint
 A marker that the user puts inside a user-defined transaction. When
 transactions are rolled back, they are only rolled back to the savepoint.

 scalar aggregate
 An aggregate function that produces a single value from a SELECT statement
 that does not include a GROUP BY clause. This is true whether the aggregate
 function is operating on all the rows in a table or on a subset of rows
 defined by a WHERE clause. (See also vector aggregate.)

 screen
 The display portion of a video terminal.

 select list
 The columns specified in the main clause of a SELECT statement.

 selection
 See restriction.

 self-join
 A join used for comparing values within a column of a table. Since this
 operation involves a join of a table with itself, you need to give the table
 two temporary names, or aliases. The aliases are then used to qualify the
 column names in the rest of the query.

 server
 A computer on a local-area network that controls access to resources such as
 files, printers, and modems.

 server user ID
 The ID number by which a user is known to SQL Server.

 severity level number
 The severity of an error condition: errors with severity levels of 19 and
 above are fatal errors.

 shared lock
 A lock created by nonupdate (read) operations. Other users can read the data
 concurrently, but no transaction can acquire an exclusive lock on the data
 until all the shared locks have been released.

 SQL
 An acronym for Structured Query Language, a database query and programming
 sublanguage originally developed for IBM mainframe computers. There is now
 an ANSI standard SQL definition for all computer systems.

 SQL Server Administration Facility (SAF)
 A facility that includes menus for performing many system administration
 functions and an SQL window for making SQL queries.

 statement
 Part of SQL Server language that begins with a keyword that names the basic
 operation to be performed.

 statement block
 A series of TRANSACT-SQL statements enclosed between the keywords BEGIN and
 END so that they are treated as a unit.

 statement permission
 Permission to execute a specific statement. See also object permissions.

 stored procedure
 A collection of SQL statements and optional control-of-flow statements
 stored under a name. SQL Server-supplied stored procedures are called system
 procedures.

 string
 See character string.

 string function
 A function that operates on strings of characters or binary data. There are
 two TRANSACT-SQL string functions, SUBSTRING and CHARINDEX.

 Structured Query Language
 See SQL.

 subquery
 A SELECT statement that is nested inside another SELECT, INSERT, UPDATE, or
 DELETE statement, or inside another subquery.

 syntax
 The rules governing the structure of a command.

 System Administrator
 The person or persons responsible for the administrative and operational
 functions that are independent of any particular application. The System
 Administrator is likely to be a person with the best overview of all the
 applications. The System Administrator advises application designers about
 the data that already exists on SQL Server, makes recommendations about
 standardizing data definitions across applications, and so on.

 system databases
 The three databases on a newly installed SQL Server: the master database,
 which controls user databases and the operation of SQL Server; the tempdb
 database, which is used for temporary tables; and the model database, which
 is used as a template to create new user databases.

 system function
 A function that returns special information from the database, particularly
 from the system tables.

 system procedures
 Stored procedures that SQL Server supplies as shortcuts for retrieving
 information from the system tables or mechanisms for accomplishing database
 administration and other tasks that involve updating system tables.

 system table
 One of the data dictionary tables. The system tables keep track of
 information about SQL Server as a whole and about each user database. The
 master database contains some system tables that are not in user databases.

 table
 A collection of rows (records) that have associated columns (fields). A
 table is the logical equivalent of a database device.

 target list
 See select list.

 text function
 A function used for operations on text data.

 theta join
 A join that uses the comparison operators as the join condition. Comparison
 operators include equal to (=), greater than (>), less than (<), greater
 than or equal to (>>=), less than or equal to (<=), not equal to (!=), not
 greater than (!>>), and not less than (!

 tick
 On MS OS/2, 312.5 milliseconds.

 transaction
 A series of processing steps that results in a specific function or activity
 being completed. A mechanism for ensuring that a set of actions is treated
 as a single unit of work. See also user-defined transaction.

 transaction log
 A system table (syslogs) in which all changes to the database are recorded.

 TRANSACT-SQL
 SQL Server's enhanced version of SQL, it is the standard language for
 communicating between applications and the server. TRANSACT-SQL includes
 statements, commands, control-of-flow language, stored procedures, triggers,
 rules and defaults. Extensions such as stored procedures make TRANSACT-SQL a
 full programming language.

 trigger
 A special form of a stored procedure that goes into effect when a user gives
 a change statement such as INSERT, DELETE, or UPDATE to a specified table or
 column. Triggers are often used to enforce referential integrity.

 trigger actions
 The actions for which a trigger is specified.

 trigger conditions
 The conditions that cause a trigger to take effect.

 trigger table
 The table to which a trigger is attached.

 unique index
 An index in which no two rows are permitted to have the same index value.

 update
 An addition, deletion, or change to data involving INSERT, DELETE, TRUNCATE
 TABLE, or UPDATE statements.

 user-defined datatype
 A definition of the type of data a column can contain; created by the user.
 These datatypes are defined in terms of the existing system datatypes. Rules
 and defaults can be bound to user-defined datatypes (but not to system
 datatypes).

 user-defined transaction
 A method of allowing users to group any number of simple transactions as a
 single unit which can be completed or canceled.

 utility program
 A program provided with SQL Server. Utility programs are executed from the
 operating system command line.

 variable
 An entity (user-defined with DECLARE or system-defined as a global variable)
 that can assume any of a set of values.

 vector aggregate
 A value that results from using an aggregate function with a GROUP BY
 clause. (See also scalar aggregate.)

 view
 An alternative way of looking at the data in one or more tables. A view is
 usually created as a subset of columns from one or more tables.

 view resolution
 In queries that involve a view, the process of verifying the validity of
 database objects in the query, and combining the query and the stored
 definition of the view.

 wildcard characters
 Special characters, the underscore (_) and the percent sign (%), used with
 the LIKE keyword to stand for one (_) or any number of (%) characters in
 pattern-matching.

 window
 A portion of the display that appears as a separate viewing area outlined by
 a frame or border.

 workstation
 A computer used by a user to work on a local-area network.





 INDEX
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ



 + string function

     A
 ABS mathematical function
 ACOS mathematical function
 Aggregate functions
   and datatype
   and GROUP BY clause
   and null value
   AVG
   COUNT
   COUNT(*)
   DISTINCT
   MAX
   MIN
   multiple columns
   nest
   SUM
   syntax
 Alias
   in correlated subquery
   in subquery
   table name
 ALL keyword
   and GROUP BY clause
   with subquery
 ALLOW_DUP_ROW keyword
 ALTER DATABASE statement
 ALTER TABLE statement
 AND operator
 ANY keyword
   with subquery
 Arithmetic operator
   add (+)
   divide (/)
   modulo (%)
   multiply (*)
   precedence
   subtract (-)
 ASC keyword
 ASCII string function
 ASIN mathematical function
 ATAN mathematical function
 ATN2 mathematical function
 AVG
   aggregate function
   row aggregate function

     B
 Backslash in long string
 Base table
 Batch
   error
   example
   file
   from isql
   rules
 BEGIN keyword
 BEGIN TRANSACTION statement
 BETWEEN keyword
 binary datatype
 bit
   datatype conversion
   datatype
 Boolean expression
 BREAK keyword
   and nested loop
 Break
 BREAK
   keyword
 Built-in function

     C
 Cartesian product
 Cascading delete
 Case sensitivity
 CEILING mathematical function
 char datatype
 char
   datatype conversion
   datatype
 Character
   display string in query results
   quotation marks in string
   wildcard
 CHARINDEX string function
 Checkpoint
 Clauses
   COMPUTE BY
   COMPUTE
   FROM
   GROUP BY
   HAVING
   INTO
   ON DEFAULT
   ON
   ORDER BY
   SET
   WHERE
 Clustered index
 CLUSTERED keyword
 Column
   add to existing table
   bind default
   choose specific
   choose
   computed in view definition
   computed in view
   define as NULL
   heading name
   insert computed
   name
   rearrange order
   rename in query results
   retrieve
 COL_LENGTH system function
 COL_NAME system function
 Comment
 COMMIT TRANSACTION statement
 Comparison operator
   and correlated subquery
   in subquery
   syntax
 COMPUTE BY clause
   groups
   in view definition
   multiple columns
 COMPUTE clause
   and aggregate function
   multiple
   rules
 Concatenate expressions
 Configuration option
 CONTINUE keyword
 Control-break report
 Control-of-flow
   keywords
   language
 Convention, naming
 Conversion function
 CONVERT function
   style parameter
 Correlated subquery
   in HAVING clause
   with alias
   with comparison operator
 COS mathematical function
 COT mathematical function
 COUNT(*)
   aggregate function
   and null value
 COUNT
   aggregate function
   row aggregate function
 CREATE DATABASE statement
 CREATE DEFAULT statement
 CREATE INDEX statement
 CREATE PROCEDURE statement
   display text
 CREATE RULE statement
 CREATE TABLE statement
 CREATE TRIGGER statement
   display text
 CREATE VIEW statement

     D
 Data
   definition
   delete
   dictionary
   group results
   insert into selected columns
   integrity
   logical independence
   modification statement
   modify
   restrict access with view
   retrieve from more than one table
   update
 Database Consistency Checker
 Database object
   display information about
   help
   naming convention
   owner permissions
   rename
 Database option
   select into/bulkcopy
 Database Owner
   permissions
 Database
   backup
   change default
   change size
   choose
   create
   default
   delete
   device
   drop
   help
   integrity
   master
   model
   object name
   options
   owner
   pubs
   relational management system
   relational
   sample
   size
   store
   system
   temporary
   transfer ownership
   user
 Datatype
   and aggregate function
   assign to column
   binary
   bind default to user-defined
   bit
   char
   conversion and join
   conversion default length
   conversion function
   conversion hierarchy
   conversion
   create
   datetime
   default
   defaults
   delete
   drop user-defined
   enter character and date
   entry rule
   float
   image
   int
   length
   money
   rename user-defined
   rules
   smallint
   specify length
   system-supplied
   text
   timestamp
   tinyint
   user-defined
   varbinary
   varchar
 Date functions
   DATEADD
   DATEDIFF
   DATENAME
   DATEPART
   GETDATE
 Date
   calculate
   compute
   current
   default display
   format
   in comparison
 DATEADD date function
 DATEDIFF date function
 DATENAME date function
 DATEPART date function
 datetime datatype
 datetime
   datatype conversion
   datatype
 DBCC statement
   options
 DB_ID system function
 DB_NAME system function
 Deadlock
 DECLARE keyword
 Default
   and null value
   bind
   create
   datatype
   defined
   delete
   drop
   rename
   temporary
   unbind
   user-defined
 DEGREES mathematical function
 DELAY option
 DELETE statement
   with nested subquery
 DESC keyword
 Device, database
 Difference set theory operation
 DIFFERENCE string function
 DISTINCT keyword
   and aggregate function
   and null values
   and row aggregate function
   in subquery
 DROP DATABASE statement
 DROP DEFAULT statement
 DROP INDEX statement
 DROP PROCEDURE statement
 DROP RULE statement
 DROP TABLE statement
 DROP TRIGGER statement
 DROP VIEW statement
 DUMP statement
 Duplicate listing, eliminate

     E
 ELSE keyword
 END keyword
 Equijoin
 Error
   display message
   flag
   in batch
 ERROREXIT option
 EXECUTE keyword
 EXECUTE statement
   WITH RECOMPILE
 EXISTS keyword
   in subquery
 EXP mathematical function
 Expression
   concatenate
   subquery as substitute
   with aggregate function

     F
 Field
 File, batch
 FILLFACTOR keyword
 float datatype
 FLOOR mathematical function
 Foreign key
   insert NULL
   insert
   update
 FROM clause (in join)
 FROM clause
   with DELETE
 Functions
   aggregate
   built-in
   conversion
   CONVERT
   date
   image
   mathematical
   nest string
   row aggregate
   string
   system
   text
   view with built-in

     G
 GETDATE date function
 Global variable

     @CONNECTIONS
     @CPU_BUSY
     @ERROR
     @IDLE
     @IO_BUSY
     @MAX_CONNECTIONS
     @NESTLEVEL
     @PACKET_ERRORS
     @PACK_RECEIVED
     @PACK_SENT
     @PROCID
     @ROWCOUNT
     @TEXTSIZE
     @TIMETICKS
     @TOTAL_ERROR
     @TOTAL_READ
     @TOTAL_WRITE
     @TRANCOUNT
     @VERSION
 GOTO Statement
 GRANT statement
   combine with REVOKE
   conflict with REVOKE
 GROUP BY clause
   and aggregate functions
   and ALL keyword
   and null value
   and ORDER BY clause
   and WHERE clause
   in subquery
   in view definition
   multiple columns
   syntax
   without aggregate function
 Guest user

     H
 HAVING clause
   and logical operator
   compared to WHERE clause
   with correlated subquery
   without aggregate functions
 Heading
   column name
 Help
   database object
   database
   index
   join
   stored procedure
   view
 HOLDLOCK keyword
 HOST_ID system function
 HOST_NAME system function

     I
 Identifier
 IF keyword
 IF UPDATE keyword
 IGNORE_DUP_KEY keyword
 IGNORE_DUP_ROW keyword
 Image functions
   PATINDEX
   TEXTPTR
   TEXTSIZE
   TEXTVALID
 image
   change value
   datatype
   function
   select value
 IN keyword
   in subquery
 Index
   clustered
   composite
   considerations
   create
   delete
   duplicate value
   help
   leaf level
   multiple columns
   nonclustered
   primary key
   search
   unique
 INDEX_COL system function
 Infected process
 Inner query
 INSERT statement
   error
   with nested subquery
   with SELECT
 Insert
   computed columns
 int datatype
 Integrity
   data
   referential
 Intersection set theory operation
 INTO clause
 ISNULL system function
 isql utility program

     J
 Join
   and view
   compared with subquery
   expression
   help
   multiple tables
   natural
   not-equal
   operator
   operator, multiple
   operator, outer
   outer
   processing
   relational model
   relational operation
   selection criteria
   self
   theta
   with null value

     K
 Keys
   and views
   delete
   foreign
   insert foreign
   insert NULL foreign
   insert primary
   primary
   update foreign
   update primary
 Keywords
   ALL
   ALLOW_DUP_ROW
   ASC
   BEGIN
   BETWEEN
   BREAK
   CLUSTERED
   CONTINUE
   Control-of-flow
   DECLARE
   DESC
   DISTINCT
   ELSE
   END
   EXECUTE
   EXISTS
   FILLFACTOR
   HOLDLOCK
   IF UPDATE
   IF
   IGNORE_DUP_KEY
   IGNORE_DUP_ROW
   IN
   LIKE
   NONCLUSTERED
   NOT BETWEEN
   NOT IN
   NOT LIKE
   NOT
   PUBLIC
   SELECT
   UNIQUE
   VALUES
   WHILE
   WITH RECOMPILE

     L
 Label, GOTO
 Leaf level
   clustered index
   nonclustered index
 LIKE keyword
   with datetime value
 Livelock
 LOAD statement
 Local variable
 Lock
   dead
   exclusive
   live
   shared
 LOG mathematical function
 LOG10 mathematical function
 Logical operator
   and HAVING clause
   precedence
 Login ID
 LOWER string function
 LTRIM string function

     M
 master database
 Mathematical functions
   ABS
   ACOS
   ASIN
   ATAN
   ATN2
   CEILING
   COS
   COT
   DEGREES
   EXP
   FLOOR
   LOG
   LOG10
   PI
   POWER
   RADIANS
   RAND
   ROUND
   SIGN
   SQRT
   TAN
 MAX
   aggregate function
   row aggregate function
 Message, display
 MIN
   aggregate function
   row aggregate function
 model database
 Modulo arithmetic operator
 money datatype
 money
   datatype conversion
   datatype

     N
 Name
   database object
   table
 Naming convention
   database object
   table
 Natural join
 Nest
   aggregate functions
   groups
   query
   scalar aggregate
   SELECT statements
   sort
   stored procedure
   string functions
   subquery
   vector aggregate
   WHILE loops
 Nonclustered index
 NONCLUSTERED keyword
 Normalization rule
 NOT BETWEEN keyword
 NOT EXISTS
   in subquery
 NOT IN keyword
   in subquery
 NOT keyword
 NOT LIKE keyword
 NOT operator
 Not-equal join
 Notational conventions
 Null value
   and aggregate functions
   and COUNT(*)
   and default
   and GROUP BY clause
   compare
   in computation
   in computed columns
   in joins
   in stored procedure
   in underlying object
   trigger
 NULL
   defined
   sort

     O
 Object permissions
 OBJECT_ID system function
 OBJECT_NAME system function
 ON clause
 ON DEFAULT clause
 Operator
   comparison
   join
 Options
   DELAY
   ERROREXIT
   PROCESSEXIT
   TIME
 OR operator
 ORDER BY clause
 Outer join
 Outer query
 Owner, database

     P
 Parameter
   stored procedure default
   use multiple
 Password
 PATINDEX function
 Permissions
   assign
   CREATE PROCEDURE
   data modification
   Database Object Owner
   Database Owner
   DROP TRIGGER
   hierarchy
   object
   on view
   public
   statement
   stored procedure
   summary table
   System Administrator
   system procedures
   table owner
   trigger
 PI mathematical function
 POWER mathematical function
 Primary key
   delete
   index
   insert
   update
 PRINT statement
 Procedure
   name
   rename
   stored
 PROCESSEXIT option
 Projection relational operation
 Projection
 PUBLIC keyword
 pubs sample database

     Q
 Qualification
 Query results
   display character string
   rename columns
   sort
 Query
   inner
   nest
   outer
   processing option
   view
 Quotation mark
   use in character strings

     R
 RADIANS mathematical function
 RAISERROR statement
 RAND mathematical function
 Range search condition
 READTEXT statement
 Record
 Referential integrity
 Relational database
   management system
 Relational operation
 Relational operators
 REPLICATE string function
 Report
   control-break
 Restriction relational operation
 Results
   group data
   sort
 RETURN statement
 REVOKE statement
   combine with GRANT
   conflict with GRANT
 RIGHT string function
 ROLLBACK TRANSACTION statement
 ROUND mathematical function
 Row aggregate functions
   AVG
   COUNT
   MAX
   MIN
   SUM
 Row
   aggregate function
   choose
   copy
   delete all
   duplicate
   insert
   summary
   unique
 Rule
   bind
   create
   datatype
   defined
   delete
   display text
   drop
   for batch file
   normalization
   rename
   stored procedure
   unbind

     S
 SAF
    see SQL Server Administration Facility; see
 Sample database
 Sample database, pubs
 SAVE TRANSACTION statement
 Savepoint
 Scalar aggregate
   nest
 Search condition
   comparison operator
   LIKE clause
   lists
   range
   wildcard
 Security
   and permissions
   with view
 SELECT * statement
 select into/bulkcopy database option
 SELECT keyword
   in join
 Select list
   compute values
   display character string
   DISTINCT keyword
   summary
 SELECT statement
   and variable assignment
   and view
   clause order
   in INSERT statement
   nest
   use to add row
 Select
   image value
   text value
 Self-join
   subquery
 SET clause
 SET statement
   options
 Set theory operation
 SETUSER statement
 SIGN mathematical function
 smallint datatype
 Sort
   nest
   query results
 SOUNDEX string function
 SPACE string function
 Space
   used by table
 sp_addtype
 sp_adduser
 sp_bindefault
 sp_bindrule
 sp_changedbowner
 sp_commonkey
 sp_configure
 sp_defaultdb
 sp_depends
 sp_droptype
 sp_help
 sp_helpdb
 sp_helpindex
 sp_helpjoins
 sp_helptext
 sp_rename
 sp_spaceused
 sp_unbindefault
 sp_unbindrule
 SQL Server Administration Facility (SAF)
 SQL
   extension
   history
 SQRT mathematical function
 Statement permissions
 Statements
   ALTER DATABASE
   ALTER TABLE
   BEGIN TRANSACTION
   COMMIT TRANSACTION
   CREATE DATABASE
   CREATE DEFAULT
   CREATE INDEX
   CREATE PROCEDURE
   CREATE RULE
   CREATE TABLE
   CREATE TRIGGER
   CREATE VIEW
   DBCC
   DELETE
   DROP DATABASE
   DROP DEFAULT
   DROP PROCEDURE
   DROP RULE
   DROP TABLE
   DROP TRIGGER
   DROP VIEW
   DUMP
   GOTO
   GRANT
   INSERT
   LOAD
   PRINT
   RAISERROR
   READTEXT
   RETURN
   REVOKE
   ROLLBACK TRANSACTION
   SAVE TRANSACTION
   SELECT
   SET
   SETUSER
   TRUNCATE TABLE
   UPDATE STATISTICS
   UPDATE
   USE
   WAITFOR
   WRITETEXT
 Stored procedure
   as security mechanism
   create
   default parameter
   delete
   display report
   display text
   drop object
   execute
   group
   help
   list references
   multiple parameters
   name
   nest
   null value as default
   object owner name
   parameter
   permissions
   reference temporary table
   rename
   rule
   wildcard character as default
   WITH RECOMPILE
 STR string function
 String functions
   +
   ASCII
   CHAR
   CHARINDEX
   concatenate
   DIFFERENCE
   LOWER
   LTRIM
   nest
   REPLICATE
   RIGHT
   SOUNDEX
   SPACE
   STR
   STUFF
   SUBSTRING
   UPPER
 String
   match
 Structured query language
    see SQL; see
 STUFF string function
 Style parameter
   CONVERT
 Subquery
   compared to not-equal join
   compared with join
   correlated
   correlated, in trigger
   instead of expression
   nest
   repeating
   rules
   select list
   self-join
   syntax
   table name
   with alias
   with ALL keyword
   with ANY keyword
   with comparison operator
   with DISTINCT keyword
   with EXISTS keyword
   with GROUP BY clause
   with IN keyword
   with modified comparison operator
   with NOT EXISTS
   with NOT IN keyword
   with WHERE clause
 SUBSTRING string function
 SUM
   aggregate function
   row aggregate function
 Summary
   row
   value trigger
   value
 SUSER_ID system function
 SUSER_NAME system function
 Syntax
   aggregate function
   ALTER DATABASE statement
   ALTER TABLE statement
   BEGIN TRANSACTION
   BEGIN...END
   BREAK
   CHARINDEX string function
   COMMIT TRANSACTION
   comparison operator
   COMPUTE BY
   COMPUTE clause
   CONTINUE
   CONVERT
   CREATE DATABASE
   CREATE DEFAULT statement
   CREATE INDEX
   CREATE PROCEDURE
   CREATE RULE
   CREATE TABLE statement
   CREATE TRIGGER
   CREATE VIEW
   DBCC
   DELETE
   DROP DATABASE statement
   DROP DEFAULT
   DROP INDEX
   DROP PROCEDURE
   DROP RULE
   DROP TABLE statement
   DROP TRIGGER
   DROP VIEW
   EXECUTE
   FROM clause
   GOTO
   GRANT
   GROUP BY clause
   IF...ELSE
   INSERT
   local variable
   RAISERROR
   READTEXT
   RETURN
   REVOKE
   ROLLBACK TRANSACTION
   SAVE TRANSACTION
   SELECT *
   select list
   SELECT
   SET
   sp_addtype
   sp_bindefault
   sp_bindrule
   sp_help
   sp_helpjoins
   sp_rename
   sp_spaceused
   sp_unbindrule
   subquery with comparison operator
   subquery with IN keyword
   subquery
   SUBSTRING function
   system function
   TRUNCATE TABLE
   UPDATE STATISTICS
   UPDATE
   USE statement
   USE
   WAITFOR
   WHERE
   WHILE
 sysdatabases table
 sysdevices table
 System Administrator permissions
 System database
 System functions
   COL_LENGTH
   COL_NAME
   DB_ID
   DB_NAME
   HOST_ID
   HOST_NAME
   INDEX_COL
   ISNULL
   OBJECT_ID
   OBJECT_NAME
   SUSER_ID
   SUSER_NAME
   USER_ID
   USER_NAME
 System procedures
   configuration option
   current users
   data definition
   database objects
   database options
   dump devices
   locks
   permissions
   sp_addtype
   sp_adduser
   sp_bindefault
   sp_bindrule
   sp_changedbowner
   sp_commonkey
   sp_configure
   sp_defaultdb
   sp_depends
   sp_droptype
   sp_help
   sp_helpdb
   sp_helpindex
   sp_helpjoins
   sp_helptext
   sp_rename
   sp_spaceused
   sp_unbindefault
   sp_unbindrule
   user identification
 System table
 System tables
   sysdatabases
   sysdevices
   sysusages
 sysusages table

     T
 Table
   # in name
   add column
   alias in correlated subquery
   alias
   base
   Cartesian product
   change existing
   create in different database
   create temporary
   create
   delete
   join multiple
   name in subquery
   name
   naming convention
   owner permissions
   rename
   sample design
   space used
   specify
   steps to create
   temporary
 TAN mathematical function
 tempdb database
 Temporary table
 Temporary
   database
   table
 Text functions
   PATINDEX
   TEXTPTR
   TEXTSIZE
   TEXTVALID
 text
   change value
   datatype
   function
   select value
 TEXTPTR function
 TEXTSIZE function
 TEXTVALID function
 Theta join
 TIME option
 Time
   default display
   format
 timestamp datatype
 tinyint datatype
 Total
 TRANSACT-SQL
   SQL enhancement
 Transaction
   backup log
   log
   process
 Trigger
   computed value
   create
   delete
   display text
   example
   list references
   memory use
   null value
   permissions
   rename
   report
   restrictions
   store
   summary value
   with correlated subquery
   with SET statement
 TRUNCATE TABLE statement
   permissions

     U
 Unbind
   default
   rule
 UNIQUE keyword
 UPDATE statement
   with nested subquery
 UPDATE STATISTICS statement
 UPPER string function
 USE statement
 User
   add
   current
   database
   guest
   identification
 USER_ID system function
 USER_NAME system function

     V
 Value
   summary
   unknown (null)
 VALUES keyword
 varbinary datatype
 varchar datatype
 varchar
   datatype conversion
   datatype
 Variable
   global. See Global variable
   local
 Vector aggregate
   nest
 View
   advantage of
   and built-in function
   and keys
   and SELECT statement
   as security mechanism
   change underlying objects
   COMPUTE BY in definition
   computed columns in definition
   create
   delete
   dependent
   derive from another view
   drop underlying objects
   GROUP BY in definition
   help
   name
   null value in underlying object
   objects referenced
   permissions
   projection
   query
   redefine
   rename column
   rename
   resolution
   restrictions
   use to retrieve data
   with computed column
   with join

     W
 WAITFOR statement
 WHERE clause
   and GROUP BY clause
   compared to HAVING clause
   in join
   in subquery
   with DELETE
 WHILE keyword
 WHILE loops, nest
 Wildcard character
   in search
   in stored procedure
 WITH RECOMPILE keyword
   in EXECUTE statement
   in stored procedure
 WRITETEXT statement

