Professional Documents
Culture Documents
Murach's SQL Server 2019 For Developers Isbn139781943872572
Murach's SQL Server 2019 For Developers Isbn139781943872572
DESIGN DATABASES
LIKE A DBA
and implement them with SQL
statements or the Management Studio
Bryan Syverson
Joel Murach
TRAINING & REFERENCE
murach's
S L Server
2019
for developers
Bryan Syverson
Joel Murach
1098765432 1
ISBN-13: 978-1-943872-57-2
Content
Introduction xvii
Reference aids
Appendix A How to set up your computer for this book 647
Index 659
Expanded contents Vii
Expanded contents
Section 1 An introduction to SQL
Chapter 1 An introduction to relational databases and SQL
--------------------
An introduction to client/server systems ......................................... 4
The hardware components of a client/server system ......................................... ............ .4
The software components of a client/server system ........................................................ 6
Other clie nt/server system architectures ......................................................................... 8
An introduction to the relational database model......................... 10
How a database table is organized ................................................................................ 10
How the tables in a relational database are related ....................................................... 12
How the columns in a table are defined ........................................................................ 14
How relational databases compare to other data models .............................................. 16
An introduction to SOL and SOL-based systems ......................... 18
A brief history of SQL .................................................................................................. 18
A comparison of Oracle, DB2, MySQL, and SQL Server ............................................ 20
The Transact-SOL statements ......................................................... 22
An introduction to the SQL statements ......................................................................... 22
Typical statements for working with database objects .................................................. 24
How to query a single table ........................................................................................... 26
How to j oin data from two or more tables .................................................................... 28
How to add, update, and delete data in a table .............................................................. 30
SQL coding guidelines .................................................................................................. 32
How to work with other database objects ...................................... 34
How to work with views ............................................................................................... 34
How to work with stored procedures, triggers, and user-defined functions .................. 36
How to use SOL from an application program .............................. 38
Common data access models ......................... .................................... ................ ........... 38
How to use ADO.NET from a .NET application ......................................................... .40
Visual Basic code that retrieves data from a SQL Server database ............................... 42
C# code that retrieves data from a SQL Server database .............................................. 44
How to work with the fixed database roles ...................................... ........................... 564
How to work with user-defined database roles ........................................................... 566
How to display information about database roles and role members ......................... 568
How to deny permissions granted by role membership .................... 570 00 . . 00 00 00 . . . . . . . . . . 00 . . 00
• Like all our books, this one includes hundreds of examples that range from
the simple to the complex. That way, you can quickly get the idea of how a
feature works from the simple examples, but you'll also see how the feature
is used in the real world from the complex examples.
• Like most of our books, this one has exercises at the end of each chapter
that give you hands-on experience by letting you practice what you' ve
learned. These exercises also encourage you to experiment and to apply
what you've learned in new ways.
• If you page through this book, you'll see that all of the information is
presented in "paired pages," with the essential syntax, guidelines, and
examples on the right page and the perspective and extra explanation on
the left page. This helps you learn more with less reading, and it is the ideal
reference format when you need to refresh your memory about how to do
something.
Prerequisites
Although you will progress through this book more quickly if you have
some development experience, everything you need to know about databases
and SQL is presented in this book. As a result, you don' t need to have any
programming background to use this book to learn SQL.
Introduction XiX
However, if you want to use C# or Visual Basic to work with a SQL Server
database as described in chapter 19, you need to have some experience using
C# or Visual Basic to write ADO.NET code. If you don't already have that
experience, you can refer to the appropriate chapter in the current edition of
Murach's C#or Murach's Visual Basic.
~~
Joel Murach
Author
[email protected]
Section 1
An introduction to SQL
Before you begin to learn the fundamentals of programming in SQL, you
need to understand the concepts and terms related to SQL and relational
databases. That's what you'lllearn in chapter 1. Then, in chapter 2, you' ll
learn about some of the tools you can use to work with a SQL Server
database. That will prepare you for using the skills you'llleam in the rest
of this book.
1
An introduction
to relational databases
and SQL
Before you can use SQL to work with a SQL Server database, you need to
be familiar with the concepts and terms that apply to database systems. In
particular, you need to understand what a relational database is. That's what
you' lllearn in the first part of this chapter. Then, you 'lllearn about some of the
basic SQL statements and features provided by SQL Server.
An introduction
to client/server systems
In case you aren' t familiar with client/server systems, the first two topics that
follow introduce you to their essential hardware and software components. These
are the types of systems that you're most likely to use SQL with. Then, the last
topic gives you an idea of how complex client/server systems can be.
~-
Database Server
D I Network
D I
Client Client
D I
Client
Q _
Client
. -- -
SQ-L-qu-
e-rie-s - - -1.
Results
I.
Database Server
Application software Database management system
Data access API Database
Server software
• To store and manage the databases of the client/server system, each server requires
a database management system (DBMS) like Microsoft SQL Server.
• The processing that's done by the DBMS is typically referred to as back-end
processing, and the database server is referred to as the back end.
Client software
• The application software does the work that the user wants to do. This type of
software can be purchased or developed.
• The data access API (application programming interface) provides the interface
between the application program and the DBMS. The current Microsoft API is
ADO.NET, which can communicate directly with SQL Server. Older APis required
a data access model, such as ADO or DAO, plus a driver, such as OLE DB or
ODBC.
• The processing that's done by the client software is typically referred to as
front-end processing, and the client is typically referred to as the f ront end.
~ SQ~ ~
D I
User request
Response
I
~
L__j
queries
Results
I
Client Application Server Database Server
User interface Business components DBMS
Database
a
A simple web-based system
Description
• In addition to a database server and clients, a client/server system can include
additional servers, such as application servers and web servers.
• Application servers are typically used to store business components that do part
of the processing of the application. In particular, these components are used to
process database requests from the user interface running on the client.
• Web servers are typically used to store web applications and web services. Web
applications are applications that are designed to run on a web server. Web services
are like business components, except that, like web applications, they are designed
to run on a web server.
• In a web-based system, a web browser running on a client sends a request to a web
server over the Internet. Then, the web server processes the request and passes any
requests for data on to the database server.
• More complex system architectures can include two or more application servers,
web servers, and database servers.
An introduction
to the relational database model
In 1970, Dr. E. F. Codd developed a model for a new type of database called
a relational database. This type of database eliminated some of the problems
that were associated with standard files and other database designs. By using the
relational model, you can reduce data redundancy, which saves disk storage and
leads to efficient data retrieval. You can also view and manipulate data in a way
that is both intuitive and efficient. Today, relational databases are the de facto
standard for database applications.
I ~ I ~
1
VendoriD
r·;··. . . . . . . . . . . ]
~............... ,,,,,,,,,,,,,,i
VendorName
US Postal Service
VendorAddress1
Attn: Supt. Window Services
VendorAddress2
PO Box 7005
VendorOty
Madison
" -
2 2 Nationl!l Information Dati! Qr PO Box 96621 NULL Wl!shington
3 3 Register al Copyrights Library Of Congress NULL Washington
4 4 Jo~k 1990 Westwood Blvd Ste 260 NULL Los Nlgeles
5 5 Newbrige Book Oubs 3000 Cndel Drive NULL Washington
6 6 CaiWomil! Oll!mber Of Commerce 3255 Rllmos Or NULL Sl!cn!mento
7 7 Towne Advertiser's Ml!iling Svcs Kevin M1nder 3441 W Ml!cl!rthur Blvd Sl!fltahll!
8 8 BFl Industries PO Box 9369 NULL Fresno
9 9 Pi!cif'IC Gl!s & Bectrlc Box 52001 NULL Sl!n Frl!Odsc
r-- Rows
10 10 Robbins Mobile Lock h1d Key 4669 N Fresno NULL Fresno
11 11 Bill Mllrvin Bectric Inc 4583 E Home NULL Fresno
12 12 City Of Fresno PO Box 2069 NULL Fresno
13 13 Golden Eagle lnsun~nce Co PO Box 85826 NULL Sl!n Diego
14 14 Expedatl! Inc 4420 N. Rrst Street. SUte 108 NULL Fresno
15 15 ASC Signs 1528 N Siem~ Vistll NULL Fresno
..,
<
16 16 lnleml!l Revenue Service NULL NULL Fresno
> -
Concepts
• A relational database consists of tables. Tables consist of rows and columns, which
can also be referred to as records and .fields.
• A table is typically modeled after a real-world entity, such as an invoice or a
vendor.
• A column represents some attribute of the entity, such as the amount of an invoice
or a vendor's address.
• A row contains a set of values for a single instance of the entity, such as one invoice
or one vendor.
• The intersection of a row and a column is sometimes called a cell. A cell stores a
single value.
• Most tables have a primary key that uniquely identifies each row in the table. The
primary key is usually a single column, but it can also consist of two or more
columns. If a primary key uses two or more columns, it's called a composite
primary key.
• In addition to primary keys, some database management systems let you define one
or more non-primary keys. In SQL Server, these keys are called unique keys. Like a
primary key, a non-primary key uniquely identifies each row in the table.
• A table can also be defined with one or more indexes. An index provides an
efficient way to access data from a table based on the values in specific columns.
An index is automatically created for a table's primary and non-primary keys.
The relationship between the Vendors and Invoices tables in the database
Primary key
I VendaOty
VendoriD VendorName VendorAddress1 VendorAddress2
113 114 Postmaster Postage Due Tedridan 1900 E Street Fresno
"
114 115 Roadw~ Package System. Inc Dept La 21095 NULL Pasadena
115 116 State of Caliomia Employment Development D... PO Box 826276 Sacramento
116 117 Subu!ban Propane 2874 S Cheny Ave NULL Fresno
117 118 Unocal p 0. Box 860070 NULL Pasadena
118 119 Yesmed.lnc PO Box 2061 NULL Fresno
119 120 Dataforms/West 1617W. Shaw Avenue Suite F Fresno
120 121 Zytka Design 3467 W Shaw Ave #103 NULL Fresno
121 122 United Parcel Service p 0 . Box 505820 NULL Reno
,.,..., 123 Federal Express Corporation P.O. Box 1140 Dept A Memphis
< >
"'
Concepts
• The tables in a relational database are related to each other through their key
columns. For example, the VendoriD column is used to relate the Vendors and
Invoices tables above. The VendoriD column in the Invoices table is called a foreign
key because it identifies a related row in the Vendors table. A table may contain one
or more foreign keys.
• When you define a foreign key for a table in SQL Server, you can 't add rows to
the table with the foreign key unless there's a matching primary key in the related
table.
• The relationships between the tables in a database correspond to the relationships
between the entities they represent. The most common type of relationship is
a one-to-many relationship as illustrated by the Vendors and Invoices tables. A
table can also have a one-to-one relationship or a many-to-many relationship with
another table.
G -Jo.
" .Jil .Iii Ne.~Query .Iii .~
• q X ----------------
,0 ,o;., ,?,. cl(, OJ t.J ~
0 Ready
Description
• The data type that's assigned to a column determines the type and size of the
information that can be stored in the column.
• Each column definition also indicates whether or not it can contain null values. A
null value indicates that the value of the column is unknown.
• A column can also be defined with a default value. Then, that value is used if
another value isn't provided when a row is added to the table.
• A column can also be defined as an identity column. An identity column is a numeric
column whose value is generated automatically when a row is added to the table.
Description
• To work with any of the data models other than the relational database model, you must
know the physical structure of the data and the relationships between the files or tables.
• Because relationships are difficult to implement in a conventional file system, redundant
data is often stored in these types of files.
• The hierarchical database model provides only for one-to-many relationships, called
parent/child relationships.
• The network database model can accommodate any type of relationship.
• Conventional files, hierarchical databases, and network databases are all more efficient
than relational databases because they require fewer system resources. However, the
flexibility and ease of use of relational databases typically outweigh this inefficiency.
An introduction
to SQL and SQL-based systems
In the topics that follow, you'll learn how SQL and SQL-based database
management systems evolved. In addition, you'lllearn how some of the most
popular SQL-based systems compare.
Description
• SQL-92 initially provided for three levels of conformance: entry, intermediate, and
full . A transitional level was later added between the entry and intermediate levels.
• SQL: 1999 includes a core specification that defines the essential elements for conformance,
plus nine packages. Each package is designed to serve a specific market niche.
• Although SQL is a standard language, each vendor has its own SQL dialect, or
variant, that may include extensions to the standards. SQL Server's SQL dialect is
called Transact-SQL.
Description
• Oracle is typically used for large, mission-critical systems that run on one or more
Unix servers.
• DB2 is typically used for large, mission-critical systems that run on legacy IBM
mainframe systems using the z/OS or OS/390 operating system.
• MySQL is a popular open-source database that runs on all major operating systems
and is commonly used for web applications.
• SQL Server is typically used for small- to medium-sized systems that run on one or
more Windows servers. However, SQL Server 2017 and later are designed to run on
Linux as well as Windows.
Description
• The SQL statements can be divided into two categories: the data manipulation
language (DML) that lets you work with the data in the database and the data
definition language (DDL) that lets you work with the objects in the database.
• SQL programmers typically work with the DML statements, while database
administrators (DBAs) use the DDL statements.
Typical statements
for working with database objects
To give you an idea of how you use the DDL statements you saw in the
previous figure, figure 1-11 presents five examples. The first statement creates an
accounts payable database named AP. This is the database that's used in many of
the examples throughout this book.
The second statement creates the Invoices table you saw earlier in this
chapter. If you don't understand all of this code right now, don't worry. You' ll
learn how to code statements like this later in this book. For now, just realize that
this statement defines each column in the table, including its data type, whether
or not it allows null values, and its default value if it has one. In addition, it
identifies identity columns, primary key columns, and foreign key columns.
The third statement in this figure changes the Invoices table by adding a
column to it. Like the statement that created the table, this statement specifies all
the attributes of the new column. Then, the fourth statement deletes the column
that was just added.
The last statement creates an index on the Invoices table. In this case, the
index is for the VendoriD column, which is used frequently to access the table.
Notice the name that's given to this index. This follows the standard naming
conventions for indexes, which you '11 learn about in chapter 11 .
Chapter 1 An introduction to relational databases and SQL 25
Description
• The REFERENCES clause for a column indicates that the column contains a
foreign key, and it names the table and column that contains the primary key.
Because the Invoices table includes foreign keys to the Vendors and Terms tables,
these tables must be created before the Invoices table.
• Because default values are specified for the PaymentTotal and CreditTotai columns,
these values don't need to be specified when a row is added to the table.
• Because the PaymentDate column accepts nulls, a null value is assumed if a value
isn't specified for this column when a row is added to the table.
A SELECT statement that retrieves and sorts selected columns and rows
from the Invoices table
SELECT InvoiceNumber, InvoiceDate, InvoiceTota l,
PayrnentTotal , CreditTotal,
InvoiceTotal - PayrnentTotal - CreditTotal AS BalanceDue
FROM Invoices
WHERE InvoiceTotal - PayrnentTotal - CreditTotal > 0
ORDER BY InvoiceDate;
2
3
4
963253264
31361833
263253268
2021}01-18
2021}01-21
2021}01-21
52.25
579.42
59.97
0.00
0.00
0.00
0.00
0.00
0.00
52.25
579.42
59.97
L
5 263253270 2021}01-22 67.92 0.00 0.00 67.92
6 263253273 2021}01-22 30.75 0.00 0.00 30.75
~
Concepts
• You use the SELECT statement to retrieve selected columns and rows from a base
table. The result of a SELECT statement is a result table, or result set, like the one
shown above.
• A result set can include calculated values that are calculated from columns in the
table.
• The execution of a SELECT statement is commonly referred to as a query.
A SELECT statement that joins data from the Vendors and Invoices tables
SELECT VendorName, InvoiceNumber, InvoiceDate, InvoiceTotal
FROM Vendors INNER JOIN Invoices
ON Vendors.VendoriD = Invoices.VendoriD
WHERE InvoiceTotal >= 500
ORDER BY VendorName, InvoiceTotal DESC;
Concepts
• A j oin lets you combine data from two or more tables into a single result set.
• The most common type of join is an inner join. This type of join returns rows from
both tables only if their related columns match.
• An outer join returns rows from one table in the j oin even if the other table doesn't
contain a matching row.
A statement that deletes all paid invoices from the Invoices table
DELETE FROM Invoices
WHERE InvoiceTota1 - Pay.mentTota1 - CreditTota1 = 0;
Concepts
• You use the INSERT statement to add rows to a table.
• You use the UPDATE statement to change the values in one or more rows of a table
based on the condition you specify.
• You use the DELETE statement to delete one or more rows from a table based on
the condition you specify.
• The execution of an INSERT, UPDATE, or DELETE statement is often referred to
as an action query.
Warning
• Until you read chapter 7 and understand the effect that these statements can have on
the database, do not execute the statements shown above.
Coding recommendations
• Start each new clause on a new line.
• Break long clauses into multiple lines and indent continued lines.
• Capitalize the fi rst letter of each keyword and each word in column and table names.
• End each statement with a semicolon (;).
• Use comments only for portions of code that are difficult to understand.
Description
• Line breaks, white space, indentation, and capitalization have no effect on the
operation of a statement.
• Comments can be used to document what a statement does or what specific parts of
a statement do. They are not executed by the system.
Note
• Throughout this book, SQL keywords are capitalized so they're easier to identify.
However, it's not necessary or customary to capitalize SQL keywords in your own code.
How to work
with other database objects
In addition to the tables you've already learned about, relational databases
can contain other objects. In the two topics that follow, you'll be introduced
to four of those objects: views, stored procedures, triggers, and user-defined
functions. Then, in section 4, you'll learn more about how to code and use these
objects.
"""'
A SELECT statement that uses the VendorsMin view
SELECT * FROM VendorsMin
WHERE VendorState = 'CA'
ORDER BY VendorName ;
Description
• A view consists of a SELECT statement that's stored with the database. Because
views are stored as part of the database, they can be managed independently of the
applications that use them.
• A view behaves like a virtual table. Since you can code a view name anywhere
you'd code a table name, a view is sometimes called a viewed table.
• Views can be used to restrict the data that a user is allowed to access or to present
data in a form that's easy for the user to understand. In some databases, users may
be allowed to access data only through views.
The result set that's created when the stored procedure is executed
VendorName VendorState VendorPhone
1 [_·~.b..~i..·.~.~.~.·~.F.~~.~.~~~~~~.·.·~.·.·~ .·~ .·~ .·~ .·.·~.·] CA (559) 555-8300
2 American Express CA (800) 555-3344
3 ASC Signs CA NULL
4 Aztek label CA (714) 555-9000
5 Bertelsmann Industry Svcs. Inc CA (805) 555-0584
6 BFI Industries CA (559) 555-1551
7 Bift Jones CA NULL
8 BiD Marvin Bectric Inc CA (559) 555-5106
"""""' """
Concepts
• A stored procedure is one or more SQL statements that have been compiled and
stored with the database. A stored procedure can be started by application code on
the client.
• Stored procedures can improve database performance because the SQL statements
in each procedure are only compiled and optimized the first time they're executed.
By contrast, SQL statements that are sent from a client to the server have to be
compiled and optimized every time they're executed.
• In addition to SELECT statements, a stored procedure can contain other SQL
statements such as INSERT, UPDATE, and DELETE. It can also contain
control-of-flow language, which lets you perform conditional processing within the
stored procedure.
• A trigger is a special type of procedure that's executed when rows are inserted,
updated, or deleted from a table or when the definition of a database is changed.
Triggers are typically used to check the validity of the data in a row that's being
updated or added to a table.
• A user-defined function (UDF) is a special type of procedure that can return a value
or a table.
Figure 1-17 How to use stored procedures, triggers, and user-defined functions
38 Section 1 An introduction to SQL
Description
• To work with the data in a SQL Server database, an application uses a data access
model. For an application written in a .NET language such as C# or Visual Basic
that model is typically ADO.NET. For an application written in Java, that model is
typically JDBC (Java Database Connectivity).
• Each data access model defines a set of objects you can use to connect to and work
with a SQL Server database. For example, both of the models shown above include
a connection object that you can use to specify the information for connecting to a
database.
• Some of the data access models require additional software, called drivers, to
communicate with SQL Server. For example, JDBC requires a Java driver.
• ADO.NET, a data access model developed by Microsoft, includes its own driver so
it can communicate directly with SQL Server.
• In addition to working with ADO.NET and JDBC directly, you can use an object-
relational mapping (ORM) framework. An ORM framework works by mapping the
data in a relational database to the objects used by an object-oriented programming
language. Then, the ORM translates requests to retrieve, insert, update, and delete
data so the DBMS will understand them.
• The most popular ORM framework for use with .NET applications is Entity
Framework (EF), which was developed by Microsoft. The most popular ORM
framework for use with Java is Hibernate. Both of these frameworks are
open-source.
To illustrate how you use a data access model, figure 1-19 introduces you to
the basic ADO.NET objects that you use in a .NET application. Then, in the next
two figures, you' ll see some actual code that creates and uses these objects. Keep
in mind, though, that there's a lot more you need to know about ADO.NET than
what's presented here.
When you develop a .NET application, you can choose from several
languages, but the most popular are Visual Basic and C#. Although C# has
become more popular than Visual Basic over the past few years, the language
you choose is largely a matter of personal preference.
To access a database using the objects shown here, you execute command
objects. Then, a connection object is used to connect to the database, perform
the requested operation, and return the result. If you execute a command
that contains a SELECT statement, the result is a result set that contains the
rows you requested. Then, you can read the rows in the result set using a data
reader object. If you execute a command that contains an INSERT, UPDATE,
or DELETE statement, the result is a value that indicates if the operation was
successful.
Chapter 1 An introduction to relational databases and SQL 41
Data reader
i
Select command
1i
Connection
Database server
i
Insert, Delete, or
Update command
Description
• To work with the data in a SQL Server database from a .NET application, you can
use ADO.NET objects like the ones shown above.
• A .NET data provider provides the classes that let you create the objects that you
use to retrieve data from a database and to store data in a database.
• To retrieve data from a database, you execute a command object that contains a
SELECT statement. Then, the command object uses a connection object to connect
to the database and retrieve the data. You can then read the results one row at a time
using a data reader object.
• To insert, update, or delete data in a database, you execute a command object that
contains an INSERT, UPDATE, or DELETE statement. Then, the command object
uses a connection to connect to the database and update the data. You can then
check the value that's returned to determine if the operation was successful.
• After data is retrieved from a database or updated in a database, the connection is
closed and the resources used by the connection are released. This is referred to as
a disconnected data architecture.
1
Create the c onnec tion objec t
Dim connection As New SqlConnection()
connection.ConnectionString = " Data Source=localhost \ SqlExpress; " &
"Initial Catalog=AP;Integrated Security=True"
1
Create the command object and set the connection,
1
SELECT statement, and parame ter value
Dim sele ctCommand As New SqlCommand
selectCommand.Connection = connection
selectCommand.CommandText = "SELECT VendoriD, " &
" VendorName, VendorAddressl, VendorAddress2, " &
"VendorCity, VendorState, VendorZipCode " &
"FROM Vendors WHERE VendoriD • ~VendoriD"
selectCommand.Parameters.AddWithValue ( "@VendoriD", vendoriD )
1
Open the connection to the database
connection.Open ( )
1
Retrieve the row specified by the SELECT statement
1
and load it into the Vendor object
Dim reader As SqlDataReader = selectCommand.ExecuteReader
If reader . Read Then
vendor.Vendor iD = Cint(reader ( "VendoriD"))
vendor.VendorName = reader("VendorName" ) .ToString
vendor.VendorAddressl = reader ( "VendorAddressl" ) .ToString
vendor.VendorAddress2 = reader("VendorAddress2").ToString
vendor.VendorCity = reader("VendorCity" ) .ToString
vendor.VendorState = reader ( "VendorState").ToString
vendor.VendorZipCode = reader ( "VendorZipCode " ).ToString
Else
vendor = Nothing
End If
reader . Close()
1
Close the connection to the database
connection.Close()
Return vendor
End Function
Description
• To issue a SQL statement from a Visual Basic program, you can create ADO.NET
objects like the ones shown above.
• After you create the ADO.NET objects, you have to set the properties of those
objects that define how they work. For example, the ConnectionString property
of a connection object contains the information ADO.NET needs to connect to a
database.
Figure 1-20 Visual Basic code that retrieves data from a SOL Server database
44 Section 1 An introduction to SQL
return vendor;
}
Description
• To issue a SQL statement from a C# application, you can use ADO.NET objects
like the ones shown above.
Figure 1-21 C# code that retrieves data from a SOL Server database
46 Section 1 An introduction to SQL
Perspective
To help you understand how SQL is used from an application program,
this chapter has introduced you to the hardware and software components
of a client/server system. It has also described how relational databases are
organized and how you use some of the basic SQL statements to work with
the data in a relational database. With that as background, you're ready to start
using SQL Server. In the next chapter, then, you ' 11 learn how to use some of the
tools provided by SQL Server.
Terms
client primary key
server composite primary key
database server non-primary key
network unique key
client/server system index
local area network (LAN) foreign key
enterprise system one-to-many relationship
wide area network (WAN) one-to-one relationship
network operating system many-to-many relationship
database management system data type
(DBMS) null value
back end default value
application software identity column
data access API (application hierarchical database
programming interface) parent/child relationship
front end network database
SQL (Structured Query Language) object database
SQL query relational database management
query results system (RDBMS)
application server Oracle
web server DB2 (Database 2)
business component ANSI (American National Standards
web application Institute)
web service levels of conformance
web browser core specification
thin client package
relational database SQL dialect
table extension
row SQL variant
column Transact-SQL
record open-source database
field data manipulation language (DML)
cell data definition language (DDL)
Chapter 1 An introduction to relational databases and SQL 47
Description
• To work with a SQL Server database and the data it contains, you can use the SQL
Server 2019 tools described above.
• After you install SQL Server 2019, you can access the Management Studio from
the Start-7Microsoft SQL Server Tools 18 program group, and you can access
the Configuration Manager from the Start-7Microsoft SQL Server 2019 program
group. To start one of these tools, just select it from its group.
• To make either of these tools easier to access, you can pin it to the Start menu by
right-clicking on it and selecting Pin to Start. Then, it will appear as a tile in the
Start menu and you can click on it to start it.
~ SQL Server Configuration Manager (locaij Name State Start Mode Log On As Proc6s 10
Cl SQL Server Services
·a SOL Server (SQLEXPRESS) Runmng Automatoc NT Servoce\MSSQLSSQLEXPRESS 3068
,.[, SQL Server Network Configuration (32bit f.tj SQL Full-text Fiker Daemon Launc ... Running Manual NT Service\ MSSQLFDLauncherSS... 3800
> -~·SOL Native Client 11.0Configuration (321 11'.>.-.sQL S R · Se · (SQ R ·
Automatic NT Service\ ReportServerSSQLEXP ... 3024
.[ SOL Server Network Configuration li1V erver eportlng rv~ces "' unnlng
·~· SQL Native Client 11.0 Configuration ! SOL Server Agent (SQLEXPRfSS) Stopped Other (Boo~ Syste... NT AUTHORITY\NETWORKSERVI... 0
· · ~.!J SOL Server Browser Stopped Other (Boo~ Syste... NT AUTHORITY\LOCALSERVICE 0
> <
• +I ::J ID ut l 6
r-
~ SQL Server Configuration Manager (locaij Protocol Name Status
rJ
SQL Server Services
T"Shared Memory Enabled
,.[, SQL Server Network Configuration (32bit
.~. SQL Native Client 11.0 Configuration (321
v ,.[, SQL Server Network Configuration
.
T"Named Pipes
.. Disabled
I
Enable
Q.- Protocols for SOLEXPRESS Disable ~
.~. SQL Native Client 11.0 Configuration
Properti.._j
Help
< >
-
Enable selected protocol.
Description
• After you install SQL Server Express, the database server will start automatically
each time you start your PC by default
• To display the Configuration Manager, select Start-?Microsoft SQL Server
2019-?SQL Server 2019 Configuration Manager.
• To start or stop a service, select the service in the right pane, and use the buttons in
the toolbar to start or stop the service.
• To change the start mode for a service, right-click on the service in the right pane,
select the Properties command to display the properties for the service, select the
Service tab, and select the start mode you want from the Start Mode combo box.
• By default, remote connections are disabled for SQL Server 2019. To enable them,
expand the SQL Server Network Configuration node and select the Protocols node
for the server. Then, right-click on the protocol you want to enable and select the
Enable command.
An introduction
to the Management Studio
Once the SQL Server database engine is installed, configured, and running,
you can use the Management Studio to connect to an instance of the SQL Server
database engine. Then, you can use the Management Studio to work with the
SQL Server database engine as described throughout this chapter.
SQL Server
Database EnQne
~Ntllt.iij;l$i.1
~: Wndows AIAherlication
I ~
SQL Server
Oataba"' EnQne
[@ ;t;tJJ.~tJI(!Q:IJ$1
I ~ Help
Description
• When you start the Management Studio, it displays a dialog box that allows you to
specify the information that's needed to connect to the appropriate database server.
• To connect to a database server, you use the Server Name combo box to enter or
select a path that specifies the database server. You begin by entering the name of
the computer, followed by a backslash, followed by the name of the SQL Server
database server.
• To connect to the SQL Server Express database engine when it's running on
your PC, you can use the localhost keyword to specify your computer as the host
machine, and you can use "SqlExpress" to specify SQL Server Express as the
database engine.
• If you select Windows authentication, SQL Server will use the login name and
password that you use for your computer to verify that you are authorized to
connect to the database server.
• If you select SQL Server authentication, you'll need to enter an appropriate
login name and password. This type of authentication is typically used only with
non-Windows clients.
Object Explorer
Description
• The Management Studio is a graphical tool that you can use to work with the
objects in a SQL Server database.
• If the Object Explorer isn' t displayed, you can use the View menu to display it. You
can close this window by clicking the Close button at the top of the window.
• To navigate through the database objects displayed in the Object Explorer, click on
the plus (+) and minus (-) signs to the left of each node to expand or collapse the
node.
• To display a menu of commands for working with an object, right-click on the
object.
• If you want to automatically hide the Object Explorer, you can click on the
AutoHide button at the top of the Object Explorer. Then, you can display the
window by pointing to the Object Explorer tab that's displayed along the left side
of the Management Studio window.
Qatabases to attach:
( MDF Rle location Dotabaoe Name
c:::::J C:\f'rovam Rleo\Mcrosoft SOL Selver\MSSOL.. AP
< >
(yld ... fiemove
Progras
Ready
I
<
--1 OK
~-
Cancel
>
Description
• To attach a database, right-click on the Databases folder and select the Attach
command to display the Attach Databases dialog box shown above. Then, click
on the Add button and use the resulting dialog box to select the mdf file for the
database. This should add both the data file and the log file for the database to the
Database Details pane at the bottom of the dialog box. Finally, click OK to attach
the database.
• If you want to attach a database that doesn't have a log file, or if you want to create
a new log file for a database, you can remove the log file for the database before
you click the OK button. To do that, select the log file in the Database Details pane
and click the Remove button.
• To detach a database, right-click on its icon and select the Tasks-7Detach command
to display the Detach Database dialog box. If the Message column indicates that
there are active connections, you can usually select the Drop option to drop the
connections. Then, click on the OK button.
0 Copx-orjy backup
•
c...-
Server:
localhoii\SQLEXPRESS
CorMdion:
......,.,Vv-ne
v'f ',lew ccmed!on!l!!lOe!lies
r
'----·;;_"'.;i"'_
T.'i':·t:-t~t_"'#ttt_-_~ _:f#b_._~~_*·_~·_...,._~!ll-?3-~;l_i"'_t_..,.'!_·~·_=t_f.#r.t:_d_,il_i't'
..i-· ll.. .. .. _____ :____J =
The default directory for SQL Server 2019 database backups
C: \ Program Files\Microsoft SQL Server\ MSSQLlS.SQLEXPRESS \ MSSQL\Backup
Description
• To back up a database, right-click on the database and select the Tasks-7 Back Up
command to display the Back Up Database dialog box. For the purposes of this
book, the default settings are usually adequate for backing up the database. As a
result, you can usually click OK to back up the database.
• To restore a database, right-click on the database and select the
Tasks7 Restore7 Database command to display the Restore Database dialog box.
Then, click OK to restore the database. This replaces the current database with the
most recent backup of the database.
The Database Properties dialog box with the compatibility level options
ii Database Properties· AP 0 X
Select• -
"J; General
Fies
~- ~· ~: SOL_Lan1_Generai_CP 1_CI_AS
1- [!D Recovery model: Sinple
1- O>anoe Trackno
J; Pem111ion1 CompatJblity!eva: SOL Server 2019 (150)
" Eldended l'ropecties Cortannert type: None
J; Q.,e,y SIO<O
Qther options.
:: ~ I I
v Dol- Scaped Carl..,.._
legacy Catdinali:y Eotmatoon OFF
-
"
legacy Catdinoli:y Eotmatoon Fa< Secondary PRINARY
Max OOP 0
Max OOP Fa< Secondary
Parameler Sniflng ON
Parameler Snilf"ll Fot Secondary PRINARY
Conneclion Q.Jef)' Optmzer Ax.. OFF
Server Q.,e,y Optmzer Axet Fa< Secondary PRINARY
localhoot\SOLE.XPRESS v Al.ESTREAM
FILESTREAM Directory Name
------
FILESTREAM Non·TranMCted Acce11 Off
v lhc
v¥ 't1ew coooed!OO Q!POC!IIe! ,ljiowScnptng True
tideFie5e'tngo Fal,.
v _,_
,ljiow Snapshot looloboo Fal""
Pr-
AN~ NUll JJ.i.,.
Allow Snapshot lsolalion
Ready
OK Cancel
Description
• The SQL Server 2019 database engine is backwards compatible and can run older
versions of SQL Server databases just as if they were running under an older
version of the SQL Server database engine.
• To set the compatibility level for a database to SQL Server 2019 , right-click the
database, select the Properties command, click on the Options item, and select SQL
Server 2019 from the Compatibility Level drop-down list.
o· rJ •
- - - - - - - - -
Obje:Ct Explorer '"' ~ X MMA17\SQLEXPRESs.AP · Mamlables .g X
Connect'" f .,, G ...Jo..
EJ iS locathost\SQLE.XPRESS (SQl Serve:r Vendors Invoices
8 Databases V Ve:ndoriD ' lnvciceiO
Ill System D11hbues
Ve:ndorName VMdortO
1!1 Database Sm pshots
E1 ii AP VtndorAddrtssl lnvoi<:tNumber
13 Dat abase Diagrams VendorAddren2 InvoiceDate
tg dbo.MainTables
VendorCity InvoiceTotal
l!l Tables
III Vi~
VendorState Payment Total
liJ External Resources VtndorZipCode CrtditTotal
Ill Synonyms VendorPhone TmnsJO
1!J Prograrrmability
VendorContactLName lnvciceOueOate
ttl StrviceBroker
liJ Storage VendorContactFName PaymentOate
IE Stcurity OefauttTermsiO
l!l .. Security
OefauttAccountNo
1±1 • Strver Objects
IE .. Replication
!±I .. PolyBase
Terms
('!) • Management
(i) I!] XEvent Profiltr
- V TermsJD
Tmn.O<>eription
TmnsOu~ays
GLAccounts I
i AccountNo
AccountOescription
Description
• Database diagrams can be used to illustrate the relationships between the tables in
a database.
• To create a database diagram, right-click on the Database Diagrams node and select
New Database Diagram to display the Add Table dialog box. If you get a dialog
box that says that the database doesn' t have one or more of the required support
objects for database diagramming, you can select Yes to create the support objects.
Then, select each table you want to add from the list that's displayed, and click the
Add button. When you save the diagram, you'll be asked to enter a name for it.
• To view an existing database diagram, expand the Database Diagrams node for the
database, and double-click on the diagram you want to display.
• The relationships between the tables in the diagram appear as links, where the
endpoints of the links indicate the type of relationship. A key indicates the "one"
side of a relationship, and the infinity symbol (oo) indicates the "many" side of a
relationship.
• The primary key for a table appears as a key icon that's displayed to the left of the
column or columns that define the primary key.
• You can use standard Windows techniques to move and size the tables in a database
diagram to make the diagram easier to understand.
0 · ,:J • " II' .~ NowOu•ry .lSI ~ ~ ,o:i'l ,'I/1 .)(, OJ ~ p 00:00:00 • 10) I' :i: ~
I Object Explorer
0 Rudy
Description
• To view the columns for a table, expand the Tables node, expand the node for
the table, and expand the Columns node. This displays the columns in the Object
Explorer.
• To modify the columns for a table, expand the Tables node, right-click on the
table, and select the Design command to display the table in a Table Designer tab.
Then, you can click on the row selector to the left of the column name to display
the properties for the column in the Column Properties tab at the bottom of the
window. If necessary, you can use the Table Designer tab or the Column Properties
tab to modify the properties for a column.
10
Databases 20
(jJ System Ootoboses Net due 30 doys 30
1iJ Database Snapshots
Net due 60 doys 60
8 i AP
Net due 80 days 9 80 9
m Database Diagrams
8 Tobles NULL NULL
1!J System Tobles
IE FileTobles
Iii External Tables
1!J Graph Tables
IE 11!1 dbo.ContoctUpdotes
IE 11!1 dbo.GlAccounts
IE 11!1 dbo.lnvoiceArchive
Ill e dbo.lnvoicelineltems
IE 11!1 dbo.lnvoices
IE 11!1 dbo.Terms
IE 11!1 dbo.Vendors
w Views
IE External Resources
IE Synonyms
IE Programmability
IE Strv~ce Broker
w Stor~ge
IE Security
IE Security ofS Cell is Read Only.
0 Ready
Description
• To view the data for a table, expand the Tables node, right-click on the table, and
select the Select Top 1000 Rows command. This automatically generates and
executes a query that displays the top 1000 rows of the table.
• To modify the data for a table, expand the Tables node, right-click on the table, and
select the Edit Top 200 Rows command. This displays the top 200 rows of the table
in an editable grid. Then, you can use the grid to insert, update, and delete data
from the table.
Description
• To open a new Query Editor window, click on the New Query button in the toolbar.
To open a saved query in a Query Editor window, click on the Open button in the
toolbar as described in figure 2-1 3.
• To select the database that you want to work with, use the Available Databases
combo box in the toolbar.
• To enter a SQL statement, type it into the Query Editor window.
• As you enter a SQL statement, the IntelliSense feature automatically displays
completion lists that help you complete the statement. To select an item from a list,
use the Up or Down arrow key to select the item and press the Tab key. To hide a
list, press the Esc key. To manually display a list, press Alt+Right-arrow or Ctrl+J.
• To execute a SQL statement, press the F5 key or click the Execute button in the
toolbar. If the statement retrieves data, the data is displayed in the Results tab that's
displayed at the bottom of the Query Editor. Otherwise, a message is displayed in
the Messages tab that's displayed at the bottom of the Query Editor.
tab. If you execute an action query, the Messages tab is displayed instead of the
Results tab. This tab will contain an indication of the number of rows that were
affected by the query. The Messages tab is also used to provide error information,
as you'll see in figure 2-12.
Obj«l Explom • Q X
SELECT ~. Invoie<Oat< , Invoie<Total, Payar<ntTotal, Cr<ditTotal,
Conn<ct • f •y C:, ..Jo
Invoic~Total - PaymentTotal - Cr~ditTota l AS Balanc~Oue!
E1 id localhost\SQLEXPRESS (SQl S•rv•r 1S.O. " FR<»'l Invoic~s
IE !Ill dbo.V<ndors
IE Viev.s
w External Resources
ltJ Synonyms
Gl Programmability
S.Mco Broker 100% •
m Stonae
1 Query compl<t<d woth <rro... localhost\SQLEXPRESS(lS.O ... murach\Ann<(S7) AP 00:00:00 Crows
Description
• Before you execute a statement, IntelliSense may display wavy red underlining
beneath the parts of a SQL statement that contain errors.
• If an en or occurs during the execution of a SQL statement, the Management Studio
displays an error message in the Messages tab of the Query Editor.
• Most errors are caused by incorrect syntax and can be detected and corrected
without any additional assistance. If not, you can get more information about an
error by looking it up in the SQL Server Reference.
·~; AP
Object Explorer • Q X
SELECT Invoict:Number, InvoiceDate, InvoiceTotal., PaymentTotal , CreditTotal,
Connect · f '¥ C:, ..Jo
InvoiceTotal - PaytM!ntTotal - Cr!:ditTotal AS BalanceDue
EJ id localhost\ SQlEXPRESS (SQL Server 15.0. " FR<»'l Invoices
8 Databases !.tiERE InvoiceTotol CreditTotol 0
[jJ System Databases
X
(f) Databas! Snapshots
8 ii AP v 1' « Scripts > Chapter 02 v ~ Search Chapter 02
[jJ
... Downloads
)I MuSic
IE 1.::1 Pictures
IE • Videos
ltJ _ local DISk (C:)
IE .. <
Description
• To save a query, click the Save button in the toolbar or press Ctrl+S. Then, if necessary,
use the Save File As dialog box to specify a file name for the query.
• To open a query, click the Open button in the toolbar or press Ctrl+O. Then, use the
Open File dialog box shown above to locate and open the query.
• To save all open queries, click the Save All button in the toolbar. Then, if necessary,
use the Save File As dialog box to specify a file name for any queries that haven' t
already been named.
• To switch between open queries, click on the tab for the query you want to display.
If you can' t see the tab, click on the drop-down arrow that's displayed to the right
of the Query Editor tabs, and select the query from the list of active files.
• To cut, copy, and paste code from one query to another, use the standard Windows
techniques.
Description
• You can use the Query Designer to build simple queries quickly and easily.
However, you may not be able to create more complex queries this way.
• To display the Query Designer, right-click on a blank Query Editor window and
select the Design Query in Editor command. Then, you can use the Query Designer
window to create the query. When you click the OK button, the query will be
inserted into the Query Editor where you can edit and run it just as you would any
other query.
• To modify a query with the Query Designer, select the query, right-click on the
selection, and select the Design Query in Editor command.
: Microsoft I SQL Docs Ov<t<V>WN v ln>t.l v Se<uJO v o......p v Mu<¥ v I Oov.J~O~ SOLs.""' I
l)ocs ~l Rtlt'f"<t orJnU<t·~L (I •SQU l!olt•tn<t au.nts
SELECT
:..qn '"
VerSion
SELECT (Transact-SQL) Is this page
helpful?
SQL So""' 2019
r" ~" ;;> No
y ~ol\01 1111 u.
APPLIES TO: 0 SQL ~rver 0 Azure SQL Database 0 Azure Synapse
) Oit4t~• In this ~tticle
Analyt!Cs (~QL DW) 0 Parallel O.ta Warehouse
> XML l synr..
>06CC ~~~~~• 10m hum th~ Udldbd>e dnll ~ndbl~ lh~ >~l~tion ol on~ 01 mdny Rom••k>
row; or column< from onr or m,1ny 1.1blr< in SOl Srrvrr Thr full o;ynt,,. of thr l "'.)<tAl
S(L(CT statement is complex. but the main clauses can~ summarized as· Pru<."""MJ
Orr1,.rcftr'•"
( WIIH ( ( XMLNAMt~PAC.t~ ,J (• common table expression • J} J SELECT
1U!fltTIHlt
SELECT
SFlfCT (IAJJ••
I l GROUP BY group_by_txprmion]
J. Oownlood I'Of Thr UNION FXCFPT and INTFRSFCT oprrators c.1n br u~ brrwrrn qurrir<
to combine or compare their results into one result set
Description
• To view the official documentation for SQL Server 2019, you can start a web
browser and search for "SQL Server technical documentation".
• The main page of the documentation displays a table of contents in the left column
and links to various topics related to SQL Server in the main area of the page.
• You can click on any link in the main area to display information about that topic.
• To use the table of contents, click on a topic to display it in the middle pane. You
can also filter the contents by title by entering text in the Filter by Title text box
above the table and contents and then selecting a title from the list that's displayed.
• To use full-text search, click the search icon in the upper right corner that looks like
a magnifying glass, enter the text into the Search text box, and press the Enter key.
Then, select the topic that you want from the resulting list of topics.
Perspective
In this chapter, you learned how to use the tools that you need to begin
learning about SQL. To begin, you learned how to start and stop the database
server. Then, you learned how to use the Management Studio to connect to
the database server, to attach a database, to view the definition of a database,
and to execute SQL statements against that database. Finally, you learned how
to view the official documentation for SQL Server. With that as background,
you're ready to go on to the next chapter where you'll start learning the details
of coding your own SQL statements.
Before you go on to the next chapter, though, I recommend that you install
SQL Server Express and the Management Studio on your system as described
in appendix A. In addition, I recommend that you download and install the
databases and sample code that come with this book as described in appendix
A. That way, you can begin experimenting with these tools. In particular, you
can enter and execute queries like the ones described in this chapter. Or, you
can open any of the queries shown in this chapter, view their code, and execute
them.
For now, though, focus on the mechanics of using the Management Studio
to enter and execute queries, and don' t worry if you don' t understand the
details of how the SQL statements are coded. You' lllearn the details for coding
SQL statements in the chapters that follow. In the next chapter, for example,
you' ll learn the details for coding a SELECT statement that retrieves data from
a single table.
Terms
database server
client tools
database engine
SQL Server 2019 Express Edition
SQL Server Express
SQL Server Management Studio
SQL Server Configuration Manager
schema
attach a database
detach a database
back up a database
restore a database
database diagram
Query Editor
IntelliSense feature
completion list
Query Designer
Chapter 2 How to use the Management Studio 81
1. Use the Management Studio to view all of the databases that are available
from the server. If the AP database isn't available, follow the procedure in
appendix A to create it. Then, view the tables that are available from the AP
database. Finally, view the columns that are available from the Invoices table.
Note the primary and foreign keys of this table and the definition for each
column.
2. Right-click on the Vendors table and select the Design command to display
the Vendors table in a Table Designer tab. Review the properties for each
column in this table. In particular, note that the VendoriD column is defined as
an identity column.
3. Use the Management Studio to create a database diagram for the AP database.
The diagram should include the Vendors, Invoices, InvoiceLineltems, Terms,
and GLAccounts tables. Save the diagram with any name you'd like.
4. Organize the tables and connecting lines in the diagram you created in step
3 so they are easy to read. (Hint: You can use the Autosize Selected Tables
button and the Arrange Tables button in the toolbar to help you do this.)
Finally, review the information that's contained in each table, note the primary
key of each table, and try to identify the relationships between the tables.
5. Open a new Query Editor window and then enter this SELECT statement:
SELECT Vendo r Name, Ve ndorSta te
FROM Vendo r s
WHERE VendorSta te = 'CA '
Press F5 to execute the query and display the results. If an error is displayed,
correct the problem before you continue. (Hint: If you get an error message
that indicates that 'Vendors' isn' t a valid object, the AP database isn't the
current database. To fix this error, select the AP database from the Available
Databases combo box.) Then, save the query with a name of VendorsinCA
and close it.
6. Open the query named VendorslnCA that you saved in exercise 5. Then, click
the Execute Query toolbar button to execute it. (Hint: You may need to select
the AP database from the Available Databases combo box.)
7. Look up information about the Query Editor in the SQL Server documenta-
tion. The easiest way to do this is to use the full-text search to look up "query
editor" . Continue experimenting with the documentation until you feel
comfortable with it.
Section 2
An introduction
to the SELECT statement
To help you learn to code SELECT statements, this chapter starts by
presenting its basic syntax. Next, it presents several examples that will give you
an idea of what you can do with this statement. Then, the rest of this chapter will
teach you the details of coding this statement.
Description
• You use the basic SELECT statement shown above to retrieve the columns
specified in the SELECT clause from the base table specified in the FROM clause
and store them in a result set.
• The WHERE clause is used to filter the rows in the base table so that only those
rows that match the search condition are included in the result set. If you omit the
WHERE clause, all of the rows in the base table are included.
• The search condition of a WHERE clause consists of one or more Boolean
expressions, or predicates, that result in a value of True, False, or Unknown. If the
combination of all the expressions is True, the row being tested is included in the
result set. Otherwise, it's not.
• If you include the ORDER BY clause, the rows in the result set are sorted in the
specified sequence. Otherwise, the rows are returned in the same order as they
appear in the base table. In most cases, that means that they' re returned in primary
key sequence.
Note
• The syntax shown above does not include all of the clauses of the SELECT
statement. You' ll learn about the other clauses later in this book.
A SELECT statement that retrieves all the data from the Invoices table
SELECT *
FROM Invoices;
lnvoiceiD VendoriD InvoiceNumber InvoiceDate InvoiceTotal Payment Total CredHotal
1 ~
[ f '.'.'.'.·.·.·.·.·.-.·.·.··.-... ..........] 122 989319-457 2019-10-08 3813.33 3813.33 0.00
"'
2 2 123 263253241 2019-1 ~1 0 40.20 40.20 0.00
123 2019-1 ~13 138.75 138.75
Ill
3 3 963253234 0.00
4 4 123 2.()()().2993 2019-1 ~1 6 144.70 144.70 0.00 'I
< >
(114 rows)
(114 rows)
(35 rows)
Description
• Use SELECT * only when you need to retrieve all of the columns from a table.
Otherwise, list the names of the columns you need.
• An expression is a combination of column names and operators that evaluate to a
single value. In the SELECT clause, you can code arithmetic expressions, string
expressions, and expressions that include one or more functions.
• After each column specification, you can code an AS clause to specify the name for
the column in the result set. See figure 3-4 for details.
Note
• The ALL and DISTINCT keywords and the TOP clause let you control the number
of rows that are returned by a query. See figures 3-8 and 3-9 for details.
Two SELECT statements that name the columns in the result set
A SELECT statement that uses the AS keyword (the preferred technique)
SELECT InvoiceNumber AS [Invoice Number], InvoiceDate AS Date,
InvoiceTotal AS Total
FROM Invoices;
A SELECT statement that uses the equal operator (an older technique)
SELECT [Invoice Number] = InvoiceNumber, Date = InvoiceDate,
Total = InvoiceTotal
FROM Invoices;
Description
• By default, a column in the result set is given the same name as the column in the
base table. If that's not what you want, you can specify a column alias or substitute
name for the column.
• One way to name a column is to use the AS phrase as shown in the first example
above. Although the AS keyword is optional, I recommend you code it for
readability.
• Another way to name a column is to code the name followed by an equal sign and
the column specification as shown in the second example above. This syntax is
unique to Transact-SQL.
• It's generally considered a good practice to specify an alias for a column that
contains a calculated value. If you don' t, no name is assigned to it as shown in the
third example above.
• If an alias includes spaces or special characters, you must enclose it in double
quotes or brackets([]). That's true of all names you use in Transact-SQL. SQL
Server also lets you enclose column aliases in single quotes for compatibility with
earlier releases.
VendorCity + 1 , 1
+ VendorState + 1 1
+ VendorZipCode
FROM Ve ndors ;
(No colullY'I name)
...............................................................................................................: (No coltn1n name)
1 [.Y?.. .~.~.~.~.~. ~~~:~. ~.~.~.:. . . . . . . . .. . . . . . . . . . ..J Madison. WI 53707
2 National Information Data Or's Address: Washington. DC 20090
3 Register of Copyrigl'ts's Address: Washington. DC 20559
4 Jobtrak's Address: Los lv1geles. CA 90025
5 Newbrige Book Clubs's Address: Washington. NJ 07882
6 Califomia 01amber Of Commerce's Address: Sacramento. CA 95827
Description
• A string expression can consist of one or more character columns, one or more
literal values, or a combination of character columns and literal values.
• The columns specified in a string expression must contain string data (that means
they're defined with a data type such as char or varchar).
• The literal values in a string expression also contain string data, so they can be
called string literals or string constants. To create a literal value, enclose one or
more characters within single quotation marks (').
• You can use the concatenation operator (+) to combine columns and literals in a
string expression.
• You can include a single quote within a literal value by coding two single quotation
marks as shown in the third example above.
Description
• Unless parentheses are used, the operations in an expression take place from left to
right in the order of precedence. For arithmetic expressions, multiplication, division,
and modulo operations are done first, followed by addition and subtraction.
• Whenever necessary, you can use parentheses to clarify or override the sequence of
operations. Then, the operations in the innermost sets of parentheses are done first,
followed by the operations in the next sets, and so on.
Description
• An expression can include any of the functions that are supported by SQL Server. A
function performs an operation and returns a value.
• A function consists of the function name, followed by a set of parentheses that
contains any parameters, or arguments, required by the function. If a function
requires two or more arguments, you separate them with commas.
• For more information on using functions, see chapter 9.
(122 rows)
(53 rows)
Description
• The DISTINCT keyword prevents duplicate (identical) rows from being included in
the result set. It also causes the result set to be sorted by its first column.
• The ALL keyword causes all rows matching the search condition to be included in
the result set, regardless of whether rows are duplicated. Since this is the default,
it's a common practice to omit the ALL keyword.
• To use the DISTINCT or ALL keyword, code it immediately after the SELECT
keyword as shown above.
Figure 3-8 How to use the DISTINCT keyword to eliminate duplicate rows
102 Section 2 The essential SQL skills
A SELECT statement with a TOP clause and the WITH TIES keyword
SELECT TOP 5 WITH TIES VendoriD, Inv oiceDate
FROM Invoice s
ORDER BY Invoi ceDate ASC;
VendoriO Invoice Date
1 [5~.·~~.·~~.·~~.·~~.·~~] 2019-1 ~
2 123 2019-10.10
3 123 2019-10.13
4 123 2019-10.16
5 123 2019-10.16
6 123 2019-10.16
Description
• You can use the TOP clause within a SELECT clause to limit the number of rows
included in the result set. When you use this clause, the first n rows that meet the
search condition are included, where n is an integer.
• If you include PERCENT, the first n percent of the selected rows are included in
the result set.
• If you include WITH TIES, additional rows will be included if their values match,
or tie, the values of the last row.
• You should include an ORDER BY clause whenever you use the TOP keyword.
Otherwise, the rows in the result set will be in no particular sequence.
Figure 3-9 How to use the TOP clause to return a subset of selected rows
104 Section 2 The essential SQL skills
Description
• You can use a comparison operator to compare any two expressions that result in
like data types. Although unlike data types may be converted to data types that can
be compared, the comparison may produce unexpected results.
• If a comparison results in a True value, the row being tested is included in the result
set. If it's False or Unknown, the row isn' t included.
• To use a string literal or a date literal in a comparison, enclose it in quotes. To use a
numeric literal, enter the number without quotes.
• Character comparisons performed on SQL Server databases are not case-sensitive.
So, for example, 'CA' and 'Ca' are considered equivalent.
(34 rows)
'
(11 rows)
Description
• You can use the AND and OR logical operators to create compound conditions that
consist of two or more conditions. You use the AND operator to specify that the
search must satisfy both of the conditions, and you use the OR operator to specify
that the search must satisfy at least one of the conditions.
• You can use the NOT operator to negate a condition. Because this operator can
make the search condition difficult to read, you should rephrase the condition if
possible so it doesn't use NOT.
• When SQL Server evaluates a compound condition, it evaluates the operators in this
sequence: (1) NOT, (2) AND, and (3) OR. You can use parentheses to override this
order of precedence or to clarify the sequence in which the operations will be evaluated.
Figure 3-11 How to use the AND, OR, and NOT logical operators
108 Section 2 The essential SQL skills
Description
• You can use the IN phrase to test whether an expression is equal to a value in a list
of expressions. Each of the expressions in the list must evaluate to the same type of
data as the test expression.
• The list of expressions can be coded in any order without affecting the order of the
rows in the result set.
• You can use the NOT operator to test for an expression that's not in the list of
expressions.
• You can also compare the test expression to the items in a list returned by a subquery
as illustrated by the third example above. You'll learn more about coding subqueries
in chapter 6.
Figure 3-13 shows how to use the BETWEEN operator in a WHERE clause.
When you use this operator, the value of a test expression is compared to the
range of values specified in the BETWEEN phrase. If the value falls within this
range, the row is included in the query results.
The first example in this figure shows a simple WHERE clause that uses the
BETWEEN operator. It retrieves invoices with invoice dates between January
1, 2020 and January 31, 2020. Note that the range is inclusive, so invoices with
invoice dates of January 1 and January 31 are included in the results.
The second example shows how to use the NOT operator to select rows
that are not within a given range. In this case, vendors with zip codes that aren't
between 93600 and 93799 are included in the results.
The third example shows how you can use a calculated value in the test
expression. Here, the PaymentTotal and CreditTotal columns are subtracted from
the InvoiceTotal column to give the balance due. Then, this value is compared to
the range specified in the BETWEEN phrase.
The last example shows how you can use calculated values in the
BETWEEN phrase. Here, the first value is the result of the GETDATE function,
and the second value is the result of the GETDATE function plus 30 days. So the
query results will include all those invoices that are due between the current date
and 30 days from the current date.
Chapter 3 How to retrieve data from a single table 111
Description
• You can use the BETWEEN phrase to test whether an expression falls within a
range of values. The lower limit must be coded as the first expression, and the
upper limit must be coded as the second expression. Otherwise, the result set will
be empty.
• The two expressions used in the BETWEEN phrase for the range of values are
inclusive. That is, the result set will include values that are equal to the upper or
lower limit.
• You can use the NOT operator to test for an expression that's not within the given
range.
One final operator you can use in a search condition is the LIKE operator
shown in figure 3-14. You use this operator along with the wildcards shown at
the top of this figure to specify a string pattern, or mask, you want to match. The
examples shown in this figure illustrate how this works.
In the first example, the LIKE phrase specifies that all vendors in cities that
start with the letters SAN should be included in the query results. Here, the
percent sign (%) indicates that any characters can follow these three letters. So
San Diego and Santa Ana are both included in the results.
The second example selects all vendors whose vendor name starts with the
letters COMPU, followed by any one character, the letters ER, and any
characters after that. Two vendor names that match that pattern are Compuserve
and Computerworld.
The third example searches the values in the VendorContactLName column
for a name that can be spelled two different ways: Damien or Damion. To do
that, the mask specifies the two possible characters in the fifth position, E and 0 ,
within brackets.
The fourth example uses brackets to specify a range of values. In this case,
the VendorS tate column is searched for values that start with the letter N and end
with any letter from A to J. That excludes states like Nevada (NV) and New York
(NY).
The fifth example shows how to use the caret(") to exclude one or
more characters from the pattern. Here, the pattern says that the value in the
VendorState column must start with the letter N, but must not end with the letters
K through Y. This produces the same result as the previous statement.
The last example in this figure shows how to use the NOT operator with a
LIKE phrase. The condition in this example tests the VendorZipCode column for
values that don' t start with the numbers 1 through 9. The result is all zip codes
that start with the number 0.
The LIKE operator provides a powerful technique for finding information in
a database that can't be found using any other technique. Keep in mind, however,
that this technique requires a lot of overhead, so it can reduce system
performance. For this reason, you should avoid using the LIKE operator in
production SQL code whenever possible.
If you need to search the text that's stored in your database, a better option
is to use the Full-Text Search feature that's provided by SQL Server. This feature
provides more powerful and flexible ways to search for text, and it performs
more efficiently than the LIKE operator. However, Full-Text Search is an
advanced feature that requires some setup and administration and is too complex
to explain here. For more information, you can look up "full-text search" in the
SQL Server documentation.
Chapter 3 How to retrieve data from a single table 113
Wildcard symbols
Symbol Description
% Matches any string of zero or more characters.
Matches any single character.
[ 1 Matches a single character listed within the brackets.
- 1 Matches a single character within the given range.
[ " 1 Matches a single character not listed after the caret.
Description
• You use the LIKE operator to retrieve rows that match a string pattern, called
a mask. Within the mask, you can use special characters, called wildcards, that
determine which values in the column satisfy the condition.
• You can use the NOT keyword before the LIKE keyword. Then, only those rows
with values that don' t match the string pattern will be included in the result set.
• Most LIKE phrases will significantly degrade performance compared to other types
of searches, so use them only when necessary.
empty string ( " ). Instead, a null value indicates that the data is not applicable,
not available, or unknown. When you allow null values in one or more columns,
you need to know how to test for them in search conditions. To do that, you can
use the IS NULL clause as shown in figure 3-15.
This figure uses a table named NullSample to illustrate how to search for
null values. This table contains two columns. The first column, InvoiceiD, is
an identity column. The second column, InvoiceTotal, contains the total for
the invoice, which can be a null value. As you can see in the first example, the
invoice with InvoiceiD 3 contains a null value.
The second example in this figure shows what happens when you retrieve
all the invoices with invoice totals equal to zero. Notice that the row that has a
null invoice total isn' t included in the result set. Likewise, it isn't included in the
result set that contains all the invoices with invoices totals that aren't equal to
zero, as illustrated by the third example. Instead, you have to use the IS NULL
clause to retrieve rows with null values, as shown in the fourth example.
You can also use the NOT operator with the IS NULL clause as illustrated
in the last example in this figure. When you use this operator, all of the rows that
don' t contain null values are included in the query results.
Chapter 3 How to retrieve data from a single table 115
2 2 0.00
3 3 NULL
4 4 2199.99
5 5 0.00
= =
Description
• A null value represents a value that's unknown, unavailable, or not applicable. It
isn' t the same as a zero, a blank space(' '), or an empty string(").
• To test for a null value, you can use the IS NULL clause. You can also use the NOT
keyword with this clause to test for values that aren't null.
• The definition of each column in a table indicates whether or not it can store null
values. Before you work with a table, you should identify those columns that allow
null values so you can accommodate them in your queries.
1
2 Computer l.Jbrary Phoenix. AZ. 85023
3 Wefts FarQo Bank Phoenix. AZ. 85038
4 Aztek Label Anaheim, CA 92807
5 Blue Shield of California Anaheim. CA 92850
6 Diversified Printin9 & Pub Brea. CA 92621
7 Abbey Office FumishinQs Fresno. CA 93722
8 ASC SiQns Fresno. CA 93703
9 BFIIndustries Fresno. CA 93792
Description
• The ORDER BY clause specifies how you want the rows in the result set sorted.
You can sort by one or more columns, and you can sort each column in either
ascending (ASC) or descending (DESC) sequence. ASC is the default.
• By default, in an ascending sort, nu11s appear first in the sort sequence, fo11owed
by special characters, then numbers, then letters. Although you can change this
sequence, that's beyond the scope of this book.
• You can sort by any column in the base table regardless of whether it's included
in the SELECT clause. The exception is if the query includes the DISTINCT
keyword. Then, you can only sort by columns included in the SELECT clause.
Figure 3-1 6 How to sort a result set by a column na me
118 Section 2 The essential SQL skills
Description
• The ORDER BY clause can include a column alias that's specified in the SELECT
clause.
• The ORDER BY clause can include any valid expression. The expression can refer
to any column in the base table, even if it isn't included in the result set.
• The ORDER BY clause can use numbers to specify the columns to use for sorting.
In that case, 1 represents the first column in the result set, 2 represents the second
column, and so on.
Figure 3-17 How to sort a result set by an alias, an expression, or a column number
120 Section 2 The essential SQL skills
Description
• The OFFSET clause specifies the number of rows that should be skipped before
rows are returned from the result set.
• The FETCH clause specifies the number of rows that should be retrieved after
skipping the specified number of rows. If you omit the FETCH clause, all of the
rows to the end of the result set are retrieved.
• The number of rows to be skipped and retrieved can be specified as an integer or an
expression that results in an integer that is greater than or equal to zero.
• The OFFSET and FETCH clauses are most useful when a client application needs
to retrieve one page of data at a time.
Perspective
The goal of this chapter has been to teach you the basic skills for coding
SELECT statements. You' ll use these skills in almost every SELECT statement
you code. As you'll see in the chapters that follow, however, there's a lot more
to coding SELECT statements than what's presented here. In the next three
chapters, then, you' lllearn additional skills for coding SELECT statements.
When you complete those chapters, you 'II know everything you need to know
about retrieving data from a SQL Server database.
Terms
keyword order of precedence
filter function
Boolean expression parameter
predicate argument
expressiOn date literal
column alias comparison operator
substitute name logical operator
string expression compound condition
concatenate subquery
concatenation operator string pattern
literal value mask
string literal wildcard
string constant Full-Text Search
arithmetic expression null value
arithmetic operator nested sort
Chapter 3 How to retrieve data from a single table 123
Exercises
1. Write a SELECT statement that returns three columns from the Vendors table:
VendorContactFName, VendorContactLName, and VendorName. Sort the
result set by last name, then by first name.
2. Write a SELECT statement that returns four columns from the Invoices table,
named Number, Total, Credits, and Balance:
Number Column alias for the InvoiceNumber column
Total Column alias for the InvoiceTotal column
Credits Sum of the PaymentTotal and CreditTotal columns
Balance InvoiceTotal minus the sum of PaymentTotal and CreditTotal
Use the AS keyword to assign column aliases.
3. Write a SELECT statement that returns one column from the Vendors table
named Full Name. Create this column from the VendorContactFName and
VendorContactLName columns. Format it as follows: last name, comma, first
name (for example, "Doe, John"). Sort the result set by last name, then by first
name.
4. Write a SELECT statement that returns three columns:
InvoiceTotal From the Invoices table
10% 10% of the value of InvoiceTotal
Plus 10% The value of InvoiceTotal plus 10%
(For example, if InvoiceTotal is 100.0000, 10% is 10.0000, and Plus 10% is
110.0000.) Only return those rows with a balance due greater than 1000. Sort
the result set by InvoiceTotal, with the largest invoice first.
5. Modify the solution to exercise 2 to filter for invoices with an InvoiceTotal
that's greater than or equal to $500 but less than or equal to $10,000.
6. Modify the solution to exercise 3 to filter for contacts whose last name begins
with the letter A, B, C, or E.
7. Write a SELECT statement that determines whether the PaymentDate column
of the Invoices table has any invalid values. To be valid, PaymentDate must be
a null value if there's a balance due and a non-null value if there's no balance
due. Code a compound condition in the WHERE clause that tests for these
conditions.
4
How to retrieve data
from two or more tables
In the last chapter, you learned how to create result sets that contain data from
a single table. Now, this chapter will show you how to create result sets that
contain data from two or more tables. To do that, you can use either a join or a
umon.
(114 rows)
Description
• A join is used to combine columns from two or more tables into a result set based
on the join conditions you specify. For an inner join, only those rows that satisfy
the join condition are included in the result set.
• A join condition names a column in each of the two tables involved in the join and
indicates how the two columns should be compared. In most cases, you use the
equal operator to retrieve rows with matching columns. However, you can also use
any of the other comparison operators in a join condition.
• In most cases, you'll join two tables based on the relationship between the primary
key in one table and a foreign key in the other table. However, you can also join
tables based on relationships not defined in the database. These are called ad hoc
relationships.
• If the two columns in a join condition have the same name, you have to qualify
them with the table name so that SQL Server can distinguish between them. To
code a qualified column name, type the table name, followed by a period, followed
by the column name.
Notes
• The INNER keyword is optional and is seldom used.
• This syntax for coding an inner join can be referred to as the explicit syntax. It is
also called the SQL-92 syntax because it was introduced by the SQL-92 standards.
• You can also code an inner join using the implicit syntax. See figure 4-7 for more
information.
(11 rows)
1 Publishers Marketing _ ,
2 Prospect Hst
3 133560 175.00 Card deck advertising
(6 rows)
Description
• Correlation names are temporary table names assigned in the FROM clause. You
can use correlation names when long table names make qualified column names
long or confusing. A correlation name can also be called a table alias.
• If you assign a correlation name to a table, you must use that name to refer to the
table within your query. You can' t use the original table name.
• Although the AS keyword is optional, I recommend you use it because it makes the
FROM clause easier to read.
• You can use a correlation name for any table in a join without using correlation
names for all of the other tables.
• Use correlation names whenever they simplify or clarify the query. Avoid using
correlation names when they make a query more confusing or difficult to read.
Description
• A fully-qualified object name is made up of four parts: the server name, the
database name, the schema name (typically dbo), and the name of the object
(typically a table). This syntax can be used when joining tables from different
databases or databases on different servers.
• If the server or database name is the same as the current server or database name,
or if the schema name is dbo or the name of the user's default schema, you can
omit that part of the name to create a partially-qualified object name. If the omitted
name falls between two other parts of the name, code two periods to indicate that
the name is omitted.
• Before you can specify a server name, you must add a linked server to the current
instance of the server. To do that, you can use the stored procedure named
sp_AddLinkedServer. Then, you can specify the name of the linked server. To
remove a linked server, you can use the stored procedure named sp_DropServer.
To remove a linked server, you can use the stored procedure named
sp_DropServer. For more information about working with linked servers, look
up "Linked Servers (Database Engine)" in the documentation for SQL Server.
The same join with the second condition coded in a WHERE clause
SELECT InvoiceNumber, InvoiceDate,
InvoiceTotal, InvoiceLineitemAmount
FROM Invoices JOIN InvoiceLineitems AS Lineitems
ON Invoices.InvoiceiD = Lineitems . InvoiceiD
WHERE Invoices.InvoiceTotal > Lineitems.InvoiceLineitemAmount
ORDER BY InvoiceNumber;
Description
• A join condition can include two or more conditions connected by AND or OR
operators.
• In most cases, your code will be easier to read if you code the join condition in the
ON expression and search conditions in the WHERE clause.
A self-join that returns vendors from cities in common with other vendors
SELECT DISTINCT Vendorsl.VendorName, Vendorsl.VendorCity,
Vendorsl.VendorState
FROM Vendors AS Vendorsl JOIN Vendo rs AS Vendors 2
ON (Vendorsl.VendorCity = Vendo rs2. VendorCity ) AND
(Vendorsl.VendorSta te = Vendors2 .VendorState) AND
(Vendorsl.VendoriD <> Vendors2 .VendoriD)
ORDER BY Vendorsl.VendorState, Vendorsl . VendorCity;
Description
• A self-join is a join that joins a table with itself.
• When you code a self-join, you must use correlation names for the tables, and you
must qualify each column name with the correlation name.
• Self-joins frequently include the DISTINCT keyword to eliminate duplicate rows.
(1 1 rows)
(11 rows )
(11 rows)
Description
• You can think of a multi-table join as a series of two-table joins proceeding from
left to right. The first two tables are joined to produce an interim result set or
interim table. Then, the interim table is joined with the next table, and so on.
Figure 4-6 Inner joins that join more than two tables
138 Section 2 The essential SQL skills
Description
• Instead of coding a join condition in the FROM clause, you can code it in the
WHERE clause along with any search conditions_ Then, you simply list the tables
you want to join in the FROM clause separated by commas_
• This syntax for coding joins is referred to as the implicit syntax, or the theta syntax.
It was used prior to the SQL-92 standards, which introduced the explicit syntax.
• If you omit the join condition from the WHERE clause, a cross join is performed.
You 'Illearn about cross joins later in this chapter.
( 202 rows )
Description
• An outer j oin retrieves all rows that satisfy the j oin condition, plus unmatched rows
in one or both tables.
• In most cases, you use the equal operator to retrieve rows with matching columns.
However, you can also use any of the other comparison operators.
• When a row with unmatched columns is retrieved, any columns from the other
table that are included in the result set are given null values.
Notes
• The OUTER keyword is optional and typically omitted.
• Prior to SQL Server 2005 , you could also use the implicit syntax to code left outer
and right outer joins. To do that, you listed the tables to be j oined in the FROM
clause, and you used the *= (left) and =* (right) operators in the WHERE clause to
specify the join condition.
-
5 Maintenance 5 5 5 Aaronsen Robert 2
6 6 WC!Ison Denise 6
7 7 Hardy Thomas 5
8 8 O'Leary Rhea 4
9 9 l..ocario Paulo 6
"""
A left outer join
SELECT DeptName, Departments.DeptNo, DeptName DeptNo L..astName
l'"'"'""'''""'""'""'"""'l
LastName
FROM Departments LEFT JOIN Employees
1 l. ~~~. . . ..J1 Hernandez
2 Payroll 2 Smlh
ON Departments.DeptNo = 3 Payroll 2 Simonian
Employees.DeptNo; 4 Payroll 2 Aaronsen
5 Operations 3 NULL
6 PefSOmel 4 Jones
7 PefSOmel 4 O'leary
8 Maintenance 5 Hardy
Description
• From these examples, you can see that none of the employees in the Employees
table work in the Operations department, and two of the employees (Watson and
Locario) work in a department that doesn't exist in the Departments table.
A SELECT statement that joins the three tables using full outer joins
SELECT DeptName, LastName, ProjectNo
FROM Departments
FULL JOIN Employees
ON Departments.DeptNo = Employees.DeptNo
FULL JOIN Projects
ON Employees.EmployeeiD = Projects.EmployeeiD
ORDER BY DeptName;
DeptName LastName ProjectNo
1 [.~'_g.~~.-. ~:~.'_'_'~.'.'.] Watson P1013
2 NULL l.ocario P1013
3 NULL NULL P1014
4 Accounting Hernandez P1011
5 Mainteniilnce Hii!rdy NULL
6 Operations NULL NULL
7 Payroll Smith P1012
8 Payroll Simonian P1012
9 Payroll Aaronsen P1012
10 Personnel Jones NULL
11 Personnel O'Leary P1011
Figure 4-1 0 Outer joins that join more than two tables
146 Section 2 The essential SQL skills
(4 5 rows )
"""'
Description
• A cross join joins each row from the first table with each row from the second
table. The result set returned by a cross join is known as a Cartesian product.
• To code a cross join using the explicit syntax, use the CROSS JOIN keywords in
the FROM clause.
• To code a cross join using the implicit syntax, list the tables in the FROM clause
and omit the join condition from the WHERE clause.
2
3
Paid
Active
().2060
P-0608
202().{)1-24 00:00:00
202().{)1-23 00:00:00
23517.58
20551.18
L
4 Active 0.2436 202().{)1-31 00:00:00 10976.06
5 Paid 989319-447 202().{)1-24 00:00:00 3689.99
6 Paid 989319-467 202().{)1-01 00:00:00 2318.03
7 Paid 989319-417 202().{)1-23 00:00:00 2051.59
8 Paid 971212 202().{)1-25 00:00:00 1000.46
9 Paid 963253230 202().{)1-07 00:00:00 739.20
(35 rows)
Description
• A union combines the result sets of two or more SELECT statements into one
result set.
• Each result set must return the same number of columns, and the corresponding
columns in each result set must have compatible data types.
• By default, a union eliminates duplicate rows. If you want to include duplicate
rows, code the ALL keyword.
• The column names in the final result set are taken from the first SELECT clause.
Column aliases assigned by the other SELECT clauses have no effect on the final
result set.
• To sort the rows in the final result set, code an ORDER BY clause after the last
SELECT statement. This clause must refer to the column names assigned in the
first SELECT clause.
A union that combines payment data from the same joined tables
SELECT InvoiceNumber, VendorName, '33% Payment' AS PaymentType,
InvoiceTotal AS Total, (InvoiceTotal * 0.333) AS Payment
FROM Invoices JOIN Vendors
ON Invoices.VendoriD = Vendors.VendoriD
WHERE InvoiceTotal > 10000
UNION
SELECT InvoiceNumber, VendorName, '50% Payment' AS PaymentType,
InvoiceTotal AS Total, (InvoiceTotal * 0.5) AS Payment
FROM Invoices JOIN Vendors
ON Invoices.VendoriD = Vendors.VendoriD
WHERE InvoiceTotal BETWEEN 500 AND 10000
UNION
SELECT InvoiceNumber, VendorName, 'Full amount' AS PaymentType,
InvoiceTotal AS Total, InvoiceTotal AS Payment
FROM Invoices JOIN Vendors
ON Invoices.VendoriD = Vendors.VendoriD
WHERE InvoiceTotal < 500
ORDER BY PaymentType, VendorName, InvoiceNumber;
Figure 4-14 Unions that combine data from the same table
154 Section 2 The essential SQL skills
(23 rows)
[fh..?..~.~.~·.::::::::::.~:J Hardy
(1 row)
Description
• The number of columns must be the same in both SELECT statements.
• The data types for each column must be compatible.
• The column names in the final result set are taken from the first SELECT statement.
Perspective
In this chapter, you learned a variety of techniques for combining data from
two or more tables into a single result set. In particular, you learned how to use
the SQL-92 syntax for combining data using inner joins. Of all the techniques
presented in this chapter, this is the one you' ll use most often. So you' ll want to
be sure you understand it thoroughly before you go on.
Terms
JOin interim table
join condition implicit syntax
mner JOin theta syntax
ad hoc relationship outer join
qualified column name left outer join
explicit syntax right outer join
correlation name full outer join
table alias CrOSS JOin
fully-qualified object name Cartesian product
partially-qualified object name umon
self-join set operator
interim result set
Exercises
Unless otherwise stated, use the explicit join syntax.
1. Write a SELECT statement that returns all columns from the Vendors table
inner-joined with the Invoices table.
2. Write a SELECT statement that returns four columns:
VendorName From the Vendors table
InvoiceNumber From the Invoices table
InvoiceDate From the Invoices table
Balance InvoiceTotal minus the sum of
PaymentTotal and CreditTotal
The result set should have one row for each invoice with a non-zero balance.
Sort the result set by VendorName in ascending order.
3. Write a SELECT statement that returns three columns:
VendorName From the Vendors table
DefaultAccountNo From the Vendors table
AccountDescription From the GLAccounts table
The result set should have one row for each vendor, with the account number
and account description for that vendor's default account number. Sort the
result set by AccountDescription, then by VendorName.
Chapter 4 How to retrieve data from two or more tables 157
4. Generate the same result set described in exercise 2, but use the implicit join
syntax.
5. Write a SELECT statement that returns five columns from three tables, all
using column aliases:
Vendor VendorName column
Date InvoiceDate column
Number InvoiceNumber column
# InvoiceSequence column
Lineltem InvoiceLineltemAmount column
Assign the following correlation names to the tables:
v Vendors table
Invoices table
li InvoiceLineltems table
Sort the final result set by Vendor, Date, Number, and #.
6. Write a SELECT statement that returns three columns:
VendoriD From the Vendors table
VendorName From the Vendors table
Name A concatenation of VendorContactFName and
VendorContactLName, with a space in between
The result set should have one row for each vendor whose contact has the
same first name as another vendor's contact. Sort the final result set by Name.
Hint: Use a self-join.
7. Write a SELECT statement that returns two columns from the GLAccounts
table: AccountNo and AccountDescription. The result set should have one row
for each account number that has never been used. Sort the final result set by
AccountNo.
Hint: Use an outer join to the InvoiceLineitems table.
8. Use the UNION operator to generate a result set consisting of two columns
from the Vendors table: VendorName and VendorState. If the vendor is in
California, the VendorState value should be "CA"; otherwise, the VendorState
value should be "Outside CA." Sort the final result set by VendorName.
5
How to code
•
summary quer1es
In this chapter, you'll learn how to code queries that summarize data. For
example, you can use summary queries to report sales totals by vendor or
state, or to get a count of the number of invoices that were processed each day
of the month. You'll also learn how to use a special type of function called an
aggregate function. Aggregate functions allow you to easily do jobs like figure
averages or totals, or find the highest value for a given column. So you 'll use
them frequently in your summary queries.
A summary query that counts unpaid invoices and calculates the total due
SELECT COUNT (*) AS Numbe rOfinvoi ces,
SUM ( I nvoiceTo tal - PaymentTo tal - Cred itTo tal ) AS To talDue
FROM Invoice s
WHERE InvoiceTo t a l - Pa ymentTot a l - Cred i tTota l > 0;
Description
• Aggregate functions, also called columnfunctions, perform a calculation on the
values in a set of selected rows. You specify the values to be used in the calculation
by coding an expression for the function 's argument. In many cases, the expression
is just the name of a column.
• A SELECT statement that includes an aggregate function can be called a summary
query.
• The expression you specify for the AVG and SUM functions must result in a
numeric value. The expression for the MIN, MAX, and COUNT functions can
result in a numeric, date, or string value.
• By default, all values are included in the calculation regardless of whether they're
duplicated. If you want to omit duplicate values, code the DISTINCT keyword.
This keyword is typically used only with the COUNT function.
• All of the aggregate functions except for COUNT(*) ignore null values.
• Aggregate functions are often used with the GROUP BY clause of the SELECT
statement, which is used to group the rows in a result set. See figure 5-3 for more
information.
• If you code an aggregate function in the SELECT clause, that clause can't include
non-aggregate columns from the base table unless the column is specified in a
GROUP BY clause or the OVER clause is included for each aggregate function .
See figure 5-10 for more information on the OVER clause.
A summary query that uses the COUNT(*), AVG, and SUM functions
SELECT ' After 7/1/2019' AS SelectionDate, COUNT(*) AS NumberOfinvoices,
AVG(InvoiceTotal) AS AverageinvoiceAmount,
SUM(InvoiceTotal) AS TotalinvoiceAmount
FROM Invoices
WHERE InvoiceDate > '2019-07-01';
SelectionDate Numbe!Oflnvoices AveragelnvoiceAmount TotallnvoiceAmount
I"'H!;"7/i'hO'i9''"!
\ooOOOOOOOOOOOOOO OOO" OOO" OOO"OOMO"OOOO!
114 1879.7413 214290.51
Notes
• If you want to count all of the selected rows, you 'll typically use the COUNT(*)
function as illustrated by the first two examples above. An alternative is to code the
name of any column in the base table that can't contain null values, as illustrated by
the third example.
• If you want to count only the rows with unique values in a specified column, you
can code the COUNT function with the DISTINCT keyword followed by the name
of the column, as illustrated by the fourth example.
Description
• The GROUP BY clause groups the rows of a result set based on one or more
columns or expressions. It's typically used in SELECT statements that include
aggregate functions.
• If you include aggregate functions in the SELECT clause, the aggregate is calcu-
lated for each set of values that result from the columns named in the GROUP BY
clause.
• If you include two or more columns or expressions in the GROUP BY clause, they
form a hierarchy where each column or expression is subordinate to the previous
one.
• When a SELECT statement includes a GROUP BY clause, the SELECT clause can
include aggregate functions, the columns used for grouping, and expressions that
result in a constant value.
• A group-by list typically consists of the names of one or more columns separated
by commas. However, it can contain any expression except for those that contain
aggregate functions.
• The HAVING clause specifies a search condition for a group or an aggregate. This
condition is applied after the rows that satisfy the search condition in the WHERE
clause are grouped.
(34 rows)
(12 rows)
Note
• You can use a join with a summary query to group and summarize the data in two
or more tables.
Figure 5-4 Queries that use the GROUP BY and HAVING clauses
168 Section 2 The essential SQL skills
(19 rows)
(20 rows)
Description
• When you include a WHERE clause in a SELECT statement that uses grouping
and aggregates, the search condition is applied before the rows are grouped and the
aggregates are calculated. That way, only the rows that satisfy the search condition
are grouped and summarized.
• When you include a HAVING clause in a SELECT statement that uses grouping
and aggregates, the search condition is applied after the rows are grouped and the
aggregates are calculated. That way, only the groups that satisfy the search
condition are included in the result set.
• A HAVING clause can only refer to a column included in the SELECT or GROUP
BY clause. A WHERE clause can refer to any column in the base tables.
• Aggregate functions can only be coded in the HAVING clause. A WHERE clause
can't contain aggregate functions.
Figure 5-5 How the HAVING clause compares to the WHERE clause
170 Section 2 The essential SQL skills
A summary query that includes a summary row for each grouping level
(SQL Server 2008 or later)
SELECT Ve ndorSt a t e, Vendor City, COUNT (*) AS QtyVe ndors
FROM Ve ndor s
WHERE VendorSt ate I N (' IA ', ' NJ' )
GROUP BY ROLLUP (Ve ndo rState , Ve ndorCity)
ORDER BY VendorState DESC, Vendo r City DESC;
VendorState VendorQy OtyVendors
1 [~J::: :.·: : :.·: : : : : : .·:J Wasllngton
2 NJ Fairfield 1
3 NJ East Bru1swick 2
4 NJ NULL 4 Summary row for state 'NJ'
5 lA Washington
6 lA Faifleld 1
7 lA NULL 2 Summary row for state 'lA'
8 NULL NULL 6 Summary row for all rows
Description
• The ROLLUP operator adds a summary row for each group specified. It also adds a
summary row to the end of the result set that summarizes the entire result set. If the
GROUP BY clause specifies a single group, only the final summary row is added.
• The sort sequence in the ORDER BY clause is applied after the summary rows
are added. Because of that, you'll want to sort grouping columns in descending
sequence so the summary row for each group, which can contain null values,
appears after the other rows in the group.
• When you use the ROLLUP operator, you can't use the DISTINCT keyword in any
of the aggregate functions.
• You can use the GROUPING function with the ROLLUP operator to determine if a
summary row has a null value assigned to a given column. See chapter 9 for details.
Figure 5-7 How to use the ROLLUP operator
174 Section 2 The essential SQL skills
A summary query that includes a summary row for each set of groups
(SQL Server 2008 or later)
SELECT VendorState, VendorCity, COUNT(*) AS QtyVendors
FROM Vendors
WHERE VendorState IN ('IA', 'NJ')
GROUP BY CUBE (VendorState, VendorCity)
ORDER BY VendorState DESC, VendorCity DESC;
VendorS!ate VendooOy QtyVendors
1 [~L.:.::::=:.::.:~.:J Washington 1
2 NJ Fairfield 1
3 NJ East Bn.nswick 2
4 NJ NULL 4 Summary row for state 'NJ'
5 lA Washington 1
6 lA Farfield 1
Summary row for state 'lA'
7 lA NULL 2
8 NULL Washington 2 Summary row for city 'Washington'
9 NULL Fairfield 2 Summary row for city 'Fairfield'
10 NULL East Bn.nswick 2
Summary row for city 'East Brunswick'
11 NULL NULL 6 Summary row for all rows
Description
• The CUBE operator adds a summary row for every combination of groups specified.
It also adds a summary row to the end of the result set that summarizes the entire
result set.
• When you use the CUBE operator, you can' t use the DISTINCT keyword in any of
the aggregate functions.
• You can use the GROUPING function with the CUBE operator to determine if a
summary row has a null value assigned to a given column. See chapter 9 for details.
Figure 5-9 shows you how to use the GROUPING SETS operator that was
introduced with SQL Server 2008. This operator is similar to the ROLLUP
and CUBE operators. However, the GROUPING SETS operator only includes
summary rows, it only adds those summary rows for each specified group, and
it uses a syntax that's similar to the 2008 syntax for the ROLLUP and CUBE
operators.
The first example in this figure is similar to the second example that's
presented in the previous two figures. However, this example uses the
GROUPING SETS operator instead of the CUBE or ROLLUP operator. Here,
the result set only includes summary rows for the two grouping elements: state
and city. To start, it displays the summary rows for the states. Then, it displays
the summary rows for the cities.
The second example in this figure shows some additional features that are
available when you use the GROUPING SETS operator. To start, within the
parentheses after the GROUPING SETS operator, you can add additional sets
of parentheses to create composite groups that consist of multiple columns. In
addition, you can add an empty set of parentheses to add a group for a summary
row that summarizes the entire result set. In this example, the first group is the
vendor's state and city, the second group is the vendor's zip code, and the third
group is an empty set of parentheses that adds a summary row for the entire
result set.
When you use composite groups, you should know that you can add
additional summary rows by applying the ROLLUP and CUBE operators to a
composite group. To do that, you code the ROLLUP or CUBE operator before
the composite group. In this figure, for instance, the third example shows a
GROUPING SETS clause that applies the ROLLUP operator to the composite
state/city group. This adds a summary row for each state, and it adds a summary
row for the entire result set, much like the second example in figure 5-7.
Although this may seem confusing at first, with a little experimentation, you
should be able to get the result set you want.
Chapter 5 How to code summary queries 177
Description
• The GROUPING SETS operator creates a summary row for each specified group.
• Within the parentheses after the GROUPING SETS operator, you can add
additional sets of parentheses to create composite groups.
• Within the parentheses after the GROUPING SETS operator, you can add an empty
set of parentheses to add a summary row that summarizes the entire result set.
• For a composite group, you can add the ROLLUP or CUBE operator to add
additional summary rows. This performs the ROLLUP or CUBE operation on the
composite group, which also adds a summary row that summarizes the entire result
set.
""
A query that calculates a cumulative total and moving average
SELECT InvoiceNumber, InvoiceDate, InvoiceTotal,
SUM(InvoiceTotal) OVER (ORDER BY InvoiceDate) AS CumTotal,
COUNT(InvoiceTotal) OVER (ORDER BY InvoiceDate) AS Count,
AVG(InvoiceTotal) OVER (ORDER BY InvoiceDate) AS MovingAvg
FROM Invoices;
InvoiceNumber InvoiceDate InvoiceTotal CumTotal CoiXll MovilgAvg ,..
1"'98931'94'5'7'"""""1 2019-1G{)S
l . o oOOO OOOOOOOOOOOO,OOOO' " '' ' "' OMOOOOOOO;
3813.33 3813.33 1 3813.33
2 263253241 2019-10.10 40.20 3853.53 2 1926.765
3 963253234 2019-10.13 138.75 3992.28 3 1330.76
4 2.()00.2993 2019-1().16 144.70 4195.23 6 699.205
5 963253251 2019-10.16 15.50 4195.23 6 699.205
6 963253261 2019-10.16 42.75 4195.23 6 699.205
"""
Description
• When used with the aggregate functions, the OVER clause lets you summarize the
data in a result set while still returning the rows used to calculate the summary.
Perspective
In this chapter, you learned how to code queries that group and summarize
data. In most cases, you'll be able to use the techniques presented here to get
the summary information you need. If not, you may want to find out about
another tool provided by SQL Server 2019 called Analysis Services. This tool
provides a graphical interface that lets you build complex data models based on
cubes. Then, you can use those models to analyze the database using complex
patterns and correlations. You can find out more about this tool by searching for
"Analysis Services" in the documentation for SQL Server.
Terms
scalar function scalar aggregate
aggregate function vector aggregate
column function cumulative total
summary query movmg average
1. Write a SELECT statement that returns two columns from the Invoices table:
VendoriD and PaymentSum, where PaymentSum is the sum of the PaymentTotal
column. Group the result set by VendoriD.
2. Write a SELECT statement that returns two columns: VendorName and
PaymentSum, where PaymentSum is the sum of the PaymentTotal column.
Group the result set by VendorName. Return only 10 rows, corresponding to
the 10 vendors who've been paid the most.
Hint: Use the TOP clause and join Vendors to Invoices.
3. Write a SELECT statement that returns three columns: VendorName,
InvoiceCount, and InvoiceSum. InvoiceCount is the count of the number of
invoices, and InvoiceSum is the sum of the InvoiceTotal column. Group the
result set by vendor. Sort the result set so the vendor with the highest number
of invoices appears first.
4. Write a SELECT statement that returns three columns: AccountDescription,
LineltemCount, and LineltemSum. LineltemCount is the number of entries in
the InvoiceLineitems table that have that AccountNo. LineitemSum is the sum
of the InvoiceLineitemAmount column for that AccountNo. Filter the result
set to include only those rows with LineitemCount greater than 1. Group the
result set by account description, and sort it by descending LineltemCount.
Hint: Join the GLAccounts table to the InvoiceLineltems table.
5. Modify the solution to exercise 4 to filter for invoices dated from October 1,
2019 to December 31, 2019.
Hint: Join to the Invoices table to code a search condition based on InvoiceDate.
Chapter 5 How to code summary queries 181
6. Write a SELECT statement that answers the following question: What is the
total amount invoiced for each AccountNo? Use the ROLLUP operator to
include a row that gives the grand total.
Hint: Use the InvoiceLineitemAmount column of the InvoiceLineltems table.
7. Write a SELECT statement that returns four columns: VendorName,
AccountDescription, LineitemCount, and LineitemSum. LineltemCount is
the row count, and LineltemSum is the sum of the InvoiceLineltemAmount
column. For each vendor and account, return the number and sum of line
items, sorted first by vendor, then by account description.
Hint: Use a four-table join.
8. Write a SELECT statement that answers this question: Which vendors are
being paid from more than one account? Return two columns: the vendor
name and the total number of accounts that apply to that vendor's invoices.
Hint: Use the DISTINCT keyword to count InvoiceLineitems.AccountNo.
9. Write a SELECT statement that returns six columns:
VendoriD From the Invoices table
InvoiceDate From the Invoices table
InvoiceTotal From the Invoices table
VendorTotal The sum of the invoice totals for each vendor
VendorCount The count of invoices for each vendor
VendorAvg The average of the invoice totals for each vendor
The result set should include the individual invoices for each vendor.
6
An introduction to subqueries
Since you know how to code SELECT statements, you already know how
to code a subquery. It's simply a SELECT statement that's coded within another
SQL statement. The trick to using subqueries, then, is knowing where and
when to use them. You' ll learn the specifics of using subqueries throughout this
chapter. The two topics that follow, however, will give you an overview of where
and when to use them.
( 2 1 rows)
Description
• A subquery is a SELECT statement that's coded within another SQL statement.
• A subquery can return a single value, a result set that contains a single column, or a
result set that contains one or more columns.
• A subquery that returns a single value can be coded, or introduced, anywhere an
expression is allowed. A subquery that returns a single column can be introduced
in place of a list of values, such as the values for an IN phrase. And a subquery that
returns one or more columns can be introduced in place of a table in the FROM
clause.
• The syntax for a subquery is the same as for a standard SELECT statement.
However, a subquery doesn't typically include the GROUP BY or HAVING clause,
and it can't include an ORDER BY clause unless the TOP phrase is used.
• A subquery that's used in a WHERE or HAVING clause is called a subquery search
condition or a subquery predicate. This is the most common use for a subquery.
• Although you can introduce a subquery in a GROUP BY or ORDER BY clause,
you usually won' t need to.
• Subqueries can be nested within other subqueries. However, subqueries that are
nested more than two or three levels deep can be difficult to read and can result in
poor performance.
(40 rows)
Advantages of joins
• The result of a join operation can include columns from both tables. The result of a
query that includes a subquery can only include columns from the table named in
the outer query. It can't include columns from the table named in the subquery.
• A join tends to be more intuitive when it uses an existing relationship between the
two tables, such as a primary key to foreign key relationship.
• A query with a join typically performs faster than the same query with a subquery,
especially if the query uses only inner joins.
Advantages of subqueries
• You can use a subquery to pass an aggregate value to the outer query.
• A subquery tends to be more intuitive when it uses an ad hoc relationship between
the two tables.
• Long, complex queries can sometimes be easier to code using subqueries.
Description
• Like a join, a subquery can be used to code queries that work with two or more
tables.
• Most subqueries can be restated as joins and most joins can be restated as
subqueries.
(34 rows)
(88 rows)
Description
• You can introduce a subquery with the IN operator to provide the list of values that
are tested against the test expression.
• When you use the IN operator, the subquery must return a single column of values.
• A query that uses the NOT IN operator with a subquery can typically be restated
using an outer join.
A query that returns invoices with a balance due less than the average
SELECT InvoiceNumber, InvoiceDate, InvoiceTotal,
InvoiceTotal - PaymentTotal - CreditTotal AS BalanceDue
FROM Invoices
WHERE InvoiceTotal - PaymentTotal - CreditTotal > 0
AND InvoiceTotal - PaymentTotal - CreditTotal <
(SELECT AVG(InvoiceTotal - PaymentTotal - CreditTotal)
FROM Invoices
WHERE InvoiceTotal - PaymentTotal - CreditTotal > 0)
ORDER BY InvoiceTotal DESC;
(9 rows)
Description
• You can use a comparison operator in a search condition to compare an expression
with the results of a subquery.
• If you code a search condition without the ANY, SOME, and ALL keywords, the
subquery must return a single value.
• If you include the ANY, SOME, or ALL keyword, the subquery can return a list of
values. See figures 6-5 and 6-6 for more information on using these keywords.
A query that returns invoices larger than the largest invoice for vendor 34
SELECT VendorName, InvoiceNumber, InvoiceTotal
FROM Invoices JOIN Vendors ON Invoices.VendoriD = Vendors.VendoriD
WHERE InvoiceTotal > ALL
(SELECT InvoiceTotal
FROM Invoices
WHERE Ve ndoriD = 34)
ORDER BY VendorName;
I :~~:=
The result set
VendorName InvoiceNumber InvoiceTotal
1 [·.·:.~.~.~~·.~~~.·~. ·~·~!::·:.~.·.·.] 509786 6940.25
2 Cahners Publishng Company 587056 2184.50 I!
(25 rows)
Description
• You can use the ALL keyword to test that a comparison condition is true for all of the
values returned by a subquery. This keyword is typically used with the comparison
operators<,>,<=, and>=.
• If no rows are returned by the subquery, a comparison that uses the ALL keyword is
always true.
• If all of the rows returned by the subquery contain a null value, a comparison that
uses the ALL keyword is always false.
X <> ANY (1, 2) (x <> 1 ) OR (x <> 2) This condition will evaluate to True for any
non-empty result set containing at least one
non-null value that isn' t equal to x.
(17 rows)
Description
• You can use the ANY or SOME keyword to test that a condition is true for one or
more of the values returned by a subquery.
• ANY and SOME are equivalent keywords. SOME is the ANSI-standard keyword,
but ANY is more commonly used.
• If no rows are returned by the subquery or all of the rows returned by the subquery
contain a null value, a comparison that uses the ANY or SOME keyword is always false.
( 36 r ows )
Description
• A correlated subquery is a subquery that is executed once for each row processed
by the outer query. By contrast, a noncorrelated subquery is executed only once.
All of the subqueries you've seen so far have been noncorrelated subqueries.
• A correlated subquery refers to a value that's provided by a column in the outer
query. Because that value varies depending on the row that's being processed, each
execution of the subquery returns a different result.
• To refer to a value in the outer query, a correlated subquery uses a qualified column
name that includes the table name from the outer query. If the subquery uses the
same table as the outer query, an alias, or correlation name, must be assigned to
one of the tables to remove ambiguity.
Note
• Because a correlated subquery is executed once for each row processed by the outer
query, a query with a correlated subquery typically takes longer to run than a query
with a noncorrelated subquery.
( 88 rows )
Description
• You can use the EXISTS operator to test that one or more rows are returned by the
subquery. You can also use the NOT operator along with the EXISTS operator to
test that no rows are returned by the subquery.
• When you use the EXISTS operator with a subquery, the subquery doesn't actually
return any rows. Instead, it returns an indication of whether any rows meet the
specified condition.
• Because no rows are returned by the subquery, it doesn' t matter what columns you
specify in the SELECT clause. So you typically just code an asterisk(*).
• Although you can use the EXISTS operator with either a correlated or a
noncorrelated subquery, it's used most often with correlated subqueries. That's
because it's usually better to use a join than a noncorrelated subquery with
EXISTS.
Description
• A subquery that's coded in the FROM clause returns a result set called a derived
table. When you create a derived table, you must assign an alias to it. Then, you
can use the derived table within the outer query just as you would any other table.
• When you code a subquery in the FROM clause, you must assign names to any
calculated values in the result set.
• Derived tables are most useful when you need to further summarize the results of a
summary query.
• A derived table is like a view in that it retrieves selected rows and columns from
one or more base tables. Because views are stored as part of the database, they're
typically more efficient to use than derived tables. However, it may not always be
practical to construct and save a view in advance.
(122 rows)
Description
• When you code a subquery for a column specification in the SELECT clause, the
subquery must return a single value.
• A subquery that's coded within a SELECT clause is typically a correlated subquery.
• A query that includes a subquery in its SELECT clause can typically be restated
using a join instead of the subquery. Because a join is usually faster and more
readable, subqueries are seldom coded in the SELECT clause.
(10 rows )
(34 rows)
(10 rows)
How to work
with common table expressions
A common table expression ( CTE) is a feature that allows you to code an
expression that defines a derived table. You can use CTEs to simplify complex
queries that use subqueries. This can make your code easier to read and
maintain. In addition, you can use a CTE to loop through nested structures.
TopinState AS
(
SELECT VendorState, MAX(SumOfinvoices) AS SumOfinvoices
FROM Summary
GROUP BY VendorState
(10 rows)
Description
• A common table expression ( CTE) is an expression (usually a SELECT statement)
that creates one or more temporary tables that can be used by the following query.
• To use a CTE with a query, you code the WITH keyword followed by the definition
of the CTE. Then, immediately after the CTE, you code the statement that uses it.
• To code multiple CTEs, separate them with commas. Then, each CTE can refer to
itself and any previously defined CTEs in the same WITH clause.
• You can use CTEs with SELECT, INSERT, UPDATE, and DELETE statements.
However, you're most likely to use them with SELECT statements as shown in this
figure and in figure 6-14.
SELECT *
FROM EmployeesCTE
ORDER BY Rank, EmployeeiD;
Perspective
As you' ve seen in this chapter, subqueries provide a powerful tool for
solving difficult problems. Before you use a subquery, however, remember
that a subquery can often be restated more clearly by using a join. In addition,
a query with a join often executes more quickly than a query with a subquery.
Because of that, you'll typically use a subquery only when it can' t be restated
as a join or when it makes the query easier to understand without slowing it
down significantly.
If you find yourself coding the same subqueries over and over, you should
consider creating a view for that subquery as described in chapter 13. This will
help you develop queries more quickly since you can use the view instead of
coding the subquery again. In addition, since views execute more quickly than
subqueries, this may improve the performance of your queries.
Terms
subquery derived table
introduce a subquery pseudocode
subquery search condition common table expression (CTE)
subquery predicate recursive query
nested subquery recursive CTE
correlated subquery anchor member
noncorrelated subquery recursive member
1. Write a SELECT statement that returns the same result set as this SELECT
statement. Substitute a subquery in a WHERE clause for the inner join.
SELECT DISTINCT VendorName
FROM Vendors JOIN Invoices
ON Vendors.VendoriD = Invoices . VendoriD
ORDER BY VendorName;
2. Write a SELECT statement that answers this question: Which invoices have
a PaymentTotal that's greater than the average PaymentTotal for all paid
invoices? Return the InvoiceNumber and InvoiceTotal for each invoice.
3. Write a SELECT statement that answers this question: Which invoices have
a PaymentTotal that's greater than the median PaymentTotal for all paid
invoices? (The median marks the midpoint in a set of values; an equal number
of values lie above and below it.) Return the InvoiceNumber and InvoiceTotal
for each invoice.
Hint: Begin with the solution to exercise 2, then use the ALL keyword in
the WHERE clause and code "TOP 50 PERCENT PaymentTotal" in the
subquery.
Chapter 6 How to code subqueries 213
4. Write a SELECT statement that returns two columns from the GLAccounts
table: AccountNo and AccountDescription. The result set should have one
row for each account number that has never been used. Use a correlated
subquery introduced with the NOT EXISTS operator. Sort the final result set
by AccountNo.
5. Write a SELECT statement that returns four columns: VendorName,
InvoiceiD, InvoiceSequence, and InvoiceLineltemAmount for each invoice
that has more than one line item in the InvoiceLineitems table.
Hint: Use a subquery that tests for InvoiceSequence > 1.
6. Write a SELECT statement that returns a single value that represents the sum
of the largest unpaid invoices submitted by each vendor. Use a derived table
that returns MAX(InvoiceTotal) grouped by VendoriD, filtering for invoices
with a balance due.
7. Write a SELECT statement that returns the name, city, and state of each
vendor that's located in a unique city and state. In other words, don' t include
vendors that have a city and state in common with another vendor.
8. Write a SELECT statement that returns four columns: VendorName,
InvoiceNumber, InvoiceDate, and InvoiceTotal. Return one row per vendor,
representing the vendor's invoice with the earliest date.
9. Rewrite exercise 6 so it uses a common table expression (CTE) instead of a
derived table.
7
Description
• The INTO clause is a SQL Server extension that lets you create a new table based on
the result set defined by the SELECT statement. Since the definitions of the columns in
the new table are based on the columns in the result set, the column names assigned in
the SELECT clause must be unique.
• You can code the other clauses of the SELECT INTO statement just as you would for
any other SELECT statement.
• If you use calculated values in the select list, you must name the column since that
name is used in the definition of the new table.
• The table you name in the INTO clause must not exist. If it does, you must delete it
using the DROP TABLE statement before you execute the SELECT INTO statement.
Warning
• When you use the SELECT INTO statement to create a table, only the column definitions
and data are copied. That means that definitions of primary keys, foreign keys, indexes,
default values, and so on are not included in the new table.
Figure 7-1 How to use the SELECT INTO statement to create test tables
218 Section 2 The essential SQL skills
An INSERT statement that adds the new row without using a column list
INSERT INTO InvoiceCopy
VALUES (97, 1 456789 1 , 1 2020-03-01 1 , 8344.50, 0, 0, 1, 1
2020-03-31 1 , NULL);
An INSERT statement that adds the new row using a column list
INSERT INTO InvoiceCopy
(VendoriD, InvoiceNumber, InvoiceTotal, PaymentTotal, CreditTotal,
TermsiD, InvoiceDate, InvoiceDueDate)
VALUES
(97 1 I 456789 I 1 8344 o 50, 0, 0, 1, I 2030-03-01 I 1 I 2020-03-31 I ) ;
Description
• You specify the values to be inserted in the VALUES clause. The values you
specify depend on whether you include a column list.
• If you don' t include a column list, you must specify the column values in the same
order as they appear in the table, and you must code a value for each column in the
table . The exception is an identity column, which must be omitted.
• If you include a column list, you must specify the column values in the same order
as they appear in the column list. You can omit columns with default values and
columns that accept null values, and you must omit identity columns.
Description
• To insert rows selected from one or more tables into another table, you can code a
subquery in place of the VALUES clause. Then, the rows in the derived table that
result from the subquery are inserted into the table.
• If you don' t code a column list in the INSERT clause, the subquery must return
values for all the columns in the table where the rows will be inserted, and the
columns must be returned in the same order as they appear in that table. The
exception is an identity column, which must be omitted.
• If you include a column list in the INSERT clause, the subquery must return values
for those columns in the same order as they appear in the column list. You can omit
columns with default values and columns that accept null values, and you must
omit identity columns.
Description
• You use the UPDATE statement to modify one or more rows in the table named in
the UPDATE clause.
• You name the columns to be modified and the value to be assigned to each
column in the SET clause. You can specify the value for a column as a literal or an
expressiOn.
• You can provide additional criteria for the update operation in the FROM clause,
which is a SQL Server extension. See figures 7-6 and 7-7 for more information.
• You can specify the conditions that must be met for a row to be updated in the
WHERE clause.
• You can use the DEFAULT keyword to assign the default value to a column that
has one, and you can use the NULL keyword to assign a null value to a column that
allows nulls.
• You can' t update an identity column.
Warning
• If you omit the WHERE clause, all the rows in the table will be updated.
An UPDATE statement that updates all the invoices for a vendor based
on the vendor's name
UPDATE InvoiceCopy
SET Te:rmsiD = 1
WHERE VendoriD =
(SELECT VendoriD
FROM VendorCopy
WHERE VendorName = 'Pacific Bell');
(6 rows affected)
Description
• You can code a subquery in the SET, FROM, or WHERE clause of an UPDATE
statement.
• You can use a subquery in the SET clause to return the value that's assigned to a
column.
• You can use a subquery in the FROM clause to identify the rows that are available
for update. Then, you can refer to the derived table in the SET and WHERE clauses.
• You can code a subquery in the WHERE clause to provide one or more values used
in the search condition.
Description
• If you need to specify column values or search conditions that depend on data in
a table other than the one named in the UPDATE clause, you can use a join in the
FROM clause.
• You can use columns from the j oined tables in the values you assign to columns in
the SET clause or in the search condition of a WHERE clause.
A DELETE statement that removes all the rows from the lnvoiceCopy table
DELETE InvoiceCopy;
(114 rows affected)
Description
• You can use the DELETE statement to delete one or more rows from the table you
name in the DELETE clause.
• You specify the conditions that must be met for a row to be deleted in the WHERE
clause.
• You can specify additional criteria for the delete operation in the FROM clause. See
figure 7-9 for more information.
Warning
• If you omit the WHERE clause from a DELETE statement, all the rows in the table
will be deleted.
Description
• You can use subqueries and joins in the FROM clause of a DELETE statement
to base the delete operation on the data in tables other than the one named in the
DELETE clause.
• You can use any of the columns returned by a subquery or a join in the WHERE
clause of the DELETE statement.
• You can also use subqueries in the WHERE clause to provide one or more values
used in the search condition.
Note
• The FROM clause is a SQL Server extension.
A WHEN clause that deletes rows that aren't matched by the source
WHEN NOT MATCHED BY SOURCE THEN
DELETE
Description
• The MERGE statement merges multiple rows from one table into another table.
Since this often involves updating existing rows and inserting new rows, the
MERGE statement is sometimes referred to as the upsert statement.
• When coding search conditions, you can use the AND keyword to create compound
search conditions.
• You can code one or more WHEN clauses that control when and how a row is
inserted, updated, or deleted.
• Within a WHEN clause, you can code a simplified INSERT, UPDATE, or DELETE
statement that doesn' t include a table name or a WHERE clause.
• To signal the end of a MERGE statement, you must code a semicolon (;).
Perspective
In this chapter, you learned how to use the INSERT, UPDATE, and
DELETE statements to modify the data in a database. Now, if you want to
practice using these statements, please use one of the two options presented
at the start of this chapter so you won't corrupt a live database or a database
shared by others.
In chapters 10 and 11, you'lllearn more about the table definitions that
can affect the way these statements work. If, for example, you delete a row
in a Vendors table that has related rows in an Invoices table, SQL Server may
delete all of the related rows in the Invoices table. But that depends on how
the relationship is defined. So, to complete your understanding of the INSERT,
UPDATE, and DELETE statements, you need to read chapters 10 and 11.
Finally, you should know that you can use the TOP clause with the
INSERT, UPDATE, and DELETE statements to limit the number of rows
that are inserted, updated, or deleted. This works similarly to using the TOP
clause with a SELECT statement as described in chapter 3. As a result, if you
read chapter 3, you shouldn' t have much trouble using the TOP clause with
the INSERT, UPDATE, and DELETE statements. For coding details, you can
search for "TOP (Transact-SQL)" in the documentation for SQL Server.
1. Write SELECT INTO statements to create two test tables named VendorCopy
and InvoiceCopy that are complete copies of the Vendors and Invoices tables.
If VendorCopy and InvoiceCopy already exist, first code two DROP TABLE
statements to delete them.
2. Write an INSERT statement that adds a row to the InvoiceCopy table with the
following values:
VendoriD: 32
InvoiceTotal: $434.58
TermsiD: 2
InvoiceNumber: AX-014-027
PaymentTotal: $0.00
InvoiceDueDate: 05/8/2020
InvoiceDate: 4/2112020
CreditTotal: $0.00
PaymentDate: null
3. Write an INSERT statement that adds a row to the VendorCopy table for
each non-California vendor in the Vendors table. (This will result in duplicate
vendors in the VendorCopy table.)
4. Write an UPDATE statement that modifies the VendorCopy table. Change
the default account number to 403 for each vendor that has a default account
number of 400.
Chapter 7 How to insert, update, and delete data 237
Description
• SQL Server defines dozens of data types that are divided into the four categories
shown above.
• The temporal data types are typically referred to as date/time data types, or simply
date data types.
• SQL Server supports most, but not all, of the ANSI-standard data types.
• SQL Server provides a synonym for each of the supported ANSI-standard data
types. Although you can use these synonyms, I recommend you use the SQL Server
data types instead.
• When you use the synonym for an ANSI data type, it's mapped to the appropriate
SQL Server data type indicated in the table above.
Description
• The integer data types are used to store whole numbers, which are numbers without
any digits to the right of the decimal point.
• The decimal data types are used to store decimal values, which can include digits
to the right of the decimal point. The precision of a decimal value indicates the total
number of digits that can be stored, and the scale indicates the number of digits that
can be stored to the right of the decimal point.
• The integer and decimal data types are considered exact numeric data types
because their precision is exact.
• The real data types are used to store floating-point numbers, which have a limited
number of significant digits. These data types are considered approximate numeric
data types because they may not represent a value exactly.
Figure 8-2 The numeric data types
244 Section 2 The essential SQL skills
The string data types with the default collation for most systems
Type Original value Value stored Bytes used
CHAR(2) 'CA' 'CA' 2
CHAR( 10) 'CA' 'CA 10
VARCHAR ( 2 0 ) 'CA' 'CA' 4 (2 + 2)
VARCHAR ( 2 0 ) 'New York' 'New York' 10 (8 + 2)
Description
• The number of bytes that are used to store characters depends on the collation that's
used. For more information about collations, see chapter 11.
• With the default collation, the char and varchar types use one byte per character. This
allows these types to store the characters used by most Western European languages.
• With the default collation, the nchar and nvarchar types use one byte-pair, or two
bytes, per character. This allows these types to store most Unicode characters,
which includes most characters used by the world's writing systems.
• The char and nchar types store fixed-length strings that use the same amount of storage
for each value. To do that, they append spaces to the end of the string whenever necessary.
• The varchar and nvarchar types store variable-length strings that use a varying
amount of storage for each value. To do that, they store the characters for the string
plus two bytes that store the length of the string.
• With the default collation, the number specified within the parentheses for a data type
matches the number of characters that can be stored in the data type. However, that's not
true for collations that allow these data types to store the full range of Unicode characters.
Figure 8-3 The string data types
246 Section 2 The essential SQL skills
Description
• You can specify a date/time value by coding a date/time literal. To code a date/time literal,
enclose the date/time value in single quotes.
• If you don' t specify a time when storing a date/time value, the time defaults to 12:00 a.m. If
you don't specify a date when storing a date/time value, the date defaults to January 1, 1900.
• By default, the two-digit year cutoff is 50, which means that 00 to 49 are interpreted as 2000
to 2049 and 50 through 99 are interpreted as 1950 through 1999.
• You can specify a time using either a 12-hour or a 24-hour clock. For a 12-hour clock, am is
the default.
Figure 8-4 The date/time data types
248 Section 2 The essential SQL skills
in the 20th or the 21st century. By default, SQL Server interprets the years 00
through 49 as 2000 through 2049, and it interprets the years 50 through 99 as
1950 through 1999. Because the two-digit year cutoff can be modified, however,
you'll want to find out what it is on your system before you use date literals with
two-digit years. Then, if you code a date literal outside the range, you'll have
to use a four-digit year. Of course, you can always code all of your date literals
with four-digit years, just to be sure.
The large value data types for SQL Server 2005 and later
Type Description
varchar(max) Works the same as the varchar type described in figure 8-3, but the max
specifi er allows this data type to store up to 2,147,483,648 bytes of data.
nvarchar(max) Works the same as the nvarchar type described in figure 8-3, but the max
specifier allows this data type to store up to 2,147,483,648 bytes of data.
varbinary(max) Stores variable-length binary data up to a maximum of 2, 147,483,648 bytes.
The number of bytes used to store the data depends on the actual length of
the data.
How the large value data types map to the old large object types
SQL Server 2005 and later Prior to 2005
varchar(max) text
nvarchar(max) ntext
varbinary(max) image
Description
• The max specifier can be used with the varchar, nvarchar, and varbinary data types
to increase the storage capacity of the column so it can store up to 2 gigabytes of
data.
• The varchar(max), nvarchar(max), and varbinary(max) data types are known as the
large value data types, and these data types can be used to store images and other
types of large character or binary data. For more information about working with
the varbinary(max) data type, see chapter 19.
• The text, ntext, and image data types that were used prior to SQL Server 2005 have
been deprecated and will be removed in a future version of Microsoft SQL Server.
As a result, you should avoid using these data types for any new development.
• The large value data types work like their smaller counterparts. As a result, once
you learn how to use the smaller counterparts, you can use the same skills to work
with the large value data types. In addition, the large value data types don' t have as
many restrictions as the old text, ntext, and image data types.
Description
• If you assign a value with one data type to a column with another data type, SQL
Server converts the value to the data type of the column using implicit conversion.
Not all data types can be converted implicitly to all other data types.
• SQL Server also uses implicit conversion when it evaluates an expression that
involves values with different data types. In that case, it converts the value whose
data type has lower precedence to the data type that has higher precedence. The
result of the expression is returned in this same data type.
• Each combination of precision and scale for the decimal and numeric values is
considered a different data type, with higher precision and scale taking precedence.
• If you want to perform a conversion that can' t be done implicitly or you want to
convert a data type with higher precedence to a data type with lower precedence, you
can use the CAST or CONVERT function to perform an explicit conversion.
• Not all data types can be converted to another data type. For example, the datetime2,
date, and time data types can' t be converted to any of the numeric data types.
Figure 8-6 How data conversion works
252 Section 2 The essential SQL skills
"'
Description
• You can use the CAST function to explicitly convert, or cast, an expression from
one data type to another.
• When you perform a division operation on two integers, the result is an integer. To
get a more accurate result, you can cast one of the integer values as a decimal. That
way, the result will be a decimal.
• CAST is an ANSI-standard function and is used more frequently than CONVERT,
which is unique to SQL Server. You should use CONVERT when you need the
additional formatting capabilities it provides. See figure 8-8 for details.
,..............
varcharDate varcharDate_1
....................., varchar0ate_107 varcharTalai varcharTotal_1 I\
Description
• You can use the CONVERT function to explicitly convert an expression from one
data type to another.
• You can use the optional style argument to specify the format to be used for
date/time, real, and money values converted to character data. For a complete list of
codes, search for "cast and convert" in the SQL Server documentation.
Description
• You can use the TRY_CONVERT function to explicitly convert an expression from
one data type to another.
• If the TRY_CONVERT function can' t convert the expression to the specified data
type, it returns a NULL value instead of generating an error.
• You can use the optional style argument to specify the format to be used for
date/time, real, and money values converted to character data. See figure 8-8 for
more information.
• The TRY_CONVERT function can only be used with an expression that has a data
type that can be explicitly converted to the specified data type.
US Postal Service
Attn: Supt. Window Services
Madison, WI 53707
Description
• The CHAR function is typically used to insert control characters into a character string.
• To code a Unicode value as a literal, precede the value with the character N.
Perspective
In this chapter, you learned about the different SQL Server data types. In
addition, you learned how to use some functions for converting data from one
type to another. In the next chapter, you'lllearn about some of the additional
functions for working with data.
Terms
data type exact numeric data types
string data type approximate numeric data types
numeric data type Unicode character
temporal data type supplementary characters
date/time data type national character
date data type fi xed-length string
integer data type variable-length string
decimal data type bit
scale ASCII (American Standard Code
prectswn for Information Interchange)
real data type large value data types
fixed-point number implicit conversion
floating-point number explicit conversion
significant digits cast
single-precision number Universal Time Coordinate (UTC)
double-precision number Greenwich Mean Time
scientific notation
1. Write a SELECT statement that returns four columns based on the InvoiceTotal
column of the Invoices table:
• Use the CAST function to return the first column as data type decimal
with 2 digits to the right of the decimal point.
• Use CAST to return the second column as a varchar.
• Use the CONVERT function to return the third column as the same data
type as the first column.
• Use CONVERT to return the fourth column as a varchar, using style 1.
2. Write a SELECT statement that returns three columns based on the
InvoiceDate column of the Invoices table:
• Use the CAST function to return the first column as data type varchar.
• Use the CONVERT function to return the second and third columns as
a varchar, using style 1 and style 10, respectively.
9
Notes
• The start argument must be an integer from 1 to the length of the string.
• The TRIM, TRANSLATE, and CONCAT_ WS functions were introduced with SQL
Server 2017.
2 17 Lance Pinos-Potter 17
3 2 Damell O'Su!Nvan 02
4 20 Jean Paul Renard 20
5 3 Alisha von Strunp 03
Description
• If you sort by a string column that contains numbers, you may receive unexpected
results. To avoid that, you can convert the string column to a numeric value in the
ORDER BY clause.
• If a string consists of two or more components, you can parse it into its individual
components. To do that, you can use the CHARINDEX function to locate the
characters that separate the components. Then, you can use the LEFT, RIGHT,
SUBSTRING, and LEN functions to extract the individual components.
Figure 9-2 How to solve common problems that occur with string data
268 Section 2 The essential SQL skills
ISNUMERIC(-1 . 25) 1
ISNUMERIC('SQL Server') 0
ISNUMERIC('2020-04-30 ') 0
RAND() 0 . 243729
Note
• To calculate the square or square root of a number with a data type other than float or
real, you must cast it to a floating-point number.
Description
• Because real values are approximate, you' ll want to search for approximate values
when retrieving real data. To do that, you can specify a range of values, or you can
use the ROUND function to search for rounded values.
• When you display real or decimal values, you may want to format them so they're
aligned on the right.
Figure 9-4 How to solve common problems that occur with numeric data
272 Section 2 The essential SQL skills
a date value for a given year, month, and day. In addition to this function, SQL
Server 2012 introduced other functions that let you create datetime,
smalldatetime, time, datetime2, and datetimeoffset values. For more information,
please search for "date and time functions" in the SQL Server documentation.
The last function (ISDATE) returns a Boolean value that indicates whether
an expression can be cast as a valid date/time value. This function is useful
for testing the validity of a date/time value before it's saved to a table. This is
illustrated by the last set of examples. Here, you can see that the first and third
expressions are valid dates, but the second and fourth expressions aren't. The
second expression isn' t valid because the month of September has only 30 days.
And the fourth expression isn't valid because a time value can have a maximum
of 59 minutes and 59 seconds. Note that this function checks for both a valid
date/time format and a valid date/time value.
The first two sets of examples illustrate the differences between the functions
that return date/time values. To start, there's a 7 hour difference between the
datetime value that's returned by the GETDATE and GETUTCDATE functions.
That's because I ran these functions from California, which is 7 hours behind the
Universal Time Coordinate (UTC). In addition, note that the datetime2(7) value
that's returned by the SYSDATETIME function provides more precise fractional
second values than the datetime value that's returned by the GETDATE and
GETUTCDATE functions. Finally, note that the SYSDATETIMEOFFSET
function returns a datetimeoffset value that includes a time zone offset.
The third set of examples shows how you can use the date parts with the
DATEPART and DATENAME functions. To start, you don't need to specify a
date part when you use the MONTH function to return an integer value for the
month. However, you can get the same result with the DATEPART function by
specifying the month date part as the first argument. Or, if you want to return
the name of the month as a string of characters, you can specify the month date
part as the first argument of the DATENAME function. Finally, you can use an
abbreviation for a date part whenever that makes sense. However, I generally
prefer to avoid abbreviations as they tend to make the code more difficult to read
and understand.
The fourth set of examples shows how to use the EOMONTH and
DATEFROMPARTS functions that were introduced with SQL Server 2012.
Here, the first expression uses the EOMONTH function to return a date for the
last day of the month for February 1, 2020. Since 2020 is a leap year, this returns
February 29, 2020. The second expression is similar, but it adds two months
to the specified date. Finally, the last expression uses the DATEFROMPARTS
function to create a date with a year value of 2020, a month value of 4, and a day
value of 3.
Chapter 9 How to use functions 275
EOMONTH('2020-02-01') 2020-02-29
EOMONTH('2020-02-01 ' ,2 ) 2020-04-30
DATEFROMPARTS(2020,4,3 ) 2020-04-03
ISDATE('2020-04-30 ' ) 1
ISDATE('2 020-04-31') 0
ISDATE('23 : 59:59') 1
ISDATE('23 : 99 : 99') 0
Notes
• When you use weekday with the DATEPART fu nction, it returns an integer that indicates
the day of the week where l =Sunday, 2=Monday, etc.
• The DAY, MONTH, and YEAR functions are ANSI-standard functions. The DATEPART
and DATENAME functions are more general-purpose functions provided by SQL Server.
Figure 9-6 How to parse dates and times
278 Section 2 The essential SQL skills
Description
• You can use the DATEADD function to subtract a specified number of date parts from a
date by coding the number of date parts as a negative value.
• If the number of date parts you specify in the DATEADD function isn' t an integer, the fractional
portion of the number is ignored.
• If the end date you specify in a DATEDIFF function is before the start date, the function
will return a negative value.
• You can also use the addition and subtraction operators to add and subtract days from a
date value . To add and subtract days from a date string, cast the string to a date/time value.
• You can also calculate the number of days between two dates by subtracting the date/time
values and converting the result to an integer.
Figure 9-7 How to perform operations on dates and times
280 Section 2 The essential SQL skills
Because date/time values often contain both a date and a time component,
searching for specific dates and times can be difficult. In this topic, you'lllearn a
variety of ways to ignore the time component when you search for a date value.
And in the next topic, you'lllearn how to ignore date components when you
search for time values.
Before I go on, you should realize that the problems described here can
sometimes be avoided by designing the database differently. For example, if you
don't need to include a time component, you can use the date data type for the
column that stores the date. That way, there's no time component to complicate
your date searches. Conversely, if you don' t need to include a date component,
you can use the time data type for the column that holds the time. Of course,
since these data types were introduced with SQL Server 2008, you'll need to use
a different technique for prior versions of SQL Server.
Figure 9-8 illustrates the problem you can encounter when searching for
dates. The examples in this figure use a table named DateSample. This table
includes an ID column that contains an integer value and a StartDate column that
contains a datetime value. Notice that the time components in the first three rows
in this table have a zero value. By contrast, the time components in the next three
rows have non-zero time components.
The problem occurs when you try to search for a date value. The first
SELECT statement in this figure, for example, searches for rows in the
DateSample table with the date 2019-10-28. Because a time component isn' t
specified, a zero time component is added when the date string is converted to
a datetime value. However, because the row with this date has a non-zero time
value, no rows are returned by this statement.
To solve this problem, you can use one of the five techniques shown in this
figure. Of these techniques, the first technique is usually the easiest to use. Here,
you use the CONVERT function to convert the datetime value to a date value. Of
course, this only works for SQL Server 2008 or later.
As a result, if you need to work with a prior version of SQL Server, you'll
need to use one of the other techniques. For example, you can use the second
technique to search for dates that are greater than or equal to the date you're
looking for and less than the date that follows the date you're looking for. Or,
you can use the third technique to search for the values that are returned by the
MONTH, DAY, and YEAR functions.
The fourth technique is to use the CAST function to convert the value in the
StartDate column to an 11-character string. That causes the time portion of the
date to be truncated (if you look back at figure 8-7, you '11 see that when a
date/time data type is cast to a string data type, the date portion contains 11
characters in the format "Mon dd yyyy"). Then, the string is converted back to a
datetime value, which adds a zero time component.
The last technique is similar, but it uses the CONVERT function instead of
the CAST function. The style code used in this function converts the datetime
value to a 10-character string that doesn' t include the time. Then, the string is
converted back to a date with a zero time component.
Chapter 9 How to use functions 281
A SELECT statement that searches for month, day, and year components
SELECT * FROM DateSample
WHERE MONTH(StartDate) = 10 AND
DAY(StartDate) = 28 AND
YEAR(StartDate) = 2019;
A SELECT statement that uses the CAST function to remove time values
SELECT * FROM DateSample
WHERE CAST(CAST(StartDate AS char(11)) AS datetime) = '2019-10-28';
A SELECT statement that uses the CONVERT function to remove time values
SELECT * FROM DateSample
WHERE CONVERT(datetime, CONVERT(char(10), StartDate, 110)) = '2019-10-28';
Description
• If you perform a search using a date string that doesn' t include the time, the date
string is converted implicitly to a date/time value with a zero time component.
Then, if the date columns you're searching have non-zero time components, you
have to accommodate the times in the search condition.
Note that the second technique (searching for a range of dates) is the only
technique that doesn't use any functions in the WHERE clause. Because of that,
this is the most efficient technique for searching for dates. As a result, you may
want to use it even if you're using SQL Server 2008 or later.
A SELECT statement that removes date values (prior to SQL Server 2008)
SELECT * FROM DateSample
WHERE CONVERT(datetime , CONVERT(char(12 ) , StartDate , 8)) >= '09:00 : 00 ' AND
CONVERT(datetime , CONVERT(char(12 ) , StartDate, 8) ) < '12:59:59 : 999';
Description
• If you perform a search using a date string that includes only a time, the date is
converted implicitly to a date/time value with a default date component of
1900-01-01. Then, if the date columns you're searching have other dates, you have
to accommodate those dates in the search condition.
Description
• The simple CASE function tests the expression in the CASE clause against the
expressions in the WHEN clauses. Then, the function returns the result expression
associated with the first test that results in an equal condition.
• The searched CASE function tests the conditional expression in each WHEN clause in
sequence and returns the result expression for the first condition that evaluates to true.
'
Description
• The IIF and CHOOSE functions are known as logical functions, and they were
introduced with SQL Server 2012.
• The IIF function lets you test an expression and return one value if the expression
is true and another value if the expression is false. It provides a shorthand way of
coding a searched CASE function with a single WHEN clause and an ELSE clause.
• The CHOOSE function provides an index into a list of values. The index value
must be a type that can be converted to an int value and it must range from 1 to the
number of values in the list.
Description
• The COALESCE and ISNULL functions let you substitute non-null values for null
values.
• The COALESCE function returns the first expression in a list of expressions that
isn' t null. All of the expressions in the list must have the same data type. If all of
the expressions are null, this function returns a null value.
• The ISNULL function returns the expression if it isn' t null. Otherwise, it returns
the value you specify. The expression and the value must have the same data type.
• COALESCE is not an ANSI-standard function, but it's more widely supported than
ISNULL, which is unique to SQL Server.
Description
• You can use the GROUPING function to determine when a null value is assigned to
a column as the result of the ROLLUP or CUBE operator. The column you name in
this function must be one of the columns named in the GROUP BY clause.
• If a null value is assigned to the specified column as the result of the ROLLUP or
CUBE operator, the GROUPING function returns a value of 1. Otherwise, it returns
a value ofO.
• You typically use the GROUPING function with the CASE function. Then, if the
GROUPING function returns a value of 1, you can assign a value other than null to
the column.
Description
• The ROW _NUMBER, RANK, DENSE_RANK, and NTILE functions are known
as ranking functions.
• The ROW _NUMBER function returns the sequential number of a row within a
partition of a result set, starting at 1 for the first row in each partition.
• The ORDER BY clause of a ranking function specifies the sort order in which the
ranking function is applied.
• The optional PARTITION BY clause of a ranking function specifies the column
that's used to divide the result set into groups.
The third example shows how the RANK and DENSE_RANK functions
work. You can use these functions to rank the rows in a result set. In this
example, both the RANK and the DENSE_RANK functions sort all invoices in
the Invoices table by the invoice total. Since the first three rows have the same
invoice total, both of these functions give these three rows the same rank, 1.
However, the fourth row has a different value. To calculate the value for this row,
the RANK function adds 1 to the total number of previous rows. In other words,
since the first three rows are tied for first place, the fourth row gets fourth place
and is assigned a rank of 4.
The DENSE_RANK function, on the other hand, calculates the value for the
fourth row by adding 1 to the rank for the previous row. As a result, this function
assigns a rank of 2 to the fourth row. In other words, since the first three rows are
tied for first place, the fourth row gets second place.
The fourth example shows how the NTILE function works. You can use
this function to divide the rows in a partition into the specified number of
groups. When the rows can be evenly divided into groups, this function is easy
to understand. For example, if a result set returns 100 rows, you can use the
NTILE function to divide this result set into 10 groups of 10. However, when the
rows can' t be evenly divided into groups, this function is a little more difficult
to understand. In this figure, for example, the NTILE function is used to divide
a result set that contains 5 rows. Here, the first NTILE function divides this
result into 2 groups with the first having 3 rows and the second having 2 rows.
The second NTILE function divides this result set into 3 groups with the first
having 2 rows, the second having 2 rows, and the third having 1 row. And so on.
Although this doesn' t result in groups with even numbers of rows, the NTILE
function creates the number of groups specified by its argument.
In this figure, the examples for the RANK, DENSE_RANK, and NTILE
functions don' t include PARTITION BY clauses. As a result, these functions are
applied to the entire result set. However, whenever necessary, you can use the
PARTITION BY clause to divide the result set into groups just as shown in the
second example for the ROW_NUMBER function.
Chapter 9 How to use functions 295
Description
• The RANK and DENSE_RANK functions both return the rank of each row within
the partition of a result set.
• If there is a tie, both of these functions give the same rank to all rows that are tied.
• To determine the rank for the next distinct row, the RANK function adds 1 to the
total number of rows, while the DENSE_RANK function adds 1 to the rank for the
previous row.
Description
• The NTILE function divides the rows in a partition into the specified number of
groups.
• If the rows can't be evenly divided into groups, the later groups may have one less
row than the earlier groups.
{PERCENT_ RANK () ICUME_ DIST} OVER ( [pa rti t i on_ by_ cla use] order_ by_ clause )
Description
• The FIRST_VALUE, LAST_VALUE, LEAD, LAG, PERCENT_RANK,
CUME_DIST, PERCENTILE_CONT, and PERCENTILE_DISC functions are
known as analytic functions. They were introduced with SQL Server 2012.
• The FIRST_VALUE function returns the first value in a sorted set of values, and
the LAST_ VALUE function returns the last value in a sorted set of values. When
you use the PARTITION BY clause with LAST_VALUE, you typically include the
ROWS or RANGE clause as well.
The LEAD and LAG functions let you refer to values in other rows of the
result set. The LAG function is illustrated in the second example in this figure.
Here, the OVER clause is used to group the result set by rep ID and sort it by
year. Then, the LAG function in the fourth column gets the sales total from one
row prior to the current row (the offset). Since the rows are sorted by year for
each sales rep, that means that the function retrieves the sales rep's sales for
the previous year. The fifth column uses the same function, but it subtracts the
result of this function from the current sales to show the change in sales from
the previous year. The LEAD function is similar, but it lets you refer to values in
following rows rather than preceding rows.
Notice that the value of the LAG function for the first row for each sales rep
is 0.00. That's because there isn' t a row for the prior year. By default, this value
is NULL. Because I wanted to calculate the change for each row in the result set,
though, I set the third argument of the LAG function (default) to 0.
The third example in this figure shows how to use all four of the remaining
functions. Each of these functions groups the rows by year and sorts them by
sales total in ascending sequence. Notice, though, that the ORDER BY clause
for the PERCENTILE_CONT and PERCENTILE_DISC functions isn' t
specified on the OVER clause. Instead, it's specified on the WITHIN GROUP
clause, which, unlike the OVER clause, allows for the result set to be sorted only
by a single column.
The PERCENT_RANK function calculates a percent that indicates the rank
of each row within a group. The result of this function is always a value between
0 and 1. If you study the results in this example, you shouldn't have any trouble
understanding how this function works.
The CUME_DIST function is similar, but it calculates the percent of values
that are less than or equal to the current value. This function represents the
cumulative distribution of the values. The cumulative distribution is calculated
by dividing the number of rows with the current value or a lower value by the
total number of rows in the group.
The PERCENTILE_CONT and PERCENTILE_DISC functions calculate
the value at the percentile you specify. The difference between these two
functions is that PERCENTILE_CONT is based on a continuous distribution of
values, and PERCENTILE_DISC is based on a discrete distribution of values.
This means that the value returned by PERCENTILE_CONT doesn' t need to
appear in the result set, but the value returned by PERCENTILE_DISC does.
In this example, these functions are used to calculate the median of the
sales totals for each year (the value in the middle). Because there are an odd
number of rows for 2017 and 2018, both functions return the value in the middle
row. Because there are an even number of rows for 2019, though, there isn' t
a middle value. In that case, the PERCENTILE_CONT function calculates
the median by adding the two middle values together and dividing by 2. As
you can see, the resulting value doesn't exist in the result set. By contrast, the
PERCENTILE_DISC function uses the CUME_DIST function to identify the
row with a cumulative distribution of .5 (the same percentile specified by the
PERCENTILE_DISC function), and it uses the value of that row as the result.
Chapter 9 How to use functions 299
Description
• The LEAD function retrieves data from a subsequent row in a result set, and the LAG
function retrieves data from a previous row in a result set.
• The PERCENT_RANK function calculates the rank of the values in a sorted set of
values as a percent. The CUME_DIST function calculates the percent of the values in a
sorted set of values that are less than or equal to the current value.
• The PERCENTILE_CONT and PERCENTILE_DISC functions calculate the value at
the specified percentile for a sorted set of values. PERCENTILE_CONT returns an exact
percentile, and PERCENTILE_DIST returns a value that exists in the sorted column.
Perspective
In this chapter, you learned about many of the functions that you can use
to operate on SQL Server data. At this point, you have all of the essential skills
you need to develop SQL code at a professional level.
However, there's a lot more to learn about SQL Server. In the next section
of this book, then, you'lllearn the basic skills for designing a database. Even
if you never need to design your own database, understanding this material
will help you work more efficiently with databases that have been designed by
others.
Terms
logical functions cumulative distribution
ranking functions continuous distribution
analytic functions discrete distribution
Exercises
1. Write a SELECT statement that returns two columns based on the Vendors
table. The first column, Contact, is the vendor contact name in this format:
first name followed by last initial (for example, "John S.") The second
column, Phone, is the VendorPhone column without the area code. Only
return rows for those vendors in the 559 area code. Sort the result set by first
name, then last name.
2. Write a SELECT statement that returns the lnvoiceNumber and balance due
for every invoice with a non-zero balance and an InvoiceDueDate that's less
than 30 days from today.
3. Modify the search expression for InvoiceDueDate from the solution for
exercise 2. Rather than 30 days from today, return invoices due before the last
day of the current month.
4. Write a summary query that uses the CUBE operator to return LineltemSum
(which is the sum of InvoiceLineltemAmount) grouped by Account (an alias
for AccountDescription) and State (an alias for VendorState). Use the CASE
and GROUPING function to substitute the literal value "*ALL*" for the
summary rows with null values.
5. Add a column to the query described in exercise 2 that uses the RANK()
function to return a column named BalanceRank that ranks the balance due in
descending order.
Section 3
Database design
and implementation
In large programming shops, database administrators are usually
responsible for designing the databases that are used by production
applications, and they may also be responsible for the databases that are
used for testing those applications. Often, though, programmers are asked
to design, create, or maintain small databases that are used for testing. And
in a small shop, programmers may also be responsible for the production
databases.
So whether you're a database administrator or a SQL programmer,
you need the skills and knowledge presented in this section. That's true
even if you aren't ever called upon to design or maintain a database. By
understanding what's going on behind the scenes, you'll be able to use
SQL more effectively.
So, in chapter 10, you'lllearn how to design a SQL Server database.
In chapter 11, you'll learn how to use the Data Definition Language (DDL)
statements to create and maintain the SQL Server objects of a database.
And in chapter 12, you'lllearn how to use the Management Studio to do
the same tasks.
10
How to design a database
In this chapter, you'll learn how to design a new database. This is useful
information for the SQL programmer whether or not you ever design a
database on your own. To illustrate this process, I'll use the accounts payable
(AP) system that you've seen throughout this book because that will make it
easier for you to understand the design techniques.
Documents
Columns
Facilities
Other
systems
Description
• A relational database system should model the real-world environment where it's
used. The job of the designer is to analyze the real-world system and then map it
onto a relational database system.
• A table in a relational database typically represents an object, or entity, in the real
world. Each column of a table is used to store an attribute associated with the
entity, and each row represents one instance of the entity.
• To model a database and the relationships between its tables after a real-world
system, you can use a technique called entity-relationship (ER) modeling. Some of
the diagrams you'll see in this chapter apply the basic elements ofER modeling.
Description
• Depending on the nature of the system, you can identify data elements in a variety
of ways, including interviewing users, analyzing existing systems, and evaluating
comparable systems.
• The documents used by a real-world system, such as the invoice shown above, can
often help you identify the data elements of the system.
• As you identify the data elements of a system, you should begin thinking about
the entities that those elements are associated with. That will help you identify the
tables of the database later on.
+
Vendor sales contact first name Vendor sales contact last name
Ruben Goldberg
An address that's divided into street address, city, state, and zip code
Vendor address
1234 West Industrial Way, East Los Angeles, Californ ia 90022
+
Street and number City
1234 West Industrial Way East Los Angeles California 90022
Description
• If a data element contains two or more components, you should consider
subdividing the element into those components. That way, you won't need to parse
the element each time you use it.
• The extent to which you subdivide a data element depends on how it will be used.
Because it's difficult to predict all future uses for the data, most designers subdivide
data elements as much as possible.
• When you subdivide a data element, you can easily rebuild it when necessary by
concatenating the individual components.
Description
• After you identify and subdivide all of the data elements for a database, you should
group them by the entities with which they're associated. These entities will later
become the tables of the database, and the elements will become the columns.
• If a data element relates to more than one entity, you can include it under all of the
entities it relates to. Then, when you normalize the database, you may be able to
remove the duplicate elements.
• As you assign the elements to entities, you should omit elements that aren' t needed,
and you should add any additional elements that are needed.
Description
• Most tables should have a primary key that uniquely identifies each row. If
necessary, you can use a composite key that uses two or more columns to uniquely
identify each row.
• The values of the primary keys should seldom, if ever, change. The values should
also be short and easy to enter correctly.
• If a suitable column doesn't exist, you can create an identity column that can be
used as the primary key.
• If two tables have a one-to-many relationship, you may need to add a foreign key
column to the table on the "many" side. The foreign key column must have the
same data type as the primary key column it's related to.
• If two tables have a many-to-many relationship, you'll need to define a linking
table to relate them. Then, each of the tables in the many-to-many relationship will
have a one-to-many relationship with the linking table. The linking table doesn't
usually have a primary key.
• If two tables have a one-to-one relationship, they should be related by their primary
keys. This type of relationship is typically used to improve performance. Then,
columns with large amounts of data can be stored in a separate table.
connecting table or an associate table. This table contains the primary key
columns from the two tables. Then, each table has a one-to-many relationship
with the linking table. Notice that the linking table doesn' t have its own primary
key. Because this table doesn' t correspond to an entity and because it's used only
in conjunction with the Employees and Committees tables, a primary key isn' t
needed.
The third example in figure 10-5 illustrates two tables that have a one-to-one
relationship. With this type of relationship, both tables have the same primary
key, which means that the information could be stored in a single table. This
type of relationship is often used when a table contains one or more columns
with large amounts of data. In this case, the EmployeePhotos table contains a
large binary column with a photo of each employee. Because this column is used
infrequently, storing it in a separate table will make operations on the Employees
table more efficient. Then, when this column is needed, it can be combined with
the columns in the Employees table using a join.
Description
• Referential integrity means that the relationships between tables are maintained
correctly. That means that a table with a foreign key doesn' t have rows with foreign
key values that don' t have matching primary key values in the related table.
• In SQL Server, you can enforce referential integrity by using declarative referential
integrity or by defining triggers.
• To use declarative referential integrity (DR/), you define foreign key constraints.
You' lllearn how to do that in the next two chapters.
• When you define foreign key constraints, you can specify how referential integrity
is enforced when a row is deleted from the primary key table. The options are to
return an error or to delete the related rows in the foreign key table.
• You can also specify how referential integrity is enforced when the primary key of
a row is changed and foreign key constraints are in effect. The options are to return
an error or to change the foreign keys of all the related rows to the new value.
• If referential integrity isn' t enforced and a row is deleted from the primary key
table that has related rows in the foreign key table, the rows in the foreign key table
are said to be orphaned.
• The three types of errors that can occur when referential integrity isn' t enforced are
called the deletion anomaly, the insertion anomaly, and the update anomaly.
TermsiD
Terms Description
TermsDueDays
Description
• Normalization is a formal process you can use to separate the data in a data
structure into related tables. Normalization reduces data redundancy, which can
cause storage and maintenance problems.
• In an unnormalized data structure, a table can contain information about two or
more entities. It can also contain repeating columns, columns that contain repeating
values, and data that's repeated in two or more rows.
• In a normalized data structure, each table contains information about a single
entity, and each piece of information is stored in exactly one place.
• To normalize a data structure, you apply the normal forms in sequence. Although
there are a total of seven normal forms, a data structure is typically considered
normalized if the first three normal forms are applied.
Description
• An index provides a way for SQL Server to locate information more quickly. When
it uses an index, SQL Server can go directly to a row rather than having to search
through all the rows until it finds the ones you want.
• An index can be either clustered or nonclustered. Each table can have one clustered
index and up to 249 nonclustered indexes.
• The rows of a table are stored in the sequence of the clustered index. By default,
SQL Server creates a clustered index for the primary key. If you don' t identify a
primary key, the rows of the table are stored in the order in which they're entered.
• Indexes speed performance when searching and joining tables. However, they can' t
be used in search conditions that use the LIKE operator with a pattern that starts
with a wildcard. And they can' t be used in search conditions that include functions
or expressiOns.
• You can create composite indexes that include two or more columns. You should
use this type of index when the columns in the index are updated infrequently or
when the index will cover almost every search condition on the table.
• Because indexes must be updated each time you add, update, or delete a row, you
shouldn' t create more indexes than you need.
Description
• Each normal form assumes that the design is already in the previous normal form.
• A database is typically considered to be normalized if it is in third normal form.
The other four forms are not commonly used and are not covered in detail in this
book.
Description
• For a table to be in first normal form, its columns must not contain repeating
values. Instead, each column must contain a single, scalar value. In addition, the
table must not contain repeating columns that represent a set of values.
• A table in first normal form often has repeating values in its rows. This can be
resolved by applying the second normal form.
"'""'
The invoice data in second normal form
lnvoiceNumber VendorName lnvoiceiD ~
1 r·1"1"2897·····················i Cahners Publishng 1
~.............................. ..........;
~
Description
• For a table to be in second normal form, every non-key column must depend on
the entire primary key. If a column doesn' t depend on the entire key, it indicates
that the table contains information for more than one entity. This is reflected by the
table 's composite key.
• To apply second normal form, you move columns that don' t depend on the entire
primary key to another table and then establish a relationship between the two
tables.
• Second normal form helps remove redundant row data, which can save storage
space, make maintenance easier, and reduce the chance of storing inconsistent data.
Term siD
Terms Description
Terms Due Days
Description
• For a table to be in third normal form, every non-key column must depend only on
the primary key.
• If a column doesn't depend only on the primary key, it implies that the column is
assigned to the wrong table or that it can be computed from other columns in the
table. A column that can be computed from other columns contains derived data.
When to denormalize
• When a column from a joined table is used repeatedly in search criteria, you should
consider moving that column to the primary key table if it will eliminate the need
for a join.
• If a table is updated infrequently, you should consider denormalizing it to improve
efficiency. Because the data remains relatively constant, you don't have to worry
about data redundancy errors once the initial data is entered and verified.
• Include columns with derived values when those values are used frequently in
search conditions. If you do that, you need to be sure that the column value is
always synchronized with the value of the columns it's derived from.
Description
• Data structures that are normalized to the fourth normal form and beyond typically
require more joins than tables normalized to the third normal form.
• Most designers denormalize data structures to some extent, usually to the third
normal form.
• Denormalization can result in larger tables, redundant data, and reduced
performance.
• Only denormalize when necessary. It is better to adhere to the normal forms unless
it is clear that performance will be improved by denormalizing.
Perspective
Database design is a complicated subject. Because of that, it's impossible
to teach you everything you need to know in a single chapter. With the skills
you've learned in this chapter, however, you should now be able to design
simple databases of your own. More important, you should now be able to
evaluate the design of any database that you work with. That way, you can be
sure that the queries you code will be as efficient and as effective as possible.
One aspect of database design that isn't covered in this chapter is
designing the security of the database. Among other things, that involves
creating login IDs and database users and assigning permissions. It may also
involve organizing the tables and other objects in the database into two or
more schemas. You' lllearn more about how to implement database security in
chapter 17.
Terms
data structure deletion anomaly
entity normalization
attribute data redundancy
instance unnormalized data structure
entity-relationship (ER) modeling normalized data structure
CASE (computer-aided software normal forms
engineering) index
composite key table scan
linking table clustered index
connecting table nonclustered index
associate table composite index
referential integrity covering index
declarative referential integrity Boyce-Codd normal form
(DRI) transitive dependency
foreign key constraints multivalued dependency
triggers domain-key normal form
orphaned row derived data
update anomaly denormalized data structure
insertion anomaly denormalization
Chapter 10 How to design a database 331
Exercises
1. Design a database diagram for a product orders database with four tables.
Indicate the relationships between tables and identify the primary key and
foreign keys in each table. Explain your design decisions.
Customers Orders Orderlineltems
CustomeriD OrdersiD OrderiD
CustomerName CustomeriD OrderSequence
CustomerAddress OrderDate ProductiD
CustomerPhone ShipAddress Quantity
Ship Date UnitPrice
Products
Producti D
ProductName
QtyPerUnit
UnitPrice
lnStock
OnOrder
2. Add the two tables below into the design for exercise 1. Create additional
tables and columns, if necessary. Explain your design decisions.
Shippers Employees
ShipperiD EmployeeiD
ShipperName FirstName
ShipperAddress LastName
ShipperPhone SSN
Hire Date
3. Modify your design for exercise 2 to identify the columns that should be
indexed, and explain your decision.
4. Design a database diagram that allows individuals to be assigned membership
in one or more groups. Each group can have any number of individuals and
each individual can belong to any number of groups. Create additional tables
and columns, if necessary. Explain your design decisions.
5. Modify your design for exercise 4 to keep track of the role served by each
individual in each group. Each individual can only serve one role in each
group. Each group has a unique set of roles that members can fulfill. Create
additional tables and columns, if necessary. Explain your design decisions.
11
How to create a database
and its tables with SQL
statements
Now that you've learned how to design a database, you're ready to learn how to
implement your design. To do that, you use the set of SQL statements that are
known as the data definition language (DOL). As an application programmer,
you can use the DDL statements to create and modify the database objects
such as tables and sequences that you need for testing. Beyond that, knowing
what these statements do will give you a better appreciation for how a database
works.
An introduction to DDL
All of the SQL statements that you' ve seen so far have been part of the data
manipulation language, or DML. But now, you'lllearn how to use the SQL
statements that are part of the data definition language. You use these statements
to define the objects of a database.
Description
• You use the data definition language (DDL) statements to create, modify, and
delete database objects such as the database itself, the tables contained in a
database, and the indexes for those tables.
• Typically, a database administrator is responsible for using the DDL statements on
production databases in a large database system. However, every SQL programmer
should be comfortable using these statements so they can create and work with
small databases for testing.
• In most cases, you' ll use the graphical user interface of the Management Studio
to create and maintain database objects as described in chapter 12. Although the
Management Studio generates DDL statements for you, you may need to verify or
correct these statements. To do that, you need to understand their syntax and use.
• If you use a SQL database other than SQL Server, it may not have a graphical tool
for managing database objects. In that case, you must use the DDL statements.
Description
• The name of an object in SQL Server is called its identifier. Most objects are
assigned an identifier when they're created. Then, the identifier can be used to refer
to the object.
• SQL Server provides for two classes of identifiers. Regular identifiers follow the
formatting rules for identifiers. Delimited identifiers are enclosed in brackets ([]) or
double quotation marks("") and may or may not follow the formatting rules. If an
identifier doesn' t follow the formatting rules, it must be delimited.
• An identifier can contain from 1 to 128 characters.
• An at sign (@) at the beginning of an identifier indicates that the identifier is a local
variable or parameter, a number sign (#) indicates that the identifier is a temporary
table or procedure, and two number signs (##) indicates that the identifier is a
global temporary object. See chapters 14 and 15 for details.
Description
• The CREATE DATABASE statement creates a new, empty database on the current
server. Although the ANSI standards don' t include this statement, it's supported by
virtually all SQL database systems. The optional clauses shown here, however, are
supported only by SQL Server.
• If you code this statement without any options, the new database is created using
the default settings and the database files are stored in the default directory on the
hard drive. For most small database projects, these settings are acceptable.
• One of the files SQL Server creates when it executes the CREATE DATABASE
statement is a transaction log file. This file is used to record modifications to the
database. SQL Server generates the name for this file by appending "_log" to the
end of the database name. The database name is limited to 123 characters.
• If you have a copy of a database file that you'd like to work with on your server,
you can use the FOR ATTACH clause in addition to the ON PRIMARY clause to
attach the fi le as a database to the current server.
• Most of the optional clauses that have been omitted from this syntax are used to
specify the underlying file structure of the database. These clauses are used by
DBAs to tune the performance of the database. See the SQL Server documentation
for details.
Warning
• On some systems, the CREATE DATABASE statement can overwrite an existing
database. Because of that, you'll want to check with the DBA before using this
statement.
Description
• The CREATE TABLE statement creates a table based on the column definitions,
column attributes, and table attributes you specify. A database can contain as many
as two billion tables.
• A table can contain between one and 1,024 columns. Each column must have a
unique name and must be assigned a data type. In addition, you can assign one or
more of the column attributes shown above.
• You can also assign one or more constraints to a column or to the entire table. See
figures 11-7, 11-8, and 11-9 for details.
• For the complete syntax of the CREATE TABLE statement, refer to the SQL Server
documentation.
values, you should only use it when a column contains a high percentage of
null values. As a general guideline, it usually makes sense to use the SPARSE
attribute when at least 60% of the column's values are null.
Description
• You use the CREATE INDEX statement to create an index for a table. An index can
improve performance when SQL Server searches for rows in the table.
• SQL Server automatically creates a clustered index for a table's primary key. If
that's not what you want, you can drop the primary key constraint using the ALTER
TABLE statement shown in figure 11-11 and then recreate the primary key with a
nonclustered index.
• Each table can have a single clustered index and up to 999 nonclustered indexes.
SQL Server automatically creates a nonclustered index for each unique key other
than the primary key.
• By default, an index is sorted in ascending sequence. If that's not what you want,
you can code the DESC keyword. The sequence you use should be the sequence in
which the rows are retrieved most often when using that index.
• A full-table index is an index that applies to every row in the table.
• A filtered index is a type of nonclustered index that includes a WHERE clause that
filters the rows that are included in the index. Filtered indexes were introduced with
SQL Server 2008.
• A filtered index can improve performance when the number of rows in the index is
small compared to the total number of rows in the table.
• For more details about working with filtered indexes, refer to the SQL Server
documentation.
Function .....
Index
Login
Role
Schema
Stored Procedure
Synonym
Table
Trigger
100% •
100% •
l
'Ud! ,
column 2 int L~~
);
100% •
Description
• Transact-SQL snippets help you write statements for creating database objects.
• To insert a Transact-SQL snippet, right-click in the Query Editor window and select
the Insert Snippet command from the resulting menu. Then, use the snippet picker
to select a snippet.
• To select a snippet using the snippet picker, double-click on the folder for the
object you want to create, then double-click on a snippet in the list that's displayed.
Alternatively, you can select the folder or snippet and then press the Tab or Enter
key.
• Once a snippet has been inserted, you can replace the highlighted portions with
your own code and add any other required code. To move from one highlighted
portion of code to the next, press the Tab key. To move to the previous highlighted
portion, press the Shift+Tab keys.
An introduction to constraints
Figure 11-7 summarizes the five types of constraints provided by SQL
Server. Except for NOT NULL, each of these constraints can be coded at either
the column level or the table level. You've already seen how to code a primary
key constraint at the column level, and you can code a unique key constraint in
the same way. Now, the first example in this figure shows how to code a primary
key constraint at the table level.
In this example, the primary key consists of two columns. Because of that,
it can't be defined at the column level. Notice that when you code a constraint
at the table level, you must code a comma at the end of the preceding column
definition. If you don't, SQL Server will try to associate the constraint with the
preceding column, and an error will result.
Two types of constraints you haven' t seen yet are check constraints and
foreign key constraints. You' ll learn more about these types of constraints in the
next two topics. To illustrate the difference between column-level constraints
and table-level constraints, however, the second and third examples in this
figure show two ways you can code the same two check constraints. The first
example uses column-level constraints to limit the values in the InvoiceTotal
and PaymentTotal columns to numbers greater than or equal to zero. The second
example uses a compound condition to specify both constraints at the table level.
Although the first technique is preferred, the second example illustrates that a
table-level constraint can refer to any of the columns in a table. By contrast, a
column-level constraint can refer only to the column that contains the constraint.
Chapter 11 How to create a database and its tables with SQL statements 347
The same statement with the check constraints coded at the table level
CREATE TABLE Invoices2
(InvoiceiD INT NOT NULL IDENTITY PRIMARY KEY,
InvoiceTotal MONEY NOT NULL,
PaymentTotal MONEY NOT NULL DEFAULT 0,
CHECK ((InvoiceTotal >= 0) AND (PaymentTotal >= 0)));
Description
• Constraints are used to enforce the integrity of the data in a table by defining rules about
the values that can be stored in the columns of the table. Constraints can be used at the
column level to restrict the value of a single column or at the table level to restrict the
value of one or more columns.
• You code a column-level constraint as part of the definition of the column it constrains.
You code a table-level constraint as if it were a separate column definition, and you name
the columns it constrains within that definition.
• Constraints are tested before a new row is added to a table or an existing row is updated.
If the new or modified row meets all of the constraints, the operation succeeds. Otherwise,
an error occurs and the operation fails.
Description
• Check constraints limit the values that can be stored in the columns of a table.
• The condition you specify for a check constraint is evaluated as a Boolean expres-
sion. If the expression is true, the insert or update operation proceeds. Otherwise, it
fails.
• A check constraint that's coded at the column level can refer only to that column. A
check constraint that's coded at the table level can refer to any column in the table.
Description
• You use the FOREIGN KEY clause to define a foreign key constraint, also called a
reference constraint. A foreign key constraint defines the relationship between two
tables and enforces referential integrity.
• A foreign key constraint that's coded at the column level can only relate a single column
in the new table to a single column in the related table. A constraint that's coded at the
table level can relate two tables by two or more columns.
• Typically, a foreign key constraint refers to the primary key of the related table. However,
it can also refer to a unique key.
• The ON DELETE clause specifies what happens to rows in the table if the row in the
related table with the same key value is deleted. The ON UPDATE clause specifies what
happens to rows in the table if the key of the related row is updated.
• The CASCADE keyword causes the rows in this table to be deleted or updated to match
the row in the related table. This is known as a cascading delete or a cascading update.
• The NO ACTION keyword prevents the row in the related table from being deleted
or updated and causes an error to be raised. This is usually the preferred option.
Description
• You can use the DROP INDEX statement to delete one or more indexes from one
or more tables in any database on the current server.
• You can use the DROP TABLE statement to delete one or more tables from any
database on the current server. To delete a table from a database other than the
current database, you must qualify the table name with the database name.
• You can use the DROP DATABASE statement to delete one or more databases
from the current server.
• You can' t delete a table if a foreign key constraint in another table refers to that
table.
• When you delete a table, all of the data, indexes, triggers, and constraints are
deleted. Any views or stored procedures associated with the table must be deleted
explicitly.
• You can't delete an index that's based on a primary key or unique key constraint. To
do that, you have to use the ALTER TABLE statement. See figure 11-11 for details.
Warnings
• You can' t undo a delete operation. For this reason, you may want to back up the
database before you use any of these statements so you can restore it if necessary.
• You should never use these statements on a production database without first
consulting the DBA.
Description
• You use the ALTER TABLE statement to modify an existing table. You can use this
statement to add columns or constraints, drop columns or constraints, or change the
definition of an existing column, including changing the column's data type.
• Before SQL Server changes the data type of a column, it checks to be sure that no
data will be lost. If it will, the operation isn't performed.
• You can modify a column to allow null values as long as the column isn' t defined
as the primary key. You can modify a column so it doesn' t allow null values as long
as none of the existing rows contain null values in that column.
• You can add a column that doesn't allow null values only if you specify a default
value for that column.
• To delete a constraint, you must know its name. If you let SQL Server generate the
name for you, you can use the Management Studio as shown in the next chapter to
look up the name.
• By default, SQL Server verifies that existing data satisfies a new check or foreign
key constraint. If that's not what you want, you can code the WITH NOCHECK
keywords.
Warning
• You should never alter a table in a production database without first consulting the DBA.
Description
• You use the CREATE SEQUENCE statement to generate integer values for a
column in one or more tables.
• By default, the CREATE SEQUENCE statement creates a sequence with the bigint
data type that starts with the minimum value for the data type, is incremented by
a value of 1, has minimum and maximum values based on the data type, doesn' t
specify the cache size, and doesn't restart the sequence when the end is reached.
• You can use the NEXT VALUE FOR function to get the next value in the sequence.
• You can query the sys.sequences table to get information about a sequence.
gets the next value from the sequence named TestSequence3 so it can be inserted
into the SequenceNo column.
The last example in figure 11-12 shows how to use the sys.sequences catalog
view to get information about a sequence. You'lllearn about catalog views in
chapter 13. For now, just realize that the SELECT statement shown here retrieves
the current value for the sequence from this view. If you review the starting value
and the increment for the sequence, you'll see how this works.
Description
• You can use the DROP SEQUENCE statement to delete a sequence. A sequence
can't be deleted if it's used as the default value for a column.
• You can use the ALTER SEQUENCE statement to alter the attributes of a
sequence. However, you can't change the data type, and you can't set the minimum
and maximum values so they don't make sense based on existing values.
• If you omit the WITH value of the RESTART clause, the sequence is restarted
based on the current definition of the sequence.
An introduction to encodings
A character set refers to a set of characters and their numeric codes. An
encoding refers to the representation of these numeric codes. For example,
Unicode is a character set that defines numeric codes for characters from most of
the world's writing systems, and UTF-8 and UTF-16 are encodings of Unicode
that provide two different ways to represent these characters.
Figure 11-14 begins by presenting four encodings that are commonly used
by SQL Server. The Latini encoding, technically known as ISO/IEC 8859-1,
is the default encoding used for the char and varchar types. It uses 1 byte per
character to represent 256 of the most commonly used characters in Western
European languages. That includes the 128 characters in the older ASCJ/
character set.
The UCS-2 encoding is the default encoding used for the nchar and nvarchar
types. It uses 2 bytes per character and can be used to represent the first 65,536
Unicode characters.
SQL Server 2012 introduced support for the supplementary characters
provided by Unicode that go beyond the original 65,536 characters. To provide
for these characters in nchar and nvarchar types, SQL Server uses the UTF-16
encoding. This encoding uses 4 bytes per character to store supplementary
characters such as emojis.
SQL Server 2019 introduced support for the UTF-8 encoding, which can be
used to store Unicode characters, including the supplementary characters, in char
and varchar data types. This encoding requires from 1 to 4 bytes per character
and is widely used when working with the Internet.
The second table in this figure should help you understand the differences in
the bytes required to store characters using the three Unicode encodings. To store
an ASCII character, for example, UTF-8 requires 1 byte, but UCS-2 and UTF-16
require 2 bytes. On the other hand, to store Asian ideographs, UTF-8 requires 3
bytes but UCS-2 and UTF-16 require only two. Both UTF-8 and UTF-16 require
4 bytes to store supplementary characters, and these characters aren't supported
by UCS-2. This is important to keep in mind when you decide what collation to
use.
Chapter 11 How to create a database and its tables with SQL statements 361
Description
• Character sets and encodings map a numeric code to each character. In practice,
these terms are used interchangeably.
• One byte consists of eight bits, which can be combined in 256 different ways. Two
bytes, or 16 bits, can be combined in 65,536 different ways.
• Most systems use the same numeric codes for the first 128 characters. These are the
codes defined by the ASCII (American Standard Code for Information Interchange)
character set.
• The Latini (ISO/IEC 8859-1) encoding starts with the 128 ASCII characters and
adds another 128 characters commonly used by Western Europe languages.
• Unicode begins by defining 65,536 Unicode characters from most of the world's
writing systems. The first 256 ofthese characters are the ones in the Latin1 character set.
• To work with Unicode characters, you use an encoding such as UCS-2, UTF-8, or
UTF-16. All three of these encodings allow you to access the first 65,536 Unicode
characters.
• Unicode also defines supplementary characters that go beyond the first 65,536
characters. SQL Server 2012 and later provide support for these characters, and you
can work with them using either the UTF-8 encoding with the char or varchar data
type (SQL Server 2019 or later) or the UTF-16 encoding with the nchar or nvarchar
data type.
• The Latini and UCS-2 encodings are known as fixed-length encodings, since they
always use the same number of bytes per character.
• The UTF-8 and UTF-16 encodings are known as variable-length encodings, since
the number of bytes per character varies depending on the character.
An introduction to collations
A collation determines how character data is stored in a server, database, or
column and how that data can be sorted and compared. You specify a collation
by coding the collation name, followed by one or more collation options.
SQL Server's default collation, for example, uses _Latini to specify that the
Latin1 encoding should be used for the char and varchar types and the UCS-2
encoding should be used for the nchar and nvarchar types. Then, the _CI option
specifies that sorting should be case-insensitive. This means that SQL Server
sorts uppercase letters such as A and lowercase letters such as a at the same
level, which is usually what you want. Finally, the _AS option specifies that
character accents are used when sorting. This means that Latin letters that have
accents such as A are not sorted at the same level as characters such as A that
don' t have accents, which is usually what you want.
Figure 11-15 begins by describing three collation sets supported by SQL
Server. Then, it describes some of the collation options. If SQL Server isn' t
storing or sorting characters the way you want, you can change the collation as
described in figure 11-17.
If you're creating a new database, for example, you probably want to use
a collation from the Windows set, not the SQL Server set. That's because the
collations in the Windows set are newer and provide sorting that's compatible
with Windows sorting. In most cases, you only want to use a collation from the
SQL Server set for backwards compatibility with existing SQL Server databases.
When sorting, you may want to include one or more of the sorting options.
To perform a case-sensitive sort, you can use a collation that includes the _CS
option. Similarly, to perform an accent-sensitive sort, you can use a collation
that includes the _AS option. Or, to perform a sort as quickly as possible, you
can use a collation that includes the _BIN or _BIN2 option. Here, bin stands for
binary, and it means that SQL Server sorts characters by their numeric codes.
This results in a case-sensitive and accent-sensitive sort.
When specifying the collation, the _SC and _UTF8 options change how
SQL Server stores data. To start, the _SC option allows supplementary
characters to be stored in the nchar and nvarchar types using UTF-16 encoding.
Similarly, using both the _SC and _UTF8 options allows Unicode characters,
including supplementary characters, to be stored in the char and varchar types
using UTF-8 encoding.
Note that if you use the _SC or the _SC and _UTF8 collation options, your
string data types may not be able to store as many characters as they would
without these options. That's because, as you learned in the last figure, many of
the Unicode characters require more than one byte with UTF-8 encoding, and all
of the supplementary characters require four bytes with both UTF-8 and UTF-16
encoding. When you use these collation options, then, you need to be sure to
declare string columns with the appropriate size. You' ll learn more about that in
a minute.
Chapter 11 How to create a database and its tables with SQL statements 363
Collation examples
The default server collation for the English (United States) locale
SQL_Latinl_General_CPl_CI_AS
Description
• A collation determines how character data is stored in a server, database, or column
and how that data can be sorted and compared. You specify a collation by coding the
collation name, followed by one or more collation options.
• For the Latin1_General_ IOO_CI_AS collation, the char and nchar types use the
1-byte Latin i character set, and the nchar and nvarchar types use the 2-byte UCS-2
encoding to access most of the Unicode characters.
• With SQL Server 2012 and later, you can use a collation that supports supplementary
characters. Then, the nchar and nvarchar types use UTF-16 encoding, not UCS-2.
• With SQL Server 2019 and later, you can use a collation that supports UTF-8
encoding. Then, the char and varchar types use UTF-8 encoding, not Latin 1.
Description
• You can use the COLLATE clause to set the collation at the database level, at the
column level, or for an expression in an ORDER BY clause.
• The default collation for a database is the collation for the server, and the default
collation for a column in a table is the collation for the database.
• You can use a binary collation only to specify that a binary sort is to be performed
on an expression.
• To use UTF-8 encoding, a collation must also provide for supplementary characters.
• If you switch from Latin 1 to UTF-8, you may need to quadruple the maximum
number of bytes for any char and varchar types impacted by the new collation.
Otherwise, these data types may not be able to support as many characters as they
did previously.
• If you switch from UCS-2 to UTF-16, you may need to double the maximum
number of bytes for any nchar and nvarchar types impacted by the new collation.
Otherwise, these data types may not be able to support as many characters as they
did previously.
USE AP ;
CREATE TABLE Terms
(TermsiD INT NOT NULL PRIMARY KEY,
TermsDescription VARCHAR ( 50) NOT NULL ,
TermsDueDa ys SMALL INT NOT NULL);
Note
• The Terms and GLAccounts tables are created first so the other tables can define
foreign keys that refer to them. Similarly, the Vendors table is created before the
Invoices table, and the Invoices table is created before the InvoiceLineltems table
(see part 2).
Also notice that most of the columns in this database are assigned the NOT
NULL constraint. The exceptions are the VendorAddress1, VendorAddress2,
VendorPhone, VendorContactLName, and VendorContactFName columns in the
Vendors table and the PaymentDate column in the Invoices table. Because not all
vendor addresses will require two lines and because some vendors won' t provide
a street address at all, a null value can be assigned to both address columns to
indicate that they're not applicable. Similarly, you may not have a phone number
and contact information for each vendor. For this reason, you could assign a null
value to one of these columns to indicate an unknown value. Finally, an invoice
wouldn't be assigned a payment date until it was paid. Until that time, you could
assign a null value to the PaymentDate column to indicate that it hasn' t been
paid.
I could also have used a default date to indicate an unpaid invoice. To do
that, I could have defined the PaymentDate column like this:
PaymentDate DATE NOT NULL DEFAULT '1900-01-01'
In this case, the date January 1, 1900 would be stored in the PaymentDate
column unless another value was assigned to that column. Usually, a null value
is a more intuitive representation of an unk nown value than a default such as
this, but either representation is acceptable. Keep in mind, though, that the
technique you use will affect how you query the table.
Since at least 60% of the values for the VendorAddress2 column are likely to
contain a null value, this column uses the SPARSE attribute to optimize storage
of null values. Although this attribute isn' t critical, it can significantly reduce the
amount of storage required for this column.
The Vendors, Invoices, and InvoiceLineltems tables also define the
appropriate foreign key constraints. Notice that the foreign key constraint in the
InvoiceLineltems table that defines the relationship to the Invoices table includes
the ON DELETE CASCADE clause. That way, if an invoice is deleted, its line
items are deleted too.
Because each of the five tables in this database has a primary key, SQL
Server creates a clustered index for each table based on that key. In addition,
this script creates seven additional indexes to improve the performance of the
database. The first five of these indexes are based on the foreign keys that each
referring table uses to relate to another table. For example, since the VendoriD
column in the Invoices table references the VendoriD column in the Vendors
table, I created a nonclustered index on VendoriD in the Invoices table. Similarly,
I created indexes for TermsiD in the Invoices table, DefaultTermsiD and
DefaultAccountNo in the Vendors table, and AccountNo in the InvoiceLineitems
table. Finally, I created indexes for the VendorName column in the Vendors table
and the InvoiceDate column in the Invoices table because these columns are
frequently used to search for rows in these tables.
As you may have noticed, I created an index for each column that
appears in a foreign key constraint except one: the InvoiceiD column in the
InvoiceLineitems table. Since this column is part of the composite primary
key for this table, it's already included in the clustered index. For this reason,
the addition of a nonclustered index on InvoiceiD by itself won' t improve
performance.
Chapter 11 How to create a database and its tables with SQL statements 371
Notes
• The InvoiceLineltems table has a composite primary key that consists of the
InvoiceiD and InvoiceSequence columns. For this reason, the PRIMARY KEY
constraint must be defined as a table-level constraint.
• In addition to the five indexes that SQL Server automatically creates for the primary
key of each table, this script creates seven additional indexes. The first five are
indexes for the foreign keys that are used in the REFERENCES constraints. The last
two create indexes on the VendorName column and the InvoiceDate column since
these columns are used frequently in search conditions.
Perspective
Now that you've completed this chapter, you should be able to create
and modify databases, tables, and indexes by coding DDL statements. This
provides a valuable background for working with any database. In practice,
though, you may sometimes want to use the Management Studio to perform the
functions that are done by DDL statements, so that's what you'lllearn to do in
the next chapter.
Terms
data definition language (DDL) sequence
database objects character set
identifier encoding
transaction log file Unicode
attach a database UTF-8
full-table index UTF-16
filtered index Latini
snippet ASCII (American Standard Code for
snippet picker Information Interchange)
constraint UCS-2
column-level constraint supplementary characters
table-level constraint fixed-length encoding
check constraint variable-length encoding
foreign key constraint collation
reference constraint script
cascading delete batch
cascading update
Chapter 11 How to create a database and its tables with SQL statements 373
Database (iles:
logical Name Fie Type F~ nlial Size (MB) h.togrowth I Max9ize
New_AP ROWS... PRIMARY f"a ···----·-·--·-·-·--·1 By 64MB. Unimited
1.••••- •••••••••••••.•••••••••••••.•••••J
New_AP_log LOG Not Applicable 8 By 64 M8. Unimited
Connedion
Server:
local1ost\SQLEXPRESS
Connection:
murach'J>me
Pr09SS
Ready < >
Connect • f ' 'f C, -1> Column Name Data Typ• Allow Nulls
- - - - - - -- - - - - -- -
0 Ready
Note
• When you create a table using the Management Studio, the table is automatically
stored in the default schema. If you want to transfer the table to a different schema,
you can use the ALTER SCHEMA statement. See chapter 17 for details.
The Foreign Key Relationships dialog box for the Invoices table
Foreign Key Relationships X
Selected Relationsh1p:
[email protected]@MN Editing properties for new relationship. The 'Tables And Columns
Specification' property needs to be filled in before the new relationship will be
accepted.
v (GeneraO
Check Existing Data On Creation Or Re Yes
Tables And Columns Specification
v Identity
(Name) FK_Invoices_Vendors
Description
v T;obl" O<OSign"r
Enforce For Replication Yes
Enforce Foreign Key Constraint Yes
v INSERT And UPDATE Specification
Delete Rule No Action
Add Close
Relationship namo:
IFK_Invoices_Vendors
Primary key tablo: Foreign key tablo:
Vendors
VendoriD
Cancel
Description
• To display the Foreign Key Relationships dialog box for a table, display the table
in the Table Designer and click on the Relationships button in the toolbar.
• To add a new foreign key relationship, click on the Add button. To delete an
existing relationship, select the relationship and click on the Delete button.
• To specify the tables and columns that define the relationship, select the
relationship, select the Tables And Columns Specification property, and click the
button that appears to display the Tables and Columns dialog box. Then, use this
dialog box to specify the primary key table and the appropriate columns in both
tables.
• To set other properties for a relationship, select the relationship and use the
properties grid to change the properties.
Description
• To display the Indexes/Keys dialog box for a table, display the table in the Table
Designer and click on the Manage Indexes and Keys button in the Table Designer
toolbar.
• To add a new index, click the Add button, use the Columns property to specify
the column name and sort order for each column in the index, and use the Name
property to enter the name you want to use for the index.
• To view or edit an existing index, select the index from the list box on the left side
of the dialog box. Then, you can view its properties on the right side of the dialog
box.
• To create a unique key and an index that's based on that key, use the Type property
to select the Unique Index option. When you do, the Is Unique property will
automatically be set to Yes.
• To create an index without creating a unique key, use the Type property to select the
Index option and set the Is Unique property to No.
• To create a clustered index, set the Create As Clustered property to Yes. If a table
already contains a clustered index, this property is grayed out to show that it isn' t
available.
• The other options in this dialog box are used for performance tuning. In most cases,
the default values for these options are acceptable. For more information, see the
SQL Server documentation.
v (Gene raO
Expression (InvoiceTotal > 0)
v ld.,ntity
(Name) CK_InvoiceTotal
Description
v Tab!" Design"'
Check Existing Data On Creation Or Re-Enabling Yes
Enforce For INSERTs And UPDATEs Yes
Enforce For Replication Yes
Description
• To display the Check Constraints dialog box for a table, display the table in the
Table Designer and click on the Manage Check Constraints button in the Table
Designer toolbar.
• To add a new constraint to the table, click the Add button. Then, you must enter the
expression that defines the constraint in the Expression property, and you usually
want to use the Name property to provide a meaningful name for the constraint.
• To delete a constraint, select the constraint and click the Delete button.
• To view or edit the properties for an existing constraint, select the constraint from
the list that's displayed on the left side of the dialog box.
• By default, SQL Server checks existing data when you add a new check constraint
to be sure it satisfies the constraint. If that's not what you want, you can set the
Check Existing Data On Creation Or Re-Enabling property to No.
• By default, SQL Server enforces the constraint for insert and update operations.
If that's not what you want, you can set the Enforce For INSERTs And UPDATEs
property to No.
• By default, SQL Server enforces the constraint when the database is replicated. If
that's not what you want, you can set the Enforce For Replication property to No.
s !ma!ll
8 1m Invoices
'· 1m lnvoiceli1ekems
Connedion
Server:
local1ost\SQLEXPRESS
Connection:
murach'J>me
vf '®w connect!OO D!ODe!!ies
Seleded object
~ame:
~MA17\SOLEXPRESS].[AP].(dbo].[Vendors] I
Pr09SS
:rn>e:
0 Ready
Qependency type:
[ able
ISchema-bound dependency
I
I
I I
OK Cancel
..
Description
• To view the dependencies for a table, right-click the table and select View
Dependencies to display the Object Dependencies dialog box.
• To expand or collapse a dependency, click on the plus (+) or minus sign (-) that's
displayed to the left of the dependency.
• You should check table dependencies before you delete or modify a column or an
entire table.
ON [PRIMARY]
GO
Description
• You can use the Management Studio to generate scripts to create, drop, or alter most
objects that are contained in the database. You can send each script to Management
Studio's Query Editor, a file, or to the clipboard.
• To generate a script that creates a database, right-click on the database and select
Script Database as7 CREATE To. Then, select New Query Editor Window, File, or
Clipboard.
• To generate a script that creates a table, right-click on the table and select
Script Table as7 CREATE To. Then, select New Query Editor Window, File, or
Clipboard.
If you experiment with scripts, you' ll find that you can send them to a new
Query Editor window, to a file, or to the clipboard. Most of the time, it's easiest
to send the generated script to a Query Editor window. Then, you can review
the script before you run it, save it, or copy it to the clipboard. In some cases,
though, you may want to send a script directly to a file without viewing it first.
Or, you may want to send the script to the clipboard without viewing it first so
you can paste it into another tool and use that tool to view, save, or run the script.
r To prov<nt any potontial data loss issuos, you should rovi.w this script in dotail btfor< n ,..
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
L
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.lnvoie<s
< >
"
0 Automatically gtn<rat< chang< script on <V<ry sav<
I Yos
I No
Description
• If you use the Table Designer to modify the design of a table, you can click the
Generate Change Script button in the toolbar to display the Save Change Script
dialog box shown above. You can use this dialog box to examine or save the SQL
script that the Management Studio uses to alter the table.
• If you check the Automatically Generate Change Script On Every Save box, the
Management Studio will prompt you with a Save Change Script dialog box each
time you attempt to save changes to the design of a table.
Figure 12-8 How to generate a change script when you modify a table
392 Section 3 Database design and implementation
Perspective
In this chapter, you learned how to use the Management Studio to create
and work with database objects, such as tables, keys, indexes, and constraints
as well as the database itself. Now that you know how to use the Management
Studio to work with the design of a database, you may be wondering when you
should use it for database design and when you should code the DDL statements
yourself.
Although it's often a matter of preference, most SQL programmers find it
easier to use the Management Studio to create and work with database objects.
That way, they don' t have to worry about the exact syntax of the DOL
statements, which they may use infrequently.
However, some experienced database programmers prefer to enter the
DOL statements themselves. That way, they have more control over how these
scripts are coded. In addition, they can save a copy of the script that creates
the database for future reference, which may be helpful if the database design
ever needs to be ported to another type of database management system such as
Oracle, DB2, or MySQL.
Terms
Table Designer
replication
dependencies
An introduction to views
In chapter 1, you learned the basics of how views work. In the next topic,
then, I'll just review this information. Then, I'll present some of the benefits of
views so you'll know when and why you should use them.
(122 rows )
4 (714) 555-9000
5 (805) 555-0584
6 (559) 555-1551
7 NU~
8 (559) 555-5106
( 75
Description
• A view consists of a SELECT statement that's stored as an object in the database.
The tables referenced in the SELECT statement are called the base tables for the
view.
• When you create a view, the query on which it's based is optimized by SQL Server
before it's saved in the database. Then, you can refer to the view anywhere you
would normally use a table in any of the data manipulation statements: SELECT,
INSERT, UPDATE, and DELETE.
• Although a view behaves like a virtual table, it doesn' t store any data. Since the
view refers back to its base tables, it always returns current data.
• A view can also be referred to as a viewed table because it provides a view to the
underlying base tables.
( 5 rows)
(5 rows)
Description
• You can create a view based on almost any SELECT statement. That means that
you can code views that join tables, summarize data, and use subqueries and
functions.
• You can restrict access to the data in a table by including selected columns in the
SELECT clause for a view, or by including a WHERE clause in the SELECT
statement so that only selected rows are retrieved by the view.
Description
• You use the CREATE VIEW statement to create a view. The name you give the
view must not be the same as the name of any existing table or view.
• The SELECT statement within the view can refer to as many as 256 tables, and it
can use any valid combination of joins, unions, or subqueries.
• You can create a view that's based on another view rather than on a table, called a
nested view. SQL Server views can be nested up to 32 levels deep.
• The SELECT statement for a view can't include an INTO clause, and it can' t
include an ORDER BY clause unless the TOP clause or the OFFSET and FETCH
clauses are also used. To sort the rows in a view, you have to include the ORDER
BY clause in the SELECT statement that uses the view.
• You can name the columns in a view by coding a list of names in parentheses
following the view name or by coding the new names in the SELECT clause. A
column must be named if it's calculated from other columns or if a column with the
same name already exists. Otherwise, the name from the base table can be used.
• You can use the WITH ENCRYPTION clause to keep users from examining the
SQL code that defines the view.
• You can use the WITH SCHEMABINDING clause to bind a view to the database
schema. Then, you can' t drop the tables on which the view is based or modify the
tables in a way that would affect the view.
• If you include the WITH SCHEMABINDING clause, you can' t use the all columns
operator (*) in the SELECT statement. In addition, you must qualify the names of
tables and views in the FROM clause with the name of the schema that contains
them.
• You can use the WITH CHECK OPTION clause to prevent a row from being
updated through a view if it would no longer be included in the view. See figure
13-7 for details.
A statement that names just the calculated column in its SELECT clause
CREATE VIEW Outstandinginvoices
AS
SELECT InvoiceNumber, InvoiceDate, InvoiceTotal,
InvoiceTotal - PaymentTotal - CreditTotal AS BalanceDue
FROM Invoices
WHERE InvoiceTotal - PaymentTotal - CreditTotal > 0;
Note
• If you name the columns of a view in the CREATE VIEW clause, you have to name
all of the columns. By contrast, if you name the columns in the SELECT clause,
you can name just the columns you need to rename.
Description
• An updatable view is one that can be used in an INSERT, UPDATE, or DELETE
statement to modify the contents of a base table that the view refers to. If a view is
not updatable, it's called a read-only view.
• The requirements for coding updatable views are more restrictive than for coding
read-only views. That's because SQL Server must be able to unambiguously
determine which base tables and which columns are affected.
• You can also insert, update, and delete data through a view using an INSTEAD OF
trigger. See chapter 15 for details.
Figure 13-6 presents the statements you use to delete or modify a view.
To delete a view, you use the DROP VIEW statement. In this statement, you
simply name the view you want to delete. Like the other statements for deleting
database objects, this statement deletes the view permanently. So, you may want
to make a backup copy of the database first if there's any chance that you may
want to restore the view later.
To modify a view, you can use the ALTER VIEW statement. Notice that the
syntax of this statement is the same as the syntax of the CREATE VIEW
statement. If you understand the CREATE VIEW statement, then, you won't
have any trouble using the ALTER VIEW statement.
Instead of using the ALTER VIEW statement to modify a view, you can
delete the view and then recreate it. If you've defined permissions for the view,
you should know that those permissions are deleted when the view is deleted. If
that's not what you want, you should use the ALTER VIEW statement instead.
The examples in this figure show how you can use the DROP VIEW and
ALTER VIEW statements. The first example is a CREATE VIEW statement that
creates a view named Vendors_SW. This view retrieves rows from the Vendors
table for vendors located in four states. Then, the second example is an ALTER
VIEW statement that modifies this view so it includes vendors in two additional
states. Finally, the third example is a DROP VIEW statement that deletes this
vtew.
In the last chapter, you learned how to display the dependencies for a table.
Before you delete a table, you should display its dependencies to determine if
any views are dependent on the table. If so, you should delete the views along
with the tables. If you don' t, a query that refers to the view will cause an error.
To prevent this problem, you can bind the view to the database schema by
specifying the WITH SCHEMABINDING option in the CREATE VIEW or
ALTER VIEW statement. Then, you won't be able to delete the base table
without deleting the views that depend on it first.
Chapter I 3 How to work with views 407
Description
• To delete a view from the database, use the DROP VIEW statement.
• To modify the definition of a view, you can delete the view and then create it again,
or you can use the ALTER VIEW statement to specify the new definition.
• When you delete a view, any permissions that are assigned to the view are also
deleted.
• If you delete a table, you should also delete any views that are based on that table.
Otherwise, an error will occur when you run a query that refers to one of those
views. To find out what views are dependent on a table, display the table's
dependencies as described in chapter 12.
• If you specify the WITH SCHEMABINDING option when you create or modify a
view, you won' t be able to delete the base tables without first deleting the view.
Note
• ALTER VIEW isn' t an ANSI-standard statement. Although it's supported on other
SQL-based systems, its behavior on each system is different.
Description
• You can use the UPDATE statement to update a table through a view. To do that,
you name the view in the UPDATE clause.
• The view you name in the UPDATE statement must be updatable. In addition, the
UPDATE statement can' t update data in more than one base table.
• If you don't specify WITH CHECK OPTION when you create a view, a change you
make through the view can cause the modified rows to no longer be included in the view.
• If you specify WITH CHECK OPTION when you create a view, an error will occur if
you try to modify a row in such a way that it would no longer be included in the view.
Description
• You can use the INSERT statement to insert rows into a base table through a view.
To do that, you name the view in the INSERT clause. Both the view and the INSERT
statement must include all of the columns from the base table that require a value.
• If the view names more than one base table, an INSERT statement can insert data into
only one of those tables.
• You can use the DELETE statement to delete rows from a base table through a view.
To do that, you name the table in the DELETE clause. For this to work, the view must
be based on a single table.
A SELECT statement that retrieves the name and schema of each table
SELECT sys . t a bles.name AS TableName, sys. schemas.name AS Sc hemaName
FROM sys.tables INNER JOIN sys.schemas
ON s y s .ta ble s . s c hema_ id = s ys.s chemas . schema_ id;
Description
• You can use the catalog views to examine the system catalog, which lists all of the
system objects that define a database, including tables, views, columns, keys, and
constraints.
• Some catalog views inherit columns from other catalog views. In that case, the
catalog view from which the columns are inherited is called the base view, and the
catalog view that inherits the columns is called the derived view.
• For a complete listing of the catalog views, refer to SQL Server documentation.
••
Object Explorer • ~ X MMA 17\SQLEXPRES...o.Vendorl'ayment' -o X SQLQuery7.sql · lo. (murach\Anne (52)) •
Connect · 'f ' ¥ C, -J>. 1m Invoices !Ill Vendors
8 id localhost\SOlEXPRESS (SOL Server 15.0. " • (All Columns) • (All Columns)
8 Databases
O lrrvoicelD 0 Vendo<t0
G) System Oatabas6
O vendoriD 0VendorName
Hl Database Snapshots
0 1nvoiceNumber O vendorAddress1
8 iii AP 0 1nvoiceDate [;;)VendorAddress2
(t) OatJbase Diagrams
(!) Tables
8 Views
1!J System Views Column Alias Table Outp ... Sort Type Sort Order Filter
0 Ready
Figure 13-1 0 How to use the Management Studio to work with views
416 Section 4 Advanced SQL skills
Perspective
In this chapter, you learned how to create and use views. As you've seen,
views provide a powerful and flexible way to predefine the data that can be
retrieved from a database. By using them, you can restrict the access to a
database while providing a consistent and simplified way for end users and
application programs to access that data.
Terms
vtew read-only view
viewed table catalog views
base table base view
nested view derived view
database schema system catalog
updatable view information schema view
Exercises
1. Write a CREATE VIEW statement that defines a view named InvoiceBasic
that returns three columns: VendorName, InvoiceNumber, and InvoiceTotal.
Then, write a SELECT statement that returns all of the columns in the view,
sorted by VendorName, where the first letter of the vendor name is N, 0 , or P.
2. Create a view named ToplOPaidlnvoices that returns three columns for each
vendor: VendorName, Lastlnvoice (the most recent invoice date), and
SumOflnvoices (the sum of the InvoiceTotal column). Return only the 10
vendors with the largest SumOflnvoices and include only paid invoices.
3. Create an updatable view named Vendor Address that returns the VendoriD,
both address columns, and the city, state, and zip code columns for each
vendor. Then, write a SELECT query to examine the result set where
VendoriD=4. Next, write an UPDATE statement that changes the address so
that the suite number (Ste 260) is stored in VendorAddress2 rather than in
VendorAddress l . To verify the change, rerun your SELECT query.
4. Write a SELECT statement that selects all of the columns for the catalog
view that returns information about foreign keys. How many foreign keys are
defined in the AP database?
5. Using the Management Studio, modify the InvoiceBasic view created in
exercise 1 to sort the result set by VendorName. What clause does the system
automatically code to allow the use of an ORDER BY clause in the view?
14
How to code scripts
At the end of chapter 11, you saw a simple script that defines the AP database
and the tables it contains. Now, this chapter teaches you how to code more
complex scripts. With the skills you 'lllearn in this chapter, you'll be able to
code scripts with functionality that's similar to the functionality provided by
procedural programming languages like C#, Visual Basic, and Java.
If you have experience with another procedural programming language,
you shouldn' t have any trouble with the skills presented in this chapter.
However, you should know that the programming power of Transact-SQL
is limited when compared to other languages. That's because Transact-SQL
is designed specifically to work with SQL Server databases rather than as a
general-purpose programming language. For its intended use, Transact-SQL
programming is powerful and fl exible.
An introduction to scripts
To start, this chapter reviews and expands on the script concepts you learned
in chapter 11. Then, it summarizes the Transact-SQL statements you can use
within scripts. Most of these statements will be presented in detail later in this
chapter.
USE ClubRoster;
Description
• A script is a series of SQL statements that you can store in a file. Each script can
contain one or more batches that are executed as a unit.
• To signal the end of a batch, you use the GO command. A GO command isn' t
required after the last batch in a script or for a script that contains a single batch.
• If a statement must be executed before the statements that follow can succeed, you
should include a GO command after it.
• The statements within a batch are executed in the order that they appear in the
batch. Because of that, you need to code statements that depend on other statements
after the statements they depend on.
• If you create a database within a script, you have to execute the batch that contains
the CREATE DATABASE statement before you can execute other statements that
refer to the database.
• The five statements listed above (CREATE VIEW, CREATE PROCEDURE,
CREATE FUNCTION, CREATE TRIGGER, and CREATE SCHEMA) can' t be
combined with other statements in a batch.
• If a script will be used with a production database, you should include documentation
as shown above. Additional information should be included when appropriate.
Description
• These statements are used within SQL scripts to add functionality similar to that
provided by procedural programming languages.
• These statements are part of the Transact-SQL, or T-SQL, language and aren' t
available on SQL-based systems other than SQL Server.
Description
• A variable is used to store data. To create a variable, you use the DECLARE
statement. The initial value of a variable is always null.
• A scalar variable is defined with a standard data type and contains a single value.
You can also create table variables to store an entire result set.
• The name of a variable must always start with an at sign (@). Whenever possible,
you should use long, descriptive names for variables.
• The scope of a variable is the batch in which it's defined, which means that it can' t
be referred to from outside that batch. Because of that, variables are often called
local variables.
• To assign a value to a variable, you can use the SET statement. Alternatively, you
can use the SELECT statement to assign a value to one or more variables.
• You can use a variable in any expression, but you can't use it in place of an object
name or a keyword.
INSERT @BigVendors
SELECT VendoriD, VendorName
FROM Vendors
WHERE VendoriD IN
(SELECT VendoriD FROM Invoices WHERE InvoiceTotal > 5000);
Description
• A table variable can store an entire result set rather than a single value. To create a
table variable, use a DECLARE statement with the table data type.
• You use the same syntax for defining the columns of a table variable as you do
for defining a new table with the CREATE TABLE statement. See figure 11-4 in
chapter 11 for details.
• Like a scalar variable, a table variable has local scope, so it's available only within
the batch where it's declared.
• You can use a table variable like a standard table within SELECT, INSERT,
UPDATE, and DELETE statements. The exception is that you can' t use it within
the INTO clause of a SELECT INTO statement.
Description
• A temporary table exists only during the current database session. In the Management
Studio, that means that the table is available until you close the window where you
created the table.
• Temporary tables are stored in the system database named tempdb.
• If you need to drop a temporary table before the end of the current session, you can do
that using the DROP TABLE statement.
• Temporary tables are useful for testing queries or for storing data temporarily in a
complex script.
• A local temporary table is visible only within the current session, but a global temporary
table is visible to all sessions. To identify a local temporary table, you prefix the name
with a number sign ( # ). To identify a global temporary table, you prefix the name with
two number signs ( ##).Temporary table names are limited to 116 characters.
• Because derived tables result in faster performance than temporary tables, you should
use derived tables whenever possible. See figure 14-6 for details.
Description
• Within a Transact-SQL script, you often need to work with table objects other than
the base tables in your database.
• The scope of a table object determines what code has access to that table.
• Standard tables and views are stored permanently on disk until they are explicitly
deleted, so they have the broadest scope and are therefore always available for use.
• Derived tables and table variables are generally stored in memory, so they can
provide the best performance. By contrast, standard tables and temporary tables are
always stored on disk and therefore provide slower performance.
• To improve the performance of your scripts, use a derived table instead of creating
a table variable. However, if you need to use the table in other batches, create a
temporary table. Finally, if the data needs to be available to other connections to the
database, create a standard table or, if possible, a view.
• Although a view isn' t a table, it can be used like one. Views provide fast
performance since they're predefined, and high availability since they' re permanent
objects. For these reasons, you should try to use a view rather than create a table
whenever that's possible. However, if you need to insert, delete, or update the data
in the table object without affecting the base tables of your database, then you can't
use a view.
• A common table expression (CTE) is a type of derived table. For more information
about CTEs, see chapter 6.
An enhanced version of the same script that uses an IF... ELSE statement
USE AP;
DECLARE @MininvoiceDue money, @MaxinvoiceDue money;
DECLARE @EarliestinvoiceDue date, @LatestinvoiceDue date;
SELECT @MininvoiceDue = MIN(InvoiceTotal - PaymentTotal - CreditTotal),
@MaxinvoiceDue = MAX(InvoiceTotal - PaymentTotal - CreditTotal),
@EarliestinvoiceDue = MIN(InvoiceDueDate),
@LatestinvoiceDue = MAX(InvoiceDueDate)
FROM Invoices
WHERE InvoiceTotal - PaymentTotal - CreditTotal > 0;
IF @EarliestinvoiceDue < GETDATE()
BEGIN
PRINT 'Outstanding invoices overdue!';
PRINT ' Dated ' + CONVERT(varchar,@EarliestinvoiceDue,1) +
'through' + CONVERT(varchar,@LatestinvoiceDue,1) + ' . ' ;
PRINT 'Amounting from $' + CONVERT(varchar,@MininvoiceDue,1) +
' t o $ ' + CONVERT(varchar,@MaxinvoiceDue,1) + ' . ' ;
END;
ELSE --@EarliestinvoiceDue >= GETDATE()
PRINT 'No overdue invoices.';
Description
• You use the IF ... ELSE statement to test a conditional expression. If that expres-
sion is true, the statements that follow the IF keyword are executed. Otherwise, the
statements that follow the ELSE keyword are executed if that keyword is included.
• If you need to execute two or more SQL statements within an IF or ELSE clause,
enclose them within a BEGIN ...END block.
• You can nest IF. ..ELSE statements within other IF. ..ELSE statements. Although
SQL Server doesn't limit the number of nested levels, you should avoid nesting so
deeply that your script becomes difficult to read.
The syntax for the IF EXISTS clause (SQL Server 2016 and later)
DROP OBJECT_ TYPE IF EXISTS object_ name;
Description
• With SQL Server 2016 and later, you can add the IF EXISTS clause to a DROP
statement to check whether an object exists before you drop it.
• With earlier versions of SQL Server, you can use the OBJECT_ID and DB_ID
functions within IF statements to check whether an object exists.
• You can use the OBJECT_ID function to check for the existence of a table, view,
stored procedure, user-defined function , or trigger. You use the DB_ID function
to check for the existence of a database. Both functions return a null value if the
object doesn' t exist. Otherwise, they return the object's identification number.
• To test for the existence of a temporary table, you must qualify the table name with
the database that contains it: tempdb. Since this is a system database, though, you
can omit the schema name as shown above.
A script that tests and adjusts credit amounts with a WHILE loop
USE AP;
IF OBJECT_ ID('tempdb •• #InvoiceCopy') IS NOT NULL
DROP TABLE #InvoiceCopy;
Description
• To execute a SQL statement repeatedly, you use the WHILE statement. This
statement is executed as long as the conditional expression in the WHILE clause is
true.
• If you need to execute two or more SQL statements within a WHILE loop, enclose
the statements within BEGIN and END keywords.
• To exit from a WHILE loop immediately without testing the expression, use the
BREAK statement. To return to the beginning of a WHILE loop without executing
any additional statements in the loop, use the CONTINUE statement.
Warning
• This script takes a few seconds to execute.
The syntax
Declare a cursor
DECLARE cursor_name CURSOR FOR select_statement;
Get column values from the row and store them in a series of variables
FETCH NEXT FROM cursor_name INTO @variable1[, @variable2] [, @variable3] ••• ;
PRINT I
I
o I
Description
• The @ @FETCH_STATUS system function returns 0 if the row was fetched
successfully or - 1 if the row can' t be fetched because the end of the result set has been
reached.
Description
• You can use the TRY ... CATCH statement to provide error handling (also known
as exception handling). This works similarly to exception handling statements
provided by C# and Visual Basic.
• A TRY block must be followed immediately by a single CATCH block.
• When an error occurs in a statement within a TRY block, control is passed to the
CATCH block where the error can be processed. If no error occurs inside the TRY
block, the CATCH block is skipped.
• Errors that have a severity of 10 or lower are considered warnings and are not
handled by TRY ... CATCH blocks. Errors that have a severity of 20 or higher and
cause the database connection to be closed are not handled by TRY ... CATCH
blocks.
• Within a CATCH block, you can use the functions shown in this figure to return
data about the error that caused the CATCH block to be executed.
UPDATE #InvoiceCopy
SET CreditTotal • CreditTotal + .05
1
El
\~ERE I nvoiceTotal - CreditTotal
IF
Payment Total
l
BREAK,
ELSE --(SELECf _MAX(Creditiatall_EBOM #Invnire(nnv) <= ~~
CONTINUE ~ound With: I
~ Begin I
~If
~ Jlijffi! Code Snippet for While loop.
r-]WHI LE Conditio j,
-;] BEGIN
1 END
Description
• Surround-with snippets make it easy to enclose a block of statements in a BEGIN ...
END, IF, or WHILE statement.
• To insert a surround-with snippet, select the statements you want to enclose. Then,
right-click on the statements, select the Surround With command from the resulting
menu, and select the snippet you want to insert from the list that's displayed.
• To select a snippet from the list, double-click on it. Alternatively, you can use the
Up and Down arrow keys to select the snippet and then press the Tab or Enter key.
• If you insert a snippet for an IF or WHILE statement, you will need to complete
the statement by replacing the highlighted condition. You can also enter an ELSE
clause for an IF statement.
• When you insert a snippet for an IF or WHILE statement, a BEGIN .. .END
statement is added automatically.
IF @MyRowCount = 1
INSERT Invoices
VALUES (@My identity, 'BA- 0199' , ' 2 0 2 0 - 03 - 01' , 4598. 23 ,
0 , 0 , 4 , ' 2 0 2 0 - 04-3 0' , NULL );
(1 r ow(s) affected )
Description
• The system functions return information about SQL Server values, objects, and
settings. They can be used anywhere an expression is allowed.
• System functions are useful in writing scripts. In addition, some of these functions
can be used to provide a value for a DEFAULT constraint on a column.
• System functions used to be called global variables, but that name is no longer
used.
• In general, it's better to store the value returned by a system function in a variable
than to use the system function directly. That's because the value of a system
function can change when subsequent statements are executed.
Description
• You use the SET statement to change configuration settings for the current session.
These settings control the way queries and scripts execute.
• If the ANSI_NULLS option is set to ON, you can only test for null values in a
column by using the IS NULL clause. See figure 3-15 in chapter 3 for details.
• In a future version of SQL Server, the ANSI_NULLS and ANSI_PADDING options
will always be on and you won't be able to turn them off. Because of that, you
shouldn' t use these options in new scripts that you write.
• Instead of using the SET ROWCOUNT statement to limit the numbers of rows that
are processed by a query, you should use the TOP clause. See chapter 3 for
information on how to use this clause with the SELECT statement. For information
on how to use it with the INSERT, UPDATE, and DELETE statements, see the SQL
Server documentation.
• In a future version of SQL Server, the SET ROWCOUNT statement won't affect
INSERT, UPDATE, and DELETE statements.
• For a complete list of the Transact-SQL statements for changing session settings, see
the topic on the SET statement in the SQL Server documentation.
So far, the scripts you've seen in this chapter have contained predefined SQL
statements. In other words, the statements don't change from one execution of
the script to another other than for the values of variables used in the statements.
However, you can also define an SQL statement as a script executes. Then, you
use the EXEC statement shown in figure 14-15 to execute the dynamic SQL.
Notice that EXEC is an abbreviation for EXECUTE.
The EXEC statement executes a string that contains a SQL statement. To
illustrate, the first script in this figure executes a SELECT statement against a
table that's specified at run time. To do that, it concatenates the literal string
"SELECT * FROM " with the value of a variable named @TableNameVar. If
you think about it, you' ll realize that there's no other way to do this in SQL. Of
course, it would have been easier to submit the simple query shown here directly
rather than to use dynamic SQL. However, a more complex script might use an
IF...ELSE statement to determine the table that's used in the query. In that case,
dynamic SQL can make the script easier to code.
The second script in this figure is more complicated. It creates a table with
columns that represent each vendor with outstanding invoices. That means that
the number of columns in the new table will vary depending on the current
values in the Invoices table.
This script starts by creating a variable named @DynarnicSQL that will
store the SQL string that's executed. Next, the new table to be created is deleted
if it already exists. Then, a SET statement assigns the beginning of the SQL
string to @DynamicSQL, which includes the CREATE TABLE statement, the
name of the new table, and an opening parenthesis. The SELECT statement that
follows concatenates the name of each column (the name of the current vendor)
and data type to the SQL string. Finally, the second SET statement concatenates
a closing parenthesis and a semicolon to the string, and the string is executed.
Notice that for each row retrieved by the SELECT statement, the variable
@DynarnicSQL is concatenated with its previous value. Although this syntax
is valid for any query, it isn' t useful except when generating dynamic SQL as
shown here. Also notice that the name of each vendor is enclosed in brackets.
That's because many of the vendors have spaces or other special characters in
their names, which aren' t allowed in column names unless they're delimited.
This figure also shows the SQL statement that's created by one execution of
this script along with the contents of the table that's created. Although this table
isn't useful the way it is, it could be used to cross-tabulate data based on the
Vendors table. For example, each row of this table could represent a date and the
Boolean value in each column could represent whether the vendor has an invoice
that's due on that date. Since more than one vendor's invoice can be due on the
same date, a cross-tabulation is a good representation of this data.
As you may have noticed, the SQL string that's generated by this script
has an extra comma following the last column specification. Fortunately, the
CREATE TABLE statement ignores this extra comma without generating an
error. However, if you wanted to, you could eliminate this comma by using the
LEFT function before concatenating the closing parenthesis.
Chapter 14 How to code scripts 447
EXEC (@DynamicSQL);
Description
• The EXEC statement executes the SQL statement contained in a string. Because
you define the SQL string within the script, you can create and execute SQL code
that changes each time the script is run. This is called dynamic SQL.
• You can use dynamic SQL to perform operations that can't be accomplished using
any other technique.
USE AP;
Description
• A SELECT INTO statement is used to retrieve information from the tables,
columns, and types catalog views and store it in a temporary table named
#TableSummary. This table has one row for each column in each table of the
database that includes the table name, column name, and data type.
• A CREATE TABLE statement is used to create a temporary table named
#AllUserTables. Then, an INSERT statement is used to insert rows into this table
that contain the name of each table in the database. This information is retrieved
from the catalog view named tables. Each row also contains a sequence number
that's generated by SQL Server.
• The system table named dtproperties and the two temporary tables themselves are
omitted from both SELECT queries.
Part 2 of this script includes a WHILE loop that uses dynamic SQL to insert
an additional row into #TableSummary for each table in #AllUserTables. Each of
these rows indicates the total number of rows in one of the base tables. The
@LoopMax variable used by this loop is set to the maximum value of the
TableiD column in #AllUserTables. The @LoopVar variable is set to 1, which
is the minimum value ofTableiD. The WHILE loop uses @LoopVar to step
through the rows of #AllUserTables.
Within the loop, the SELECT statement sets @TableNameVar to the value
of the TableName column for the current table. Then, @ExecVar is built by
concatenating each of the clauses of the final SQL string. This string consists of
three statements. The DECLARE statement is used to create a variable named
@CountVar that will store the number of rows in the current table. Note that
because this variable is created within the dynamic SQL statement, its scope is
limited to the EXEC statement. In other words, it isn' t available to the portion of
the script outside of the EXEC statement.
The SELECT statement within the dynamic SQL statement retrieves the row
count from the current table and stores it in the @CountVar variable. Then, the
INSERT statement inserts a row into the #TableSummary table that includes the
table name, a literal value that indicates that the row contains the row count, and
the number of rows in the table. You can see the contents of the SQL string that's
created for one table, the ContactUpdates table, in this figure.
After the SQL string is created, it's executed using an EXEC statement.
Then, @LoopVar is increased by 1 and the loop is executed again. When the
loop completes, the script executes a SELECT statement that retrieves the data
from the #TableSummary table. That result set is also shown in this figure.
Chapter 14 How to code scripts 451
SET @LoopVar = 1;
The contents of the SQL string for one iteration of the loop
DECLARE @CountVar int; SELECT @CountVar = COUNT(*) FROM ContactUpdates;
INSERT #TableSummary VALUES ('ContactUpdates','*Row Count*', @CountVar);
Description
• The WHll..E statement loops through the tables in the #AllUserTables table. For
each table, it creates a dynamic SQL string that contains a SELECT statement and
an INSERT statement. The SELECT statement retrieves the number of rows in the
table, and the INSERT statement inserts a new row into the #TableSummary table
that indicates the number of rows.
• The final SELECT statement retrieves all of the rows and columns from the
#TableSumrnary table sorted by column name within table name.
Description
• You can use the SQLCMD utility to run T-SQL scripts from a command line. This
provides a way to use a DOS batch file to run a script.
• To open a Command Prompt window, select Command Prompt from the Start menu.
On Windows 10, it's in the Windows System group.
• To start the SQLCMD utility, enter "sqlcmd" at the C:\> prompt along with the
appropriate command line switches.
• You must begin most commands with the -S switch to specify the name of a valid server.
• To log in, you can use the -E switch for Windows authentication, or you can use the
-U and -P switches for SQL Server authentication.
• Once you've started the SQLCMD utility and logged in, you can enter the statements
you want to execute followed by the GO command.
• To exit from the SQLCMD utility, enter "exit" at the SQLCMD prompt.
Figure 14-1 7 How to use the SQLCMD utility
454 Section 4 Advanced SQL skills
Perspective
In this chapter, you've learned how to code procedural scripts in T-SQL. By
using the techniques you've learned here, you'll be able to code scripts that are
more general, more useful, and less susceptible to failure. In particular, when
you use dynamic SQL, you'll be able to solve problems that can't be solved
using any other technique.
In the next chapter, you'll expand on what you've learned here by learning
how to code stored procedures, functions, and triggers. These objects are
basically one-batch scripts that are stored with the database. But they provide
special functionality that gives you greater control over a database, who has
access to it, and how they can modify it.
Terms
script nested IF. .. ELSE statements
batch WHILE loop
T-SQL statement cursor
variable error handling
scalar variable exception handling
local variable surround-with snippets
table variable system function
temporary table global variable
local temporary table dynamic SQL
global temporary table SQLCMD
scope Windows PowerShell
Chapter 14 How to code scripts 455
Exercises
1. Write a script that declares and sets a variable that's equal to the total
outstanding balance due. If that balance due is greater than $ 10,000.00, the
script should return a result set consisting ofVendorName, InvoiceNumber,
InvoiceDueDate, and Balance for each invoice with a balance due, sorted
with the oldest due date first. If the total outstanding balance due is less than
$10,000.00, the script should return the message "Balance due is less than
$1 0,000.00."
2. The following script uses a derived table to return the date and invoice total
of the earliest invoice issued by each vendor. Write a script that generates the
same result set but uses a temporary table in place of the derived table. Make
sure your script tests for the existence of any objects it creates.
USE AP;
Scripts
Of the four types of procedural programs, only scripts can contain two or
more batches. That's because only scripts can be executed by SQL Server tools
such as the Management Studio and the SQLCMD utility. In addition, only
scripts are stored in files outside of the database. For these reasons, scripts tend
to be used most often by SQL Server programmers and database administrators.
Description
• You can write procedural programs with Transact-SQL using scripts, stored
procedures, user-defined functions, and triggers.
• Scripts are useful for those users with access to the SQL Server client tools, such as
the Management Studio. Typically, these tools are used by SQL programmers and
DBAs, not by application programmers or end users.
• Stored procedures, user-defined functions, and triggers are all executable database
objects that contain SQL statements. Although they differ in how they're executed
and by the kinds of values they can return, they all provide greater control and
better performance than a script.
• Stored procedures give the SQL programmer control over who accesses the
database and how. Since some application programmers don' t have the expertise to
write certain types of complex SQL queries, stored procedures can simplify their
use of the database.
• User-defined f unctions are most often used by SQL programmers within the stored
procedures and triggers that they write, although they can also be used by
application programmers and end users.
• Triggers are special procedures that execute when an action query, such as an
INSERT, UPDATE, or DELETE statement, is executed. Like constraints, you can
use triggers to prevent database errors, but triggers give you greater control and
flexibility.
• Since procedures, functions, and triggers are database objects, the SQL statements
you use to create, delete, and modify them are considered part of the DDL.
Description
• A stored procedure is an executable database object that contains SQL statements.
A stored procedure is also called a sproc (pronounced either as one word or as
"ess-proc") or a procedure.
• Stored procedures are precompiled. That means that the execution plan for the SQL
code is compiled the first time the procedure is executed and is then saved in its
compiled form. For this reason, stored procedures execute faster than an equivalent
SQL script.
• You use the EXEC statement to run, or call, a procedure. If this statement is the
first line in a batch, you can omit the EXEC keyword and code just the procedure
name. To make your code easier to read, however, you should always include the
EXEC keyword.
• You can call a stored procedure from within another stored procedure. You can even
call a stored procedure from within itself. This technique, called a recursive call or
recursion, is seldom used in SQL programming.
• One of the advantages of using procedures is that application programmers and end
users don' t need to know the structure of the database or how to code SQL.
• Another advantage of using procedures is that they can restrict and control access
to a database. If you use procedures in this way, you can prevent both accidental
errors and malicious damage.
Description
• You use the CREATE PROC statement to create a stored procedure in the current
database. The name of a stored procedure can be up to 128 characters and is
typically prefixed with the letters sp.
• The CREATE PROC statement must be the first and only statement in a batch. If
you're creating the procedure within a script, then, you must code a GO command
following any statements that precede the CREATE PROC statement.
• To create a temporary stored procedure, prefix the procedure name with a number
sign (#)for a local procedure or two number signs (##) for a global procedure. A
temporary stored procedure only exists while the current database session is open.
• You can use parameters to pass one or more values from the calling program to the
stored procedure or from the procedure to the calling program. See figures 15-4 and
15-5 for more information on working with parameters.
• The AS clause contains the SQL statements to be executed by the stored procedure.
Since a stored procedure must consist of a single batch, a GO command is inter-
preted as the end of the CREATE PROC statement.
• The RECOMPILE option prevents the system from precompiling the procedure,
which means that it has to be compiled each time it's run. Since that reduces system
performance, you don' t typically use this option.
• The ENCRYPTION option prevents users from viewing the code in a stored
procedure. See figure 15-11 for more information on viewing stored procedures.
• The EXECUTE_AS_clause option allows users to execute the stored procedure
with the permissions specified by the EXECUTE AS clause. For more information,
look up "EXECUTE AS clause" in the SQL Server documentation.
Description
• To declare a parameter within a stored procedure, you code the name of the
parameter followed by its data type. The parameter name must start with an at sign
(@ ), and the data type can be any type except table. Parameters are always local to
the procedure.
• Input parameters accept values passed from the calling program.
• Output parameters store values that are passed back to the calling program. You
identify an output parameter by coding the OUTPUT keyword after the parameter
name and data type
• Optional parameters are parameters that do not require that a value be passed from
the calling program. To declare an optional parameter, you assign it a default value.
Then, that value is used if one isn't passed from the calling program.
• A stored procedure can declare up to 2100 parameters. If you declare two or more
parameters, the declarations must be separated by commas.
• It's a good programming practice to code your CREATE PROC statements so they
list required parameters first, followed by optional parameters.
IF @DateVar IS NULL
SELECT @DateVar MIN ( InvoiceDate) FROM Invoices ;
Description
• To call a procedure that accepts parameters, you pass values to the procedure
by coding them following the procedure name. You can pass the parameters by
position or by name.
• To pass parameters by position, list them in the same order as they appear in the
CREATE PROC statement and separate them with commas. When you use this
technique, you can omit optional parameters only if they're declared after any
required parameters.
• To pass parameters by name, code the name of the parameter fo llowed by an equal
sign and the value. You can separate multiple parameters with commas. When you
use this technique, you can list the parameters in any order and you can easily omit
optional parameters.
• To use an output parameter in the calling program, you must declare a variable to
store its value. Then, you use the name of that variable in the EXEC statement, and
you code the OUTPUT keyword after it to identify it as an output parameter.
IF @DateVar IS NULL
SELECT @DateVar = MIN(InvoiceDate) FROM Invoices;
RETURN @InvCount;
Description
• The RETURN statement immediately exits the procedure and returns an optional
integer value to the calling program. If you don' t specify a value in this statement,
the return value is zero.
• To use the return value in the calling program, you must declare a variable to store
its value. Then, you code that variable name followed by an equals sign and the
name of the procedure in the EXEC statement.
Description
• The process of checking the values in one or more columns is known as data validation.
It's a good practice to validate the data within a stored procedure whenever possible.
• The THROW statement manually raises an error. Unless this error is caught by a
TRY ... CATCH statement within the stored procedure, the error will be returned to the
caller j ust like an error that's raised by the database engine.
• You use the state argument to identify how serious an error is. The severity of an error
that's raised with the THROW statement is always 16.
• A THROW statement that's coded within a block must be preceded by a semicolon.
• A THROW statement that doesn' t include any parameters must be coded in a CATCH
block.
A stored procedure
that manages insert operations
Figure 15-8 presents a stored procedure that might be used by an application
program that inserts new invoices into the Invoices table. This should give you a
better idea of how you can use stored procedures.
This procedure starts with a comment that documents the stored procedure.
This documentation includes the author's name, the date the procedure was
created, the dates it was modified, who it was modified by, and a general
description of the procedure 's purpose. Since this procedure returns a value, the
return value is briefly described in the comments too. Of course, you can include
any other information that you feel is useful.
This procedure uses nine parameters that correspond to nine of the columns
in the Invoices table. All of these parameters are input parameters, and each
parameter is assigned the same data type as the matching column in the Invoices
table. This means that if the calling program passes a value that can' t be cast to
the proper data type, an error will be raised as the procedure is called. In other
words, this type of error won' t be caught by the procedure.
If the calling program was to pass a value of 13-15-89 to the @InvoiceDate
parameter, for example, an error would occur because this value can' t be cast as
a date. To handle this type of error within the procedure, you could define each
parameter with the varchar data type. Then, the procedure could test for invalid
data types and raise appropriate errors when necessary.
All of the input parameters are also assigned a default value of NULL. Since
most of the columns in the Invoices table can' t accept null values, this might
seem like a problem. As you 'll see in a minute, however, the procedure tests the
value of each parameter before the insertion is attempted. Then, if the parameter
contains an invalid value, an appropriate error is returned to the calling program
and the insert operation is never performed. By coding the procedure this way,
you can fix some errors by supplying default values, and you can return custom
error messages for other errors.
Chapter 15 How to code stored procedures, functions, and triggers 473
USE AP;
GO
Description
• The nine parameters used in this procedure correspond to nine of the columns in
the Invoices table.
After the AS keyword, a series of IF statements are used to test the values
in each input parameter. The first IF statement, for example, tests the value
of @VendoriD to determine if that vendor exists in the Vendors table. If not,
a THROW statement is used to raise a custom error that indicates that the
VendoriD is invalid. This statement also exits the stored procedure and returns
the custom error to the calling program.
For the first IF statement, the custom error contains a short message that
says, "Invalid VendoriD." However, this message could easily be enhanced to
display a more helpful and descriptive message such as "The specified VendoriD
value does not exists in the Vendors table. Please specify a valid VendoriD."
The next five IF statements check for null values. In addition, the IF
statement for the @InvoiceD ate parameter checks to be sure that it falls between
the current date and 30 days prior to the current date, and the IF statement for
the @lnvoiceTotal parameter checks to be sure that it's greater than zero. If not,
a custom error is raised.
Instead of returning an error if the @CreditTotal or @PaymentTotal
parameter contains a null value, this procedure sets the value of the parameter to
zero. It also checks that the credit total isn' t greater than the invoice total, and it
checks that the payment total isn' t greater than the invoice total minus the credit
total.
The next IF statement checks the value of the @TermsiD parameter to see if
a row with this value exists in the Terms table. If not, this parameter is set to the
value of the DefaultTermsld column for the vendor if the parameter contains a
null value. If it contains any other value, though, a custom error is raised.
This procedure also sets the @lnvoiceDueDate parameter if a due date
isn't passed to the procedure. To do that, it gets the value of the TermsDueDays
column from the Terms table and stores it in a variable, and then uses the
DATEADD function to add the value of that variable to the @lnvoiceDate
parameter. You might think that you could calculate the invoice due date using a
SET statement like this:
SET @InvoiceDueDate = @InvoiceDate +
(SELECT TermsDueDays FROM Terms WHERE TermsiD = @Ter.msiD);
You can' t do that, though, because as you learned in chapter 9, you can' t use the
addition and subtraction operators with the date data type, and all three of the
date parameters for this procedure are declared with that type.
If a due date is passed to the procedure, the procedure checks that the date is
after the invoice date but isn' t more than 180 days after the invoice date. Finally,
the procedure checks the @PaymentDate parameter to be sure that it's not less
than the invoice date or more than 14 days before the current date.
As you can see, some of the data validation performed by this procedure
duplicates the constraints for the Invoices table. If you didn' t check the VendoriD
to be sure that it existed in the Vendors table, for example, the foreign key
constraint would cause an error to occur when the row was inserted. By testing
for the error before the insertion, however, the stored procedure can raise a
custom error message that might be more easily handled by the calling program.
Some of the data validation performed by this procedure goes beyond the
constraints for the columns in the table. For example, all three of the date values
Chapter 15 How to code stored procedures, functions, and triggers 475
Description
• A series of IF statements is used to validate the data in each column of the new
invoice row. If the value in a column is invalid, a THROW statement is used to
return a custom error to the calling program. This terminates the stored procedure.
• Some of the conditions tested by this code could be accomplished using constraints.
However, testing these conditions before the INSERT statement is executed
prevents system errors from occurring and allows you to return a user-friendly
custom error message. Other conditions tested by this code can't be enforced using
constraints.
are tested to determine whether they fall within an appropriate range. This
illustrates the flexibility provided by using stored procedures to validate data.
If the input parameters pass all of the validation tests, the INSERT statement
is executed. Since the data has already been validated, the INSERT statement
should execute successfully most of the time and insert the row. Then, the
following statement uses the @@IDENTITY function to get the new invoice ID
value that's generated when the row is inserted, and it returns that value to the
calling program.
However, even though the data for the insert has been validated, it's still
possible for an unexpected system error to occur. In that case, SQL Server will
raise a system error and end the stored procedure.
In most cases, a stored procedure like this would be called from an
application program. Since the details of doing that are beyond the scope of
this book, however, this figure presents a SQL script that calls the procedure.
This script includes processing similar to what might be used in an application
program. In short, this script uses a TRY ... CATCH statement to catch any errors
that have been raised and to handle them appropriately. In many cases, handling
an error is as simple as displaying a message that describes the error and
indicates what can be done to fix the problem. In this figure, for example, the
CATCH block uses three PRINT statements to indicate that an error occurred,
to display the error number, and to display the error message. In other cases,
though, error handling can include additional processing such as saving data or
exiting the program as gracefully as possible.
Chapter 15 How to code stored procedures, functions, and triggers 477
Description
• If the data in all of the columns of the new row is valid, the procedure executes an
INSERT statement to insert the row. If the insert succeeds, this procedure gets the
new l nvoiceiD value and returns it to the calling program, which ends the
procedure. Otherwise, the database engine raises a system error, returns a value of
zero, and ends the procedure.
• If this procedure was called by an application program, the program would need
to handle any errors that occur. This includes custom errors raised by this stored
procedure and unexpected system errors raised by SQL Server if it can't execute
the INSERT statement.
Description
• If you want to pass a table as a parameter to a stored procedure or a user-defined
function, you must create a user-defined table type for the table.
• A user-defined table type can only be used as an input parameter, not as an output
parameter.
• Creating a user-defined table type is similar to creating a regular table. However,
you can' t define foreign keys for the table.
Description
• To delete a stored procedure from the database, use the DROP PROC statement.
• To modify the definition of a procedure, you can delete the procedure and then
create it again, or you can use the ALTER PROC statement to specify the new
definition.
• When you delete a procedure, any security permissions that are assigned to the
procedure are also deleted. If that's not what you want, you can use the ALTER
PROC statement to modify the procedure and preserve the permissions.
Description
• Microsoft SQL Server includes many system stored procedures that you can use to
perform useful tasks on a database. These procedures are identified by the prefix
sp_. You can use these procedures in the scripts and procedures you write.
• System stored procedures are stored in the Master database, but you can execute
them on any database. These procedures operate within the current database context
that you've set with the USE statement.
• SQL Server has hundreds of system stored procedures. To view the complete list,
look up "system stored procedures" in the SQL Server documentation.
• You can also create your own system stored procedures. To do that, give the
procedure a name that begins with sp_ and create it in the Master database.
Description
• A user-defined function, also called a UDF or just a function, is an executable
database object that contains SQL statements.
• The name of a function can be up to 128 characters and is typically prefixed with
the letters fn.
• Functions always return a value. A scalar-valued function returns a single value of
any T-SQL data type. A table-valued function returns an entire table.
• A table-valued function can be based on a single SELECT statement, in which case
it's called a simple table-valued function, or it can be based on two or more
statements, in which case it's called a multi-statement table-valued function.
• A function can't have a permanent effect on the database. In other words, it can' t
run an action query against the database.
• You can call, or invoke, a scalar-valued function from within any expression. You
can invoke a table-valued function anywhere you'd refer to a table or a view.
• Unlike other database objects, you must specify the name of the schema when
invoking a UDF.
Description
• Functions can be defined with from zero to 1024 input parameters. You specify
these parameters in parentheses after the name of the function in the CREATE
FUNCTION statement. Each parameter can be assigned an optional default value.
• A function can't contain output parameters. Instead, you specify the data type of
the data to be returned by the function in the RETURNS clause.
• You code the statements that define the function within a BEGIN ... END block.
This block includes a RETURN statement that specifies the value to be returned.
• When you invoke a function, you list the parameters within parentheses after the
name of the function. To use the default value of a parameter, code the DEFAULT
keyword in place of the parameter value. You can't pass function parameters by
name.
• When you create a function, it's stored in the schema associated with the current
user if you don' t specify a schema name. When you invoke the function, however,
you must specify the schema name.
• The SCHEMABINDING option binds the function to the database schema.
This prevents you from dropping or altering tables or views that are used by the
function. This option is more commonly used for table-valued functions.
• The ENCRYPTION option prevents users from viewing the code in the function.
• The EXECUTE_AS_clause option specifies the security context under which the
function is executed.
I 1 L.~.~_n_;;y~~~~-~-i-~-~~~-i-~~~~] 31527.24
Description
• You create a simple table-valued function, also called an inline table-valued
function, by coding the table data type in the RETURNS clause of the CREATE
FUNCTION statement. Then, you code a SELECT statement that defines the table
in the RETURN statement.
• To use a simple table-valued function, code the function name in place of a table
name or a view name. If you use a table-valued function in a join operation, you' ll
want to assign a correlation name to it as shown above.
Warning
• Because this code must loop through the Invoices table thousands of times, this
function can take several seconds or more to execute.
Description
• To delete a user-defined function from the database, use the DROP FUNCTION
statement.
• To modify the definition of a function, you can delete the function and then create
it again, or you can use the ALTER FUNCTION statement to specify the new
definition.
• When you delete a function, any security permissions that are assigned to the
function and any dependencies between the function and the tables and views
it uses are also deleted. If that's not what you want, you can use the ALTER
FUNCTION statement to modify the function and preserve the permissions and
dependencies.
< >
Description
• A trigger is a special kind of procedure that executes, or fires, in response to an
action query. Unlike a stored procedure, you can't invoke a trigger directly, you
can't pass parameters to a trigger, and a trigger can' t pass back a return value.
• A trigger is associated with a single table or view, which you identify in the ON
clause. The trigger can be set to fire on INSERT, UPDATE, or DELETE statements
or on a combination of these statements.
• A trigger can be set to fire after the action query (AFTER) or instead of the action
query (INSTEAD OF). A FOR trigger is the same as an AFfER trigger.
• A table can have multiple AFfER triggers, even for the same action. A view can't
have an AFTER trigger. A table or view can have only one INSTEAD OF trigger
for each action.
• To hide the code for the trigger from the user, include the ENCRYPTION option.
• To execute a trigger under a specific security context, include the
EXECUTE_AS_clause option.
• It's a common programming practice to name triggers based on the table or view
and the actions that will cause the trigger to fire.
• Within a trigger, you can refer to two tables that are created by the system: Inserted
and Deleted. The Inserted table contains the new rows for insert and update operations.
The Deleted table contains the original rows for update and delete operations.
Similarly, a table named Deleted that contains the rows that are being
deleted is created by SQL Server during a delete operation. For an update
operation, SQL Server creates both tables. In that case, the Inserted table
contains the rows with the updated data, and the Deleted table contains the
original data from the rows that are being updated.
Description
• An AFTER trigger fires after the action query is executed. If the action query
causes an error, the AFTER trigger never fires.
• AFTER triggers can be used to archive deleted data.
SET @InvoiceDueDate =
DATEADD(day, @DefaultTerms , @InvoiceDate);
INSERT Invoices
(VendoriD, InvoiceNumber, InvoiceDate, InvoiceTotal,
TermsiD, InvoiceDueDate, PaymentDate )
VALUES (@VendoriD, @InvoiceNumber, @InvoiceDate,
@InvoiceTotal, @TermsiD, @InvoiceDueDate, NULL);
END;
END;
ELSE
THROW 50027, 'Limit INSERT to a single row.•, 1;
Description
• An INSTEAD OF trigger is executed instead of the action query that causes it to
fire. Because the action query is never executed, the trigger typically contains code
that performs the operation.
• INSTEAD OF triggers are typically used to provide for updatable views. They can
also be used to prevent errors, such as constraint violations, before they occur.
• Each table or view can have only one INSTEAD OF trigger for each type of
action. However, if a table is defined with a foreign key constraint that specifies the
CASCADE UPDATE or CASCADE DELETE option, INSTEAD OF UPDATE
and INSTEAD OF DELETE triggers can't be defined for the table.
Figure 15-19 How to use INSTEAD OF triggers
500 Section 4 Advanced SQL skills
THROW 50113, 'Correct line item amounts before posting payment. ' , 1;
ROLLBACK TRAN;
END;
END;
Description
• Triggers can be used to enforce database rules for data consistency that can' t be
enforced by constraints.
• Triggers can also be used to enforce the same rules as constraints, but with more
flexibility.
Description
• In the ON clause, you can specify the DATABASE keyword to fire the trigger only
for the current database, or you can specify the ALL SERVER keywords to fire the
trigger for any database on the current server.
• To specify a DDL statement for a trigger, you can code the name of the DDL
statement, replacing any spaces with underscores.
• The EVENTDATA function returns an XML document of the xml data type. If
necessary, you can use the skills presented in chapter 18 to parse this document.
Description
• To delete a trigger from the database, use the DROP TRIGGER statement.
• To modify the definition of a trigger, you can locate the trigger in the Object
Explorer of SQL Server Management Studio by expanding the table the trigger is
associated with and then expanding the Triggers folder. Finally, you can right-click
on the trigger and choose modify.
• To modify the definition of a DDL trigger, you can locate the trigger in the
Object Explorer by expanding the Programmability folder and then expanding the
Database Triggers folder. Finally, you can right-click on the trigger and choose
modify.
• When you delete a trigger, any security permissions that are assigned to the trigger
are also deleted. If that's not what you want, you can use the ALTER TRIGGER
statement to modify the trigger and preserve the permissions.
• Unless a trigger and its corresponding table or view belong to the default schema,
you must include the schema name on the DROP TRIGGER and ALTER
TRIGGER statements.
Perspective
In this chapter, you've learned how to create the three types of executable
database objects supported by SQL Server using SQL statements. Stored
procedures are the most flexible of the three because you can use them in so many
different ways. You can code procedures to simultaneously simplify and restrict
a user's access to the database, to verify data integrity, and to ease your own
administrative tasks.
Although they're generally less flexible than stored procedures, functions
and triggers are powerful objects. You can use them to solve problems that
otherwise would be difficult or impossible to solve. In particular, table-valued
functions are one of the most useful extensions provided by Transact-SQL
because they behave like views but can accept parameters that can change the
result set.
In addition to using SQL statements to work with stored procedures,
functions, and triggers, you can use the Management Studio. You'll find all
three of these object types in folders within the Programmability folder for
a database. Then, you can add, modify, and delete objects using the menus
that appear when you right-click on a folder or object. You can use this same
technique to work with the user-defined table types you use with stored
procedures.
Terms
stored procedure required parameter
user-defined function (UDF) optional parameter
trigger passing parameters by position
sproc passing parameters by name
call a procedure return value
precompiled data validation
execution plan user-defined table type
recursive call system stored procedure
recursion scalar-valued function
temporary stored procedure table-valued function
local procedure simple table-valued function
global procedure multi-statement table-valued function
parameter invoke a function
input parameter inline table-valued function
output parameter fire a trigger
Chapter 15 How to code stored procedures, functions, and triggers 507
Exercises
1. Create a stored procedure named spBalanceRange that accepts three optional
parameters. The procedure should return a result set consisting of
VendorName, InvoiceNumber, and Balance for each invoice with a balance
due, sorted with largest balance due first. The parameter @VendorVar is a
mask that's used with a LIKE operator to filter by vendor name, as shown in
figure 15-5. @BalanceMin and @BalanceMax are parameters used to specify
the requested range of balances due. If called with no parameters or with a
maximum value of 0, the procedure should return all invoices with a balance
due.
2. Code three calls to the procedure created in exercise 1:
(a) passed by position with @VendorVar='M%' and no balance range
(b) passed by name with @VendorVar omitted and a balance range from
$200 to $1000
(c) passed by position with a balance due that's less than $200 filtering for
vendors whose names begin with C or F
3. Create a stored procedure named spDateRange that accepts two parameters,
@DateMin and @DateMax, with data type varchar and default value null.
If called with no parameters or with null values, raise an error that describes
the problem. If called with non-null values, validate the parameters. Test
that the literal strings are valid dates and test that @DateMin is earlier than
@DateMax. If the parameters are valid, return a result set that includes the
InvoiceNumber, InvoiceDate, InvoiceTotal, and Balance for each invoice for
which the InvoiceDate is within the date range, sorted with earliest invoice
first.
4. Code a call to the stored procedure created in exercise 3 that returns invoices
with an InvoiceDate between October 10 and October 20, 2019. This call
should also catch any errors that are raised by the procedure and print the
error number and description.
5. Create a scalar-valued function named fnUnpaidinvoiceiD that returns the
InvoiceiD of the earliest invoice with an unpaid balance. Test the function in
the following SELECT statement:
SELECT VendorName, InvoiceNumber, InvoiceDueDate,
InvoiceTotal - CreditTotal - Pay.mentTotal AS Balance
FROM Vendors JOIN Invoices
ON Vendors . VendoriD =
Invoices.VendoriD
WHERE InvoiceiD =
dbo.fnUnpaidinvoiceiD();
7. Use the function you created in exercise 6 in a SELECT statement that returns
five columns: VendorName and the four columns returned by the function.
8. Create a trigger for the Invoices table that automatically inserts the vendor
name and address for a paid invoice into a table named ShippingLabels. The
trigger should fire any time the PaymentTotal column of the Invoices table is
updated. The structure of the ShippingLabels table is as follows:
CREATE TABLE ShippingLabels
(VendorName varchar(SO),
VendorAddressl varchar(SO),
VendorAddress2 varc har(SO),
VendorCity varchar(SO),
VendorState char(2) ,
VendorZipCode varchar(20));
Use this UPDATE statement to test the trigger:
UPDATE Invoices
SET PaymentTotal = 67.92, PaymentDate = '2020-02-23'
WHERE InvoiceiD =
100 ;
9. Write a trigger that prohibits duplicate values except for nulls in the
NoDupName column of the following table:
CREATE TABLE TestUniqueNulls
(RowiD int IDENTITY NOT NULL,
NoDupName varchar(20) NULL);
(Note that you can't do this by using a unique constraint because the
constraint wouldn't allow duplicate null values.) If an INSERT or UPDATE
statement creates a duplicate value, roll back the statement and return an error
message.
Write a series of INSERT statements that tests that duplicate null values are
allowed but duplicates of other values are not.
16
How to manage
transactions and locking
If you've been working with a stand-alone copy of SQL Server, you've been
the only user of your database. In the real world, though, a database is typically
used by many users working simultaneously. Then, what happens when two
users try to update the same data at the same time?
In this chapter, you' ll learn how SQL Server manages concurrent changes.
But first, you' ll learn how to combine related SQL statements into a single
unit, called a transaction. By learning these skills, you'll be able to write code
that anticipates these conflicts.
Description
• A transaction is a group of database operations that are combined into a logical
unit. By default, each SQL statement is treated as a separate transaction. However,
you can combine any number of SQL statements into a single transaction as shown
above.
• When you commit a transaction, the operations pelformed by the SQL statements
become a permanent part of the database. Until it's committed, you can undo all of
the changes made to the database since the beginning of the transaction by rolling
back the transaction.
• A transaction is either committed or rolled back in its entirety. Once you commit a
transaction, it can't be rolled back.
For some systems, however, a violation of data integrity such as this one
is critical. For instance, consider the classic example of a transfer between two
accounts in a banking system. In that case, one update reduces the balance in the
first account and another update increases the balance in the second account. If
one of these updates fails, either the bank or the customer gets an unexpected
windfall. Because an error like this could cause problems even during the short
period of time it may take to fix it, these two updates should be coded as a
transaction.
DELETE Invoices
WHERE VendoriD = 34;
IF @@ROWCOUNT > 1
BEGIN
ROLLBACK TRAN;
PRINT 'More invoices than expected. Deletions rolled back.' ;
END;
ELSE
BEGIN
COMMIT TRAN;
PRINT 'Deletions committed to the database.';
END;
Description
• Although you can omit the TRAN keyword from the COMMIT and ROLLBACK
statements, it's generally included for readability.
• By default, SQL Server is in autocommit mode. Then, unless you explicitly start
a transaction using the BEGIN TRAN statement, each statement is automatically
treated as a separate transaction. If the statement causes an error, it's automatically
rolled back. Otherwise, it's automatically committed.
• Even if you don' t explicitly start a transaction, you can roll it back using the
ROLLBACK TRAN statement. However, you can' t explicitly commit an implicit
transaction.
• When you use save points, you can roll a transaction back to the beginning or to a
particular save point. See figure 16-4 for details on using save points.
• Although you can name a transaction in the BEGIN TRAN statement and you can
refer to that name in the COMMIT TRAN and ROLLBACK TRAN statements,
you' re not likely to do that.
PRINT I I Io
DECLARE @VendorsCount inti @InvoicesCount int;
SELECT @VendorsCount = COUNT (*) FROM Vendors;
SELECT @InvoicesCount = COUNT (*) FROM Invoices;
PRINT 'Vendors Count: ' +CONVERT (varchar @VendorsCount);
PRINT 'Invoices Count: ' + CONVERT (varchar 1 @InvoicesCount);
Description
• You can nest transactions by coding nested BEGIN TRAN statements. Each time
this statement is executed, it increments the @ @TRANCOUNT system function
by 1. Then, you can query this function to determine how many levels deep the
transactions are nested.
• If you execute a COMMIT TRAN statement when @ @TRANCOUNT is equal to
1, all of the changes made to the database during the transaction are committed and
@@TRANCOUNT is set to zero. If @@TRANCOUNT is greater than 1, however,
the changes aren't committed. Instead, @ @TRANCOUNT is simply decremented
by 1.
• The ROLLBACK TRAN statement rolls back all active transactions regardless of
the nesting level where it's coded. It also sets the value of @ @TRANCOUNT back
to 0.
• Since there are few programming problems that you can only solve using nested
transactions, you probably won't use them often.
3
l
Register af Copyrights
I
3 4 Jobtrak
Description
• You can partially roll back a transaction if you use save points. If you code a save
point name in the ROLLBACK TRAN statement, the system rolls back all of the
statements to that save point.
• If you don' t code a save point name, the ROLLBACK TRAN statement rolls back
the entire transaction.
• Since you can't code a save point name in a COMMIT TRAN statement, the
system always commits the entire transaction.
• As with nested transactions, there are few practical programming problems that you
can solve using save points.
An introduction to concurrency
and locking
When two or more users have access to the same database, it's possible for
them to be working with the same data at the same time. This is called
concurrency. Concurrency isn' t a problem when two users retrieve the same
data at the same time. If they then try to update that data, however, that can be a
problem. In the topics that follow, you'lllearn more about concurrency and how
SQL Server uses locking to prevent concurrency problems. You'll also learn how
you can control the types of problems that are allowed.
Two transactions that retrieve and then modify the data in the same row
Transaction A
BEGIN TRAN;
DECLARE @InvoiceTotal money, @PaymentTotal money, @CreditTotal money;
SELECT @InvoiceTotal InvoiceTotal, @CreditTotal = CreditTotal,
@PaymentTotal = PaymentTotal FROM Invoices WHERE InvoiceiD = 112;
UPDATE Invoices
SET InvoiceTotal = @InvoiceTotal, CreditTotal = @CreditTotal + 317.40,
PaymentTotal = @PaymentTotal WHERE InvoiceiD = 112;
COMMIT TRAN;
Transaction 8
BEGIN TRAN;
DECLARE @InvoiceTotal money, @PaymentTotal money, @CreditTotal money;
SELECT @InvoiceTotal = InvoiceTotal, @CreditTotal = CreditTotal,
@PaymentTotal = PaymentTotal FROM Invoices WHERE InvoiceiD = 112;
UPDATE Invoices
SET InvoiceTotal = @InvoiceTotal, CreditTotal = @CreditTotal,
PaymentTotal = @InvoiceTotal - @CreditTotal,
PaymentDate = GetDate() WHERE InvoiceiD = 112;
COMMIT TRAN;
Description
• Concurrency is the ability of a system to support two or more transactions working
with the same data at the same time.
• Because small systems have few users, concurrency isn' t generally a problem on
these systems. On large systems with many users and many transactions, however,
you may need to account for concurrency in your SQL code.
• Concurrency is a problem only when the data is being modified. When two or more
transactions simply read the same data, the transactions don't affect each other.
• You can avoid some database concurrency problems by using locks, which delay
the execution of a transaction if it conflicts with a transaction that's already
running. Then, the second transaction can't use the data until the first transaction
releases the lock.
• Although SQL Server automatically enforces locking, you can write more efficient
code by understanding and customizing locking in your programs.
Description
• In a large system with many users, you should expect for these kinds of problems
to occur. In general, you don' t need to take any action except to anticipate the
problem. In many cases, if the query is resubmitted, the problem goes away.
• On some systems, if two transactions overwrite each other, the validity of the
database is compromised and resubmitting one of the transactions will not
eliminate the problem. If you're working on such a system, you must anticipate
these concurrency problems and account for them in your code.
• You should consider these locking problems as you write your code. If one of these
problems would affect data integrity, you can change the default locking behavior
by setting the transaction isolation level as shown in the next figure.
Figure 16-6 The four concurrency problems that locks can prevent
522 Section 4 Advanced SQL skills
Description
• Since SQL Server manages locking automatically, you can' t control every aspect of
locking for your transactions. However, you can set the isolation level in your code.
• The transaction isolation level controls the degree to which transactions are isolated
from one another. The server isolates transactions by using more restrictive locking
behavior. If you isolate your transactions from other transactions, concurrency
problems are reduced or eliminated.
• You specify the transaction isolation level by changing the ISOLATION LEVEL
session setting. The default transaction isolation level is READ COMMITTED. At
this level, some lost updates can occur, but this is acceptable for most transactions.
• The READ UNCOMMITTED isolation level doesn' t set any locks and ignores locks
that are already held. Setting this level results in the highest possible performance for
your query, but at the risk of every kind of concurrency problem. For this reason, you
should only use this level for data that is rarely updated.
• The REPEATABLE READ level places locks on all data that's used in a transaction,
preventing other users from updating that data. However, this isolation level still
allows inserts, so phantom reads can occur.
• The SNAPSHOT level uses row versioning rather than locks to provide read
consistency. To use this level, you use the ALTER DATABASE statement to set the
ALLOW_SNAPSHOT_ISOLATION option on.
• The SERIALIZABLE level places a lock on all data that's used in a transaction.
Since each transaction must wait for the previous transaction to commit, the
transactions are handled in sequence. This is the most restrictive of the five isolation
levels.
• You can also use row versioning with the READ COMMITTED isolation level. To
do that, you must set the READ_COMMITTED_SNAPSHOT database option on.
• With row versioning, each time a transaction modifies a row, SQL Server stores an
image of the row as it existed before the modification. That way, read operations
that use row versioning retrieve the row as it existed at the start of the transaction
(SNAPSHOT) or statement (READ COMMITTED).
Description
• SQL Server can lock data at various levels, known as lockable resources. The ten
levels form a hierarchy based on granularity, which refers to the amount of data
the resource encompasses. A resource that encompasses more data than another
resource is said to be less granular, or coarser, than the other resource.
• A coarse-grain lock affects more data than a fine-grain lock. For this reason, more
transactions are locked out when the lock is less granular. Since this slows database
performance, the server assigns locks of the finest possible granularity.
• Locking is automatically enabled and controlled by a SQL Server application called
the lock manager. This program generates locking events and handles the setting
and releasing of locks.
• Maintaining several fine-grain locks requires greater server resources than
maintaining one coarse-grain lock. For this reason, the lock manager will
automatically convert multiple fine-grain locks on the same resource into a single
coarse-grain lock. This is known as lock escalation.
Description
• SQL Server automatically determines the appropriate lock mode for your transaction.
In general, retrieval operations acquire shared locks, and update operations acquire
exclusive locks. As a single transaction is being processed, its lock may have to be
converted, or promoted, from one lock mode to a more exclusive lock mode.
• An Update (U) lock is acquired during the first part of an update, when the data
is being read. Later, if the data is changed, the Update lock is promoted to an
Exclusive (X) lock. This can prevent a common locking problem called a deadlock.
• An intent lock indicates that SQL Server intends to acquire a shared lock or an
exclusive lock on a finer-grain resource. For example, an Intent Shared (IS) lock
acquired at the table level means that the transaction intends to acquire shared locks
on pages or rows within that table. This prevents another transaction from acquiring
an exclusive lock on the table containing that page or row.
• Schema locks are placed on a table's design. Schema Modification (Sch-M) locks
are acquired when the design is being changed with a DOL statement. Schema
Stability (Sch-S) locks are acquired when compiling a query to prevent a schema
change while the query is compiling.
• The Bulk Update (BU) lock mode is acquired for the BULK INSERT statement
and by the bulk copy program (bcp). Since these operations are typically done by
DBAs, neither is presented in this book.
The three intent locks differ based on the portion of the resource and the
type of lock that the transaction intends to acquire. An Intent Shared (IS) lock
indicates that the transaction intends to acquire a shared lock on some, but
not all, of the finer-grained resource. Likewise, an Intent Exclusive (IX) lock
indicates an intent to acquire an exclusive lock on some, but not all, of the
resource. Finally, a Shared with Intent Exclusive (SIX) lock indicates an intent to
acquire both an exclusive lock on some of the resource and a shared lock on the
entire resource.
The Bulk Update (BU) lock mode is used exclusively for copying large
amounts of data in bulk into a database using either the BULK INSERT
statement or the bulk copy program. Since bulk copies are usually done by
DBAs to create databases based on other sources, they're not presented in this
book.
Intent Shared
Shared s
Update u
Shared w/lntent Exclusive SIX
Intent Exclusive IX
Exclusive X
Bulk Update BU
Schema Modification Sch-M
Description
• If a resource is already locked by a transaction, a request by another transaction
to acquire a lock on the same resource will be granted or denied depending on the
compatibility of the two lock modes.
• For example, if a transaction has a Shared (S) lock on a table and another
transaction requests an Exclusive (X) lock on the same table, the lock isn' t granted.
The second transaction must wait until the first transaction releases its lock.
• Intent locks can help improve performance since the server only needs to examine
the high-level locks rather than examining every low-level lock.
Description
• A deadlock occurs when neither of two transactions can be committed because they
each have a lock on a resource needed by the other.
• SQL Server automatically detects deadlocks and allows one of the transactions to
commit. The other transaction is rolled back and raises error number 1205. This
transaction is known as the deadlock victim.
Note
• To test this example, you must execute transaction A first and then execute
transaction B within five seconds.
Make large changes when you can be assured of nearly exclusive access
• If you need to change millions ofrows in an active table, don't do so during hours
of peak usage.
• If possible, give yourself exclusive access to the database before making large
changes.
Perspective
In this chapter, you've learned the ways that SQL Server protects your data
from the problems that can occur on a real-world system. Since the failure of
one or more related SQL statements can violate data integrity, you learned how
to prevent these problems by grouping the statements into transactions. Since
multiple transactions can simultaneously modify the same data, you learned
how to prevent concurrency problems by setting the transaction isolation level
to change the default locking behavior. And since changing the isolation level
can increase the chances of deadlocks, you learned defensive programming
techniques to prevent deadlocks.
Terms
transaction row vers10nmg
commit a transaction granularity
roll back a transaction fine-grain lock
autocommit mode coarse-grain lock
nested transactions lock manager
save point lock escalation
concurrency lock mode
locking shared lock
lost update exclusive lock
dirty read intent lock
nonrepeatable read schema lock
phantom read lock promotion
transaction isolation level deadlock
lockable resource deadlock victim
Exercises
1. Write a set of action queries coded as a transaction to reflect the following
change: United Parcel Service has been purchased by Federal Express
Corporation and the new company is named FedUP. Rename one of the
vendors and delete the other after updating the VendoriD column in the
Invoices table.
2. Write a set of action queries coded as a transaction to move rows from the
Invoices table to the InvoiceArchive table. Insert all paid invoices from
Invoices into InvoiceArchive, but only if the invoice doesn't already exist
in the InvoiceArchive table. Then, delete all paid invoices from the Invoices
table, but only if the invoice exists in the InvoiceArchive table.
17
How to manage
database security
If you've been using a stand-alone copy of SQL Server installed on your own
computer, the security of the system hasn' t been of concern. When you install
SQL Server for use in a production environment, however, you must configure
security to prevent misuse of your data. In this chapter, you' lllearn how to do
that using either the Management Studio or Transact-SQL.
<
(uses Wind:~s login ID) ) Object
Description
• Typically, a network user must log on to the network at a PC using a login ID and
password. If the client PC uses Windows, SQL Server can use the Windows login
ID defined for the user. Otherwise, you can create a separate SQL Server login ID.
• Once a user is logged on to SQL Server, the security configuration determines
which database objects the user can work with and which SQL statements the user
can execute.
• Permissions determine the actions a user can take on a database object, such as a
table, view, or stored procedure, on the objects in a schema, on a database, and on a
server.
• A role is a collection of permissions that you can assign to a user by assigning the
user to that role.
• You can create a collection of users in Windows called a group. Then, you can
assign permissions and roles either to individual users or to a group of users.
• The users, groups, logins, and roles that have access to a server are called
principals. The entities that can be secured on a server, including the server itself,
are called securables.
• If you need to set up a new system with many users, it's often easier to code SQL
scripts using the SQL security statements. The Management Studio is better for
making changes to an existing system or for setting up a small system.
• Even if you use the Management Studio to manage security, you should know how
to manage security with Transact-SQL statements. That will help you understand
the underlying structure of SQL Server security.
O None
® failed logins only
0 SyccessfU loQins only
•
Connedion
Server:
local1ost\SQLEXPRESS
Connection:
murach'J>me Options
Pr09SS
Ready
OK Cancel
Description
• To change the SQL Server authentication mode, right-click on the server in the
Object Explorer of the Management Studio, select the Properties command to
display the Server Properties dialog box, then display the Security page.
• When you use Windows authentication, access to SQL Server is controlled via the
security integrated into Windows. This simplifies login because Windows users
only have to log on once.
• When you use SQL Server authentication, access to SQL Server is controlled via the
separate security built into SQL Server. The user has a login ID and password that
are distinct from their Windows login ID and password, so they have to log on twice.
Description
• You use the CREATE LOGIN statement to create a new SQL Server login ID or to
create a new login ID from a Windows account.
• If you don' t specify a default database when you create a login, the default is set
to master. If you don ' t specify a default language, the default is set to the default
language of the server. Unless it's been changed, the server language default is
English.
• The password you specify for a SQL Server login ID should be a strong password.
A strong password is not easy to guess and cannot easily be hacked. A password
can have up to 128 characters.
• If you include the MUST_CHANGE option, SQL Server will prompt the user for a
new password the first time the login ID is used.
• The CHECK_EXPIRATION option determines whether SQL Server enforces
password expiration policy. The C HECK_POLICY option determines if password
policies, such as strong passwords, are enforced. These options are enforced only if
SQL Server is running on Windows Server.
be dropped in a future release of SQL Server, you should use the statements
presented in this chapter instead.
Statements that use the ALTER LOGIN and DROP LOGIN statements
A statement that changes the password for a SQL Server login ID
ALTER LOGIN JohnDoe WITH PASSWORD= 'lg22A%G45x';
Description
• You use the ALTER LOGIN statement to enable or disable a login ID, change the
name for a login ID, or change the default database or language. For a SQL Server
login ID, you can also change the password and the password options.
• You use the DROP LOGIN statement to drop a login ID.
Description
• You use the CREATE USER statement to create a user for a login ID for the current
database. If the login name is the same as the user name, you can omit the FOR
LOGIN clause.
• When you create a database user, you can specify a default schema. Then, SQL
Server will look in this schema when it searches for objects for the database user
before it looks in the default schema (dbo).
• The ALTER USER statement lets you change the name of an existing database user
or change the default schema for a user.
• You use the DROP USER statement to delete a user from the current database.
• Since all three of these statements work on the current database, you must change
the database context using the USE statement before executing any of these
statements.
Description
• You use the CREATE SCHEMA statement to create a schema in the current
database. You can also create tables and views within the new schema, and you can
grant, revoke, or deny permissions for those tables and views.
• After you create a schema, you can create any object within that schema by
qualifying the object name with the schema name.
• You use the ALTER SCHEMA statement to transfer an object from one schema to
another.
• You can't transfer an object from one schema to another if any views or functions
are bound to it.
• When you transfer an object from one schema to another, all the permissions that
were associated with that object are dropped.
• You use the DROP SCHEMA statement to delete a schema. The schema you delete
must not contain any objects.
A GRANT statement that grants SELECT permission for the Invoices table
GRANT SELECT
ON Invoices
TO SusanRoberts;
Description
• You use this GRANT statement format to give a user permission to work with a
database object. This format of the REVOKE statement takes object permissions
away. See figure 17-8 for a list of the standard permissions that can be granted for
objects.
• The object_name argument specifies the object for which the permission is being
granted or revoked and can specify a table, a view, a stored procedure, or a
user-defined function. If you specify a table, a view, or a table-valued function,
you can also list the columns for which SELECT, UPDATE, or REFERENCES
permissions are granted or revoked.
• The database_principal argument in the TO and FROM clauses can be the name
of a database user or a user-defined role.
• The WITH GRANT OPTION clause gives a user permission to grant this
permission to other users.
• The REVOKE statement includes two clauses that undo WITH GRANT OPTION.
GRANT OPTION FOR revokes the user's permission to grant the permission to
others. CASCADE revokes the permission from any other users who were given
the permission by this user.
• Since both the GRANT and REVOKE statements work on the current database,
you must first change the database context using the USE statement.
Description
• You can only grant permissions that are appropriate for the object or schema.
• You can grant or revoke SELECT, UPDATE, or REFERENCES permission to
specific columns in a table, view, or table-valued function. However, a view is
typically a better way to limit access to specific columns.
Description
• You use this format of the GRANT statement to give a user permission to work
with all the objects in a database schema. This format of the REVOKE statement
takes schema permissions away. These statements work just as they do for object
pernuss1ons.
• To create an object in a schema or to delete an object from a schema, the user must
have ALTER permission to the schema. To create an object, the user must also have
CREATE permission to the database that contains the object. See figure 17-10 for
information on database permissions.
• You can also grant or revoke the CREATE SEQUENCE permission on a schema.
• The ON clause in the GRANT and REVOKE statements includes a class name
(SCHEMA) and a scope qualifier(:: ). Although class names and scope qualifiers
can be used with other Transact-SQL statements, they're not usually required.
Description
• In addition to granting or revoking permissions for objects and schemas, you can
grant or revoke permissions for databases.
• The list of SQL statements shown above only includes those discussed in this book.
For a complete list, refer to the "GRANT Database Permissions (Transact-SQL)"
topic in the SQL Server documentation.
Description
• You can also use the GRANT and REVOKE statements to grant and revoke
permissions for the entire server. When you do that, you specify a login or server
role on the TO or FROM clause.
• For a complete list of server permissions, refer to the "GRANT Server Permissions
(Transact-SQL)" topic in the SQL Server documentation.
Description
• A role is a collection of permissions you can assign to a user or group of users. By
assigning a user to a role, you grant that user all of the permissions of the role. You
can use roles to simplify user and security administration.
• SQL Server has built-in, or fixed, roles defined at the server level and at the
database level. In addition, you can create user-defined roles for your server or
database.
• Each role is assigned a set of permissions. For example, the dbcreator role can
execute CREATE DATABASE, ALTER DATABASE, DROP DATABASE, and
RESTORE DATABASE statements. This role can also add new members to the
role.
• You use the ALTER SERVER ROLE statement to add a user to or remove a user
from a server role. You can also use this statement to rename a user-defined server
role.
• The fixed server roles are intended for users who are involved in the administration
of the server. For a complete list of the fixed server roles, see the "Server-level
Roles" topic in the SQL Server documentation.
• The ALTER SERVER ROLE statement was introduced with SQL Server 2012. [n
previous versions of SQL Server, you used the sp_AddSrvRoleMember system
stored procedure to add a user to a server role, and you used the
sp_DropSrvRoleMember procedure to remove a user from a server role.
Description
• You use the CREATE SERVER ROLE statement to create a user-defined server
role. Role names can be up to 128 characters in length and can include letters,
symbols, and numbers, but not the backslash (\) character.
• The AUTHORIZATION clause lets you specify a login or fixed server role that
owns the user-defined role. If you omit this clause, the role will be owned by the
login that executes the statement.
• Once you create a server role, you can grant permissions to or revoke permissions
from the role. To do that, the current database must be master.
• To add members to a user-defined server role or delete members from the role, you
use the ALTER SERVER ROLE statement shown in figure 17-12.
• You use the DROP SERVER ROLE statement to delete a user-defined server role.
You can' t delete a fixed server role or the public server role.
• Before you can delete a server role, you must delete all of its members. To find out
how to list the members of a role, see figure 17-14.
• The CREATE SERVER ROLE and DROP SERVER ROLE statements were
introduced with SQL Server 2012.
Description
• The sys.server_principals catalog view contains information about each principal
defined on the server. To display information about just the server roles, use a
SELECT statement with a WHERE clause that checks for a type of 'R'.
• The sys.server_role_members catalog view contains a list of each role
principal/member principal combination. To display information about the
members of a server role, j oin this view with the sys.server_role_member view
and restrict the results to the principal ID of the role whose members you want to
display.
• To display information about j ust the fixed server roles and members, you can use
the sp_HelpSrvRole and sp_HelpSrvRoleMember system stored procedures.
Figure 17-14 How to display information about server roles and role members
564 Section 4 Advanced SQL skills
Description
• The fixed database roles are added to each database you create. You can add and
delete members from these roles, but you can't delete the roles.
• You use the ALTER ROLE statement to assign a user to or remove a user from a
database role in the current database. You can also use this statement to change the
name of a user-defined database role.
• The database_principal parameter can be the name of a database user, a
user-defined database role, or a Windows login or group. If you specify a Windows
login or group that doesn't have a corresponding database user, a database user is
created.
• The users you specify are assigned to or removed from the role you name in the
current database. Because of that, you should be sure to change the database
context before executing one of these stored procedures.
• SQL Server also provides a public database role. Any user that's not given specific
permissions on a securable is given the permissions assigned to the public role.
• Prior to SQL Server 2012, you could use the ALTER ROLE statement only to
change the name of a user-defined database role. To add or drop a role, you used
the sp_AddRoleMember or sp_DropRoleMember system stored procedure.
Description
• You use the CREATE ROLE statement to create a user-defined database role. Role
names can be up to 128 characters in length and can include letters, symbols, and
numbers, but not the backslash (\) character.
• Once you create a database role, you can grant permissions to or revoke
permissions from the role. Then, you grant or revoke permissions for every member
of the role.
• To add members to a user-defined database role, delete members from the role, or
rename the role, you use the ALTER ROLE statement shown in figure 17-15.
• You use the DROP ROLE statement to delete user-defined database roles. You can't
delete a fixed database role or the public database role.
• Before you can delete a database role, you must delete all of its members. To find
out how to list the members of a role, see figure 17-17.
Description
• To display information about the roles defined in the current database, use the
sp_HelpRole system stored procedure. If you don' t specify a role name on this
procedure, information about all of the roles in the database is returned.
• To display information about the members of a database role, use the
sp_HelpRoleMember system stored procedure. If you don' t specify the role name
on this procedure, information about the members of all of the roles in the database
that have at least one member is returned.
• You can display additional information about database roles using the
sys.database_principals and sys.database_role_members catalog views. These
views are similar to the sys.server_principals and sys.server_role_members views
described in figure 17-14.
Figure 17-1 7 How to display information about database roles and role members
570 Section 4 Advanced SQL skills
'
A script that denies SELECT permission to GLAccounts
DENY SELECT
ON GLAccounts
TO MartinRey;
Description
• The permissions granted to individual users are granted by two sources: permissions
granted to their user names or login IDs and permissions granted through any roles to
which they are members.
• The DENY statement differs from the REVOKE statement in that DENY prevents the
permission from being granted by role membership. A denied permission can't be granted
by role membership, but a revoked permission can.
GRANT SELECT
ON Invoices
TO AppinvoiceQuery;
~II
'·····························'
2 2 123 263253241 2019-10-10 40.20 40.20 0.00
~
Description
• An application role is a special type of user-define database role. It can' t contain any
members, but it's activated when a connection executes the sp_SetAppRole system
stored procedure.
• Once the connection activates an application role, the normal security for the login ID set
by the permissions for the ID and its roles is ignored. Instead, the connection assumes a
new security profile as defined by the permissions for the application role.
• Once a connection activates an application role, the application role remains in effect
until the connection is closed or until the sp_UnsetAppRole procedure is executed. To
use sp_UnsetAppRole, you must create a cookie when you execute sp_SetAppRole.
• Application roles are typically used by application programs that manage their own
security. Then, those programs can control the users that can log on to the server.
l
Mapped Credertials Credential Provider
Connection:
murach'J>me
vf '®w connect!OO o!ODe!!ies
Pr09SS Rernol(.e
Ready Def.ut !latabase: master
OK Cancel
Description
• To create a new login ID, expand the Security folder for the server in the Object
Explorer. Then, right-click the Logins folder and select New Login to display the
Login - New dialog box.
• Select the type of authentication you want to use and then specify the appropriate
settings. The settings that are available depend on whether you select Windows or
SQL Server authentication.
• To modify a login ID, right-click the ID in the Object Explorer and select
Properties to display the Login Properties dialog box. The General page of this
dialog box lets you change all the settings for the login ID except for the login
name and the authentication mode.
• You can use the options on the Status page of the Login Properties dialog box to
deny or grant a login ID permission to connect to SQL Server. You can also disable
or enable a login ID.
• To delete a login ID, right-click the ID in the Object Explorer and select Delete to
display the Delete Object dialog box. Click the OK button to delete the login ID.
Connedion
Server:
local1ost\SQLEXPRESS
Connection:
murach'J>me
vf '®w conn<et100 D!ODe!!ies
Pr09SS
R.ady
OK Cancel
Description
• To work with the server roles for a login ID, right-click the ID in the Object
Explorer and select Properties to display the Login Properties dialog box. Then,
display the Server Roles page. The roles that the login ID is currently assigned to
are checked in the list that's displayed.
• To add or remove a login ID from a server role, select or deselect the role.
Figure 17-21 How to work with the server roles for a login ID
578 Section 4 Advanced SQL skills
Connedion
Ser.ler:
local1ost\SQLEXPRESS Database LOie membership for: AP
Connection:
murach'J>me
0 cll_accessadmin
0 cll_backupoperator
vf '®w conn<et100 o!ODe!!ies 0 <ll_datareader
0 cll_datawriter
0 <ll_ddadmil
0 cll_denydatareader
0 cll_denydatawlier
Pr09SS 0 cll_owner
R.ady
0 <ll_secuityadrnin
0 ~woiceErtry
0 public
OK Cancel
Conneclion
Server. femissions lor dbo.Vendoo: ~UIM Pennissions...
~aho-\SQLEXPRESS
OK Cancel
Description
• To display the Database User dialog box, expand the database in the Object
Explorer, expand the Security and Users folders, right-click the user name, and
select Properties.
• To work with the permissions for a user, display the Securables page. Then, click
the Search button and use it to select the securables you want to work with.
• To grant the user permission to a securable, select the securable and then select the
Grant option for the permission. You can also select the With Grant option.
• To revoke the user permission to a securable, deselect the Grant option.
• To deny the user permission to a securable, select the Deny option.
• To set the permissions for the columns in a table, view, or table-valued function,
highlight the securable and permission. Then, click the Column Permissions button.
• The permissions that are available change depending on the type of securable that's
selected.
• To display the combination of the permissions granted with this dialog box and the
permissions granted through roles to a securable, display the Effective tab.
I!:J
Name
~lnvoiceOuety
T~
lwhcation role l
1:~~.1 ::.Ert~
Database role
User
~ MartnRey User
:i pubic Database role
Connedion
Server: f'.enrissions for Joi-n Doe:
local1ost\SQLEXPRESS
Explicit 8fective
Connection:
murach'J>me Permssion Grantor WlhGrant ~
OK Cancel
Description
• To display the permissions for a database, right-click the database in the Object
Explorer, select Properties to display the Database Properties dialog box, and
display the Permissions page.
• By default, all database users except for guest are included in the Users or roles list
To add the guest user or any system or user-defined roles, click the Search button
and select the users and roles from the dialog box that's displayed.
• To grant a user or role permission to perform a database operation, select the user
or role and then select the Grant option for the permission. You can also select the
With Grant option to allow the user or role to grant the permission to other users
and roles.
• To revoke a user or role permission to perform a database operation, deselect the
Grant option.
• To deny the user permission to a database operation, select the Deny option.
Perspective
Although managing security on a server can be complex, SQL Server
provides useful tools to simplify the job. In this chapter, you've learned how to
manage security for your server and database using both Transact-SQL and the
Management Studio. Once you're familiar with both of these techniques, you
can use the one that's easiest for the security task at hand.
Unfortunately, the techniques presented in this chapter don't secure your
data when it's "at rest" on a hard drive that contains the data files for the
database. In that case, if a thief steals the database files, he or she can attach the
data files to a different server and gain access to the data. To close this security
hole, you can encrypt the data files and store the encryption key in a different
location. This encrypts every column of every table. That way, even if the data
files are stolen, the thief won' t be able to open them without the key. To encrypt
data files, you can use Transparent Data Encryption (TDE). TDE makes it easy
to encrypt the entire database without affecting existing applications. For more
information about TOE, you can refer to the SQL Server documentation.
However, TOE doesn' t protect your data when it's "alive" in a database
that's running on a server or when it's "in transit" between the client and the
server. To do that, SQL Server 2016 introduced the Always Encrypted feature.
This feature encrypts data for sensitive columns such as columns that store
credit card numbers. When you use it, the client application uses an enhanced
library to encrypt this data when it's "in transit" . Then, this encrypted data
is used by the database when it's "alive" and "at rest" . As a result, the data
is always encrypted. Since the encryption key is stored on the client, the
unencrypted data is only available to the users of the client application, not
to database administrators. This eases security concerns when storing data in
a database that's running in the cloud. For more information about Always
Encrypted, you can refer to the SQL Server documentation.
Terms
login 10 Windows authentication
perrrusswns SQL Server authentication
object permissions strong password
schema permissions scope qualifier
database permissions fixed role
server permissions fixed server role
role user-defined server role
group fixed database role
principal user-defined database role
securable application role
authentication mode
Chapter 17 How to manage database security 585
Exercises
1. Write a script that creates a user-defined database role named PaymentEntry
in the AP database. Give UPDATE permission to the new role for the Invoices
table, UPDATE and INSERT permission for the InvoiceLineitems table, and
SELECT permission for all user tables.
2. Write a script that (1) creates a login ID named "AAaron" with the password
"AAar99999"; (2) sets the default database for the login to the AP database;
(3) creates a user named "Aaron" for the login; and (4) assigns the user to the
PaymentEntry role you created in exercise 1.
3. Write a script that creates four login IDs based on the contents of a new table
named NewLogins:
CREATE TABLE NewLogins
(LoginName varchar(128));
INSERT NewLogins
VALUES ('BBrown'), ('CChaplin'), ('DDyer'), ('EEbbers');
Use dynamic SQL and a cursor to perform four actions for each row in this
table: (1) create a login with a temporary password that's based on the first
four letters of the login name followed by "99999"; (2) set the default database
to the AP database; (3) create a user for the login with the same name as the
login; and (4) assign the user to the PaymentEntry role you created in exercise
1.
4. Using the Management Studio, create a login ID named "FFalk" with the
password "FFal99999," and set the default database to the AP database. Then,
grant the login ID access to the AP database, create a user for the login ID
named "FFalk", and assign the user to the PaymentEntry role you created in
exercise 1.
Note: If you get an error that says "The MUST_CHANGE option is not
supported", you can deselect the "Enforce password policy" option for the
login ID.
5. Write a script that removes the user-defined database role named
PaymentEntry. (Hint: This script should begin by removing all users from this
role.)
6. Write a script that (1) creates a schema named Admin, (2) transfers the table
named ContactUpdates from the dbo schema to the Admin schema, (3)
assigns the Admin schema as the default schema for the user named Aaron
that you created in exercise 2, and (4) grants all standard privileges except for
REFERENCES and ALTER to AAaron for the Admin schema.
18
How to work with XML
In this chapter, you'lllearn how to use SQL Server to work with XML data.
That includes using the xml data type, using an XML schema to validate the
data that's stored in an xml type, and converting relational data to XML and
XML to relational data. These features make it easier to work with XML.
An introduction to XML
Before you learn how to use SQL Server to work with XML, you need to
understand some basic XML concepts. In particular, you need to understand how
XML can be used to structure data, and you need to understand how an XML
schema can be used to validate XML data.
An XML document
XML (Extensible Markup Language) can be used to create an XML
document that contains data that has been structured with XML tags. For
example, figure 18-1 shows an XML document that contains data about an event
that caused a DDL trigger to fire. You learned how to return an XML document
like this one in chapter 15 using the EVENTDATA function within a DDL
trigger.
Within an XML document, an element begins with a start tag and ends with
an end tag. In this figure, for example, the <EventType> tag marks the start of
the EventType element, and the </EventType> tag marks the end of this element.
Within a start tag, one or more attributes can be coded. In this figure, for
example, the start tag for the SetOptions element contains five attributes:
ANSI_NULLS, ANSI_NULL_DEFAULT, ANSI_PADDING,
QUOTED_IDENTIFIER, and ENCRYPTED. Here, each attribute consists of an
attribute name, an equal sign, and a string value in quotes.
Although values can be assigned to attributes, a value can also be coded
between the start and end tags for an element. That's the case with most of the
elements in this figure. The EventType element, for example, contains the string
value "CREATE_TABLE."
Elements can also contain other elements. An element that's contained
within another element is known as a child element. Conversely, the element that
contains a child element is known as the child's parent element. In this figure,
for example, the SetOptions element is a child element of the TSQLComrnand
element, and the TSQLCommand element is the parent element of the
SetOptions element. Although it's not shown here, a child element can also
repeat within a parent element.
The highest-level element in an XML document is known as the root
element. In this fi gure, the EVENT_INSTANCE element is the root element. A
well-formed XML document can have only one root element.
Chapter 18 How to work with XML 589
An XML document
<EVENT_ INSTANCE >
<EventType>CREATE_TABLE</EventType>
<PostTime>2020-02-10T12:38:23.147</PostTime>
<SPID>54</SPID>
<ServerName>MMA17\SQLEXPRESS</ServerName>
<LoginName>murach\anne</LoginName>
<UserName>dbo</UserName>
<DatabaseName>AP</DatabaseName>
<SchemaName>dbo</SchemaName>
<ObjectName>VendorsTest</ObjectName>
<ObjectType>TABLE</ObjectType>
<TSQLCommand>
<SetOptions ANSI_ NULLS="ON" ANSI_ NULL_ DEFAULT="ON" ANSI_ PADDING="ON"
QUOTED_ IDENTIFIER="ON" ENCRYPTED="FALSE" />
<CommandText>
CREATE TABLE VendorsTest
(VendoriD int, VendorName varchar(SO));
</CommandText>
</TSQLCommand>
</EVENT_ INSTANCE>
Description
• XML (Extensible Markup Language) is used to structure data using XML tags. An
XML tag begins with< and ends with>.
• An XML document contains data that has been structured with XML tags.
• An element begins with a start tag and ends with an end tag. The start tag provides
the name of the element and can contain one or more attributes. An attribute
consists of an attribute name, an equal sign, and a string value in quotes. The end
tag repeats the name, prefixed with a slash (/).
• Text can also be coded between an element's start and end tags. This text is referred
to as the element's content.
• Elements can contain other elements. An element that's contained within another
element is known as a child element. The element that contains a child element is
known as the child's parent element. Child elements can also repeat within a parent
element.
• The highest-level element in an XML document is known as the root element. An
XML document can have only one root element.
• The EVENTDATA function described in chapter 15 returns an xml data type that
contains an XML document like the one shown in this figure .
An XML schema
The XML Schema Definition (XSD) language can be used to define an XML
schema, which is a set of rules that an XML document must follow to be valid.
Although there are several languages for defining XML schemas, the XSD
language is the only one that you can use with SQL Server. Figure 18-2 shows
an XML schema that can be used to validate the XML document shown in figure
18-1. In addition, you may notice that the schema itself is an XML document.
To start, this XML schema uses the schema element to specify some attributes
that apply to the entire schema. That includes the xmlns attribute that defines the
prefix for the XML namespace that's used throughout the rest of the schema to
qualify the name of each element. Then, this schema defines each of the elements
and attributes that are used by the XML document shown in fi gure 18-1. This
definition specifies whether an element can contain other elements, the sequence
of the elements, and the name and data type of each element.
Unfortunately, the details of the XSD language are beyond the scope of
this book. As a result, the examples in this chapter only use the XML schema
presented in this figure. Fortunately, if you' re working with an industry standard
XML document, an XSD has probably already been created for it. As a result,
you may be able to get the XSD from a colleague, or you may be able to find the
XSD by searching the Internet.
If you need to create an XML schema for an XML document, you can use the
Management Studio's XML Editor to generate one as described in figure 18-4.
Then, if necessary, you can edit the generated XML schema so it's appropriate
for your XML document. To do that, however, you may need to learn more about
the XSD language by searching the Internet or by getting a book about working
with XML.
Chapter 18 How to work with XML 591
Description
• The XML Schema Definition (XSD) language can be used to define an XML schema,
which is a set of rules that an XML document must follow to be valid.
• You may be able to find an XSD for some types of XML documents by searching
the Internet. If not, you can use the Management Studio's XML Editor to generate
an XSD for an XML document as described in figure 18-4. The details for working
with the XSD language are beyond the scope of this book.
• SQL Server can use the XML schema shown in this figure to validate the XML
document shown in figure 18-1.
Description
• You can use the xml data type just as you use most other SQL Server data types. In
particular, you can use it as the type for a column in a table or a variable in a script,
procedure, function, or trigger.
• Within a DDL trigger, the EVENTDATA function returns an XML document like
the one shown in figure 18-1.
• The xml type is implicitly cast to a string when necessary and a string is implicitly
cast to the xml type.
Description
• To view XML in the XML Editor, run a query that returns the XML that you want
to view and then click on the cell that contains the XML. When you do, the XML
will be displayed in the XML Editor.
• To work with the data in the XML Editor, you can display the XML Editor toolbar.
To do that, right-click on a blank space in the toolbar area and select XML Editor.
• To create an XML Schema Definition for an XML document, display the XML
document in the XML Editor and then click on the Create Schema button in the
XML Editor toolbar.
You can use the same coding technique with attributes, although a well-formed
XML document shouldn' t have more than one attribute with the same name.
The second example shows how to use the exist() method to return an int
value that indicates whether the element or attribute specified by the XQuery
exists and contains data. To start, this example declares a variable of the xrnl
type and uses a SELECT statement to store an XML document in this variable.
Then, it uses an IF statement to check if the variable has an EventType element
that contains data. To accomplish this, the IF statement checks if the int value
returned by the exist() method is equal to 1. If so, it prints a message that
indicates that the EventType element exists and contains data.
If you want to check an attribute instead of an element, you can use the same
skills. However, you must prefix the name of the attribute with an at sign (@)
like this:
IF ~EventData . exi st(
1
/EVENT_ I NSTANCE/TSQLCommand/SetOptions/@ANSI_ NULLS 1 ) = 1
Chapter 18 How to work with XML 597
Figure 18-5 How to use the methods of the xml data type (part 1 of 2)
598 Section 4 Advanced SQL skills
The third example shows how to use the value() method to return the value
of the specified element or attribute. To do that, you use the first argument of
the value() method to specify the XQuery string for the element or attribute.
Then, you use the second argument to specify the SQL Server data type that you
want to return. In this figure, for example, the varchar data type is used to store
the data for the EventType element and for the ANSI_NULLS attribute of the
SetOptions element. Since the value() method returns a single value, this method
requires its XQuery argument to specify the element or attribute number within
square brackets, even if the xml type contains only one attribute or element with
the specified name. As a result, the XQuery path must be enclosed in parentheses
whenever you use the value() method.
The fourth example shows how to use the modify() method to update the
data that's stored in an xml type. To start, this example declares a variable of
the xml type and uses a SELECT statement to store an XML document in this
variable. Then, it uses a SET statement to replace the value that's stored in the
EventType element of the xml variable with a string value of "TEST". To do
that, it uses the modify() method with an XML DML "replace value of' state-
ment. In addition, it uses the text() function to retrieve the string value that's
stored in the EventType element. Finally, a SELECT statement is used to retrieve
the modified value of the xrnl variable. Although you don' t typically use a
SELECT statement like this, you shouldn' t have any trouble understanding how
it works.
In addition to the "replace value of ' statement, you can use the XML
DML insert and delete statements to insert nodes into and delete nodes from an
xml type. To learn more about these statements, you can begin by looking up
"modify() Method (xml data type)" in the SQL Server documentation. Then, you
can follow the links to learn more about the XML DML statements.
Although you can use the modify() method to modify the data that's stored
in an xml data type, you should avoid extensive use of this method whenever
possible. Instead, you should consider storing the data in one or more tables.
Then, you can use the DML statements that are available from SQL to insert,
update, and delete the data in those tables.
Chapter 18 How to work with XML 599
SET @EventData.modify
('replace value of (/EVENT_ INSTANCE/EventType/text())[1] with "TEST"');
Description
• XQuery is a language that's designed to query an XML document.
• XML Data Manipulation Language (XML DML) is a language that's designed to
insert, update, or delete nodes from an XML document.
Figure 18-5 How to use the methods of the xml data type (part 2 of 2)
600 Section 4 Advanced SQL skills
'
Description
• You can use the value() method to parse an xml data type that has been stored in a
database into multiple columns.
A trigger that parses XML data and inserts it into the table
CREATE TRIGGER Database_ CreateTable_ DropTable2
ON DATABASE
AFTER CREATE_ TABLE, DROP_ TABLE
AS
DECLARE @EventData xml;
SELECT @EventData = EVENTDATA();
INSERT INTO DDLActivityLog2 VALUES
(
@EventData.value( 1
(/EVENT_INSTANCE/EventType) [1] 1 , 1 varchar(40) 1 ) ,
@EventData.value( 1
(/EVENT_INSTANCE/PostTime) [1] 1 , 1 Varchar(40) 1 ) ,
@EventData.value( 1
(/EVENT_INSTANCE/LoginName) [1] 1 , 1 varchar(40) 1 ) ,
@EventData.value( 1
(/EVENT_ INSTANCE/ObjectName) [1] 1 , 1 varchar(40) 1 )
);
Description
• You can use the value() method to parse an xml data type into multiple columns
before you store it in the database.
Figure 18-7 Another example that parses the xml data type
604 Section 4 Advanced SQL skills
Description
• Before you can use an XML Schema Definition with the xml data type, you must
add the XSD to the database. To do that, you can use the CREATE XML SCHEMA
COLLECTION statement.
Description
• To provide XML validation, you can specify an XSD for the xml data type when you
use it to define a column or a variable. Then, when you attempt to store data in the xml
data type, SQL Server will use the XSD to make sure the data is valid. If the data isn' t
valid, SQL Server will display an appropriate error message.
Figure 18-9 How to use an XML schema to validate the xml data type
608 Section 4 Advanced SQL skills
Description
• To view an XML schema that has been added to the database, you can use the
XML_SCHEMA_NAMESPACE function.
• To get information about XML schemas that have been added to the database, you
can query the sys.xml_schema_collections catalog view.
• To drop an XML schema from the database, you can use the DROP XML
SCHEMA COLLECTION statement. You can' t drop an XML schema if it's being
used by another object.
Description
• To specify the name of the root element for the XML document, use the ROOT
keyword.
• To return one XML element for each row, use the RAW keyword.
• To automatically parse the rows of the result set so the elements reflect the structure
of the tables in the database, use the AUTO keyword.
Figure 18-11 How to use the FOR XML cla use of the SELECT state me nt (pa rt 1 of 2)
612 Section 4 Advanced SQL skills
As you saw in the first two examples in figure 18-11, the data for each
column that's returned by a SELECT statement that includes the FOR XML
clause is stored in attributes. However, if you want to store the data for each
column in XML elements instead, you can add the ELEMENTS keyword to the
FOR XML clause. The third example in this figure, for instance, is identical to
the second example except that it includes the ELEMENTS keyword. Although
the XML document that's returned by this example is longer than in the previous
example, it's also easier to read because it uses elements instead of attributes. In
some cases, you may prefer to use this type of XML document.
Most of the time, the skills presented in this figure are all you need to use
the FOR XML clause to create XML documents from the relational data in a
database. However, several other options are available from this clause that give
you even more control over the format of the XML document that's returned
by the SELECT statement. For more information, look up "FOR XML (SQL
Server)" in the SQL Server documentation.
Chapter 18 How to work with XML 613
Description
• By default, the data that's stored in a database column is stored in an XML
attribute. To store data in XML elements instead, use the ELEMENTS keyword.
• The names that are used for elements and attributes correspond to the names of the
tables and columns that are used in the query. However, you can specify correlation
names for the tables and columns in your query to modify these names whenever
that's necessary.
Figure 18-11 How to use the FOR XML clause of the SELECT statement (part 2 of 2)
614 Section 4 Advanced SQL skills
-- SELECT the data from the table returned by the OPENXML statement
SELECT *
FROM OPENXML (@VendorinvoicesHandle, '/Vendorinvoices/Vendor/Invoice')
WITH
(
VendorName varchar(SO) ' •• /VendorName' ,
InvoiceNumber varchar(SO) 'InvoiceNumber•,
InvoiceTotal money 'InvoiceTotal'
) ;
Description
• Before you can use the OPENXML statement, you must use the
sp_Xml_PrepareDocument procedure to read the XML document into memory and
return a handle to this document.
• You can use the OPENXML statement to open an xml data type as a result set. In
the WITH clause, you can use two dots(..) to navigate back one level in the XML
hierarchy.
• After you use the OPENXML statement, you should use the
sp_Xml_RemoveDocument procedure to remove the XML document from
memory.
Perspective
XML is a useful technology for storing and transferring data, and the
features that SQL Server provides make it easy to work with XML. Of course,
SQL Server is a relational database that is primarily designed to work with
relational data. As a result, you should think twice before using the xml data
type to store XML data in a table.
Instead, whenever possible, you should parse the XML data and store
it in one or more tables. That way, you can still use SQL to easily retrieve
and update the data. However, if the data is structured in a way that makes it
difficult to store in tables, or if you won' t need to retrieve or update the data
very often, you can use the skills presented in this chapter to store and work
with that data.
Terms
Extensible Markup Language (XML) XML Schema Definition (XSD)
XML document XML schema
tag untyped XML
element XMLEditor
start tag method
end tag X Query
attribute XML Data Manipulation Language
content (XMLDML)
child element typedXML
parent element XML validation
root element XPath
Chapter 18 How to work with XML 617
Exercises
1. Write a SELECT statement that returns an XML document that contains
all of the invoices in the Invoices table that have more than one line item.
This document should include one element for each of these columns:
InvoiceNumber, InvoiceDate, InvoiceTotal, InvoiceLineltemDescription, and
InvoiceLineltemAmount. Then, save the XML document that's returned in a
file named MultipleLineitems.xml. Finally, generate an XML schema for the
file and save it in a file named MultipleLineltems.xsd.
2. Write a script that uses the XML document shown below to update the contact
information in the Vendors table.
<ContactUpdates>
<Contact VendoriD="4">
<LastName>McCrystle</LastName>
<FirstName>Timothy</FirstName>
</Contact>
<Contact VendoriD="10">
<LastName>Flynn</LastName>
<FirstName>Erin</FirstName>
</Contact>
</ContactUpdates>
To accomplish this, begin by storing this XML document in a variable of the
XML type. Then, you can use two UPDATE statements to update the Vendors
table.
3. Write a script that returns a result set that contains all of the data stored in the
XML document in exercise 2.
4. Write a script that (1) creates a table named Instructions, (2) inserts the XML
document shown below into the table, and (3) selects all rows from this table.
The Instructions table should have two columns. The first column should be
an identity column named InstructionsiD, and the second column should be
an xml column named Instructions.
<Instructions>
<Step>
<Description>This is the first step.</Description>
<SubStep>This is the first substep . <SubStep>
<SubStep>This is the second substep.<SubStep>
</Step>
<Step>
<Description>This is the second step.</Description>
</Step>
<Step>
<Description>This is the third step . </Description>
</Step>
< /Instructions>
19
An introduction to BLOBs
Figure 19-1 describes the pros and cons of three approaches that you can use
for working with large binary values, which are often referred to as binary large
objects (BLOBs).
The second approach was introduced with SQL Server 2005, and it
solves the problems of storing a pointer to a file by storing the binary data in
a varbinary(max) column within the database. As a result, the binary data is
backed up when the database is backed up, and access to this data is controlled
by database permissions.
However, this approach has two limitations. First, the binary data must be
less than 2 gigabytes (GB). Second, database access is not as fast as file system
access. As a result, you can' t use this approach if you need to store a BLOB
that's larger than 2GB , and you won' t want to use it if fast access to your BLOBs
is critical to your application.
Description
• With SQL Server 2008 and later, you can use a feature known as FILESTREAM
storage to overcome several limitations for working with binary large objects
(BLOBs) that existed in previous versions of SQL Server.
• To be able to use FILESTREAM storage, the drive that stores the files must be in
NTFS format.
3 3 3 Ox30313233343536373839414243444546
Description
• You can use the varbinary(max) data type for a column that stores binary data.
A .NET application
that uses a varbinary(max) column
Once you understand how to use SQL to work with binary data that's stored
in a varbinary(max) column, you can use a .NET application to write binary data
that's in a file to a column in a database. Then, you can use a .NET application
to present binary data in a way that's meaningful to the user. In this chapter, for
example, you'lllearn how to read binary data for an image from a column in the
database and display it in a picture box control on a Windows form.
This topic presents a simple application that uses C# to work with BLOBs.
All of the principles in this application apply to Visual Basic and the other .NET
languages. The differences mainly have to do with the syntax of each language.
If you prefer to use Visual Basic, you can download the Visual Basic code for
this application from our website (see appendix A).
If you have some C# programming experience, you shouldn' t have much
trouble understanding this code. If you don' t have C# experience, that's fine
too. In that case, you can focus on how this code uses the .NET Framework to
execute SQL statements against a database. Then, if you want to learn more
about writing C# applications, we recommend our current book on C#. For more
information, please see www.murach.com.
lmaoeiD: ~
Padd..foot
Product 10: ~
Upload
Note: The mage fie rrust be in the C:/IIUac:h/ SQL Se<ver 2019/lmages chctory
Description
• To view an image, select the ID for the image from the combo box.
• To upload an image, enter a filename and a product ID for the image, and click on
the Upload button.
Figure 19-3 The user interface for the Product Image Manager application
626 Section 4 Advanced SQL skills
II read image bytes from the database and display in picture box
Byte[] imageByteArray = ProductDB.Readimage(imageiD);
MemoryStream ms =new MemoryStream(imageByteArray);
imagePictureBox.Image = System.Drawing.Image.FromStream(ms);
ms .Close ();
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "Error");
}
}
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "Error");
}
}
Figure 19-4 The event handlers for the Product Image Manager form
628 Section 4 Advanced SQL skills
namespace MusicStoreimageManager
{
class ProductDB
{
II The directory for the images
static string imagesPath = "C:IMurachiSQL Server 20191Imagesl";
connection.Open();
command.Exec uteNonQuery();
}
catch (Exception e)
{
throw e;
}
finally
{
if (connection != null)
connection.Close();
}
}
connection.Open();
SqlDataReader reader = command.ExecuteReader();
return imageByteArray;
}
catch (Exception e)
{
throw e;
}
finally
{
if (connection I= null )
connection.Close();
}
}
connection.Open();
SqlDataReader reader = command.ExecuteReader();
return imageiDList;
}
catch (Exception e)
{
throw e;
}
finally
{
if (connection I= null)
connection.Close();
}
}
OK Cancel Apply
Description
• By default, FILESTREAM storage is disabled for the server.
Description
• If you want to use the FILESTREAM feature, you must create a database that
includes a file group that provides for FILESTREAM storage.
Description
• To create a table that provides for FILESTREAM storage, you include a column
definition that contains the FILESTREAM keyword. In addition, you create a
column with a globally unique identifier (GUJD) that's used to locate the file that
stores the FILESTREAM data.
• A GUID is a value that's unique within the current database and other networked
versions of the database around the globe. To define a column that contains a
GUID, you specify the uniqueidentifier data type for the column, and you specify
the ROWGUIDCOL property for the column.
• You can use the NEWID function to generate a globally unique value.
• You can use the PathName function to return the path to the binary file stream. This
function is case sensitive, so you must use exact capitalization.
won' t return a path to the file stream, and the application won' t be able to write
data to the file stream.
namespace MusicStoreimageManager
{
class ProductDB
{
II define the directory for the images
static string imagesPath = "C:IMurachiSQL Server 20191Imagesl";
connection.Open();
command.ExecuteNonQuery();
After executing the query that inserts the row into the database, you must
get a reference to the file stream for the BLOB. To do that, you begin by
defining a SELECT statement that returns two columns. The first column uses
the PathName function to return the path to the file stream. The second column
uses the GET_FILESTREAM_TRANSACTION_CONTEXT function to get the
context for the transaction. Note that the GUID value that was created earlier in
this method is used in the WHERE clause of this SELECT statement to specify
the row that was inserted by the INSERT statement earlier in the same method.
After the SELECT statement is defined, this code executes this statement.
Then, it uses the reader object that's returned to check whether the result set
contains data. If so, the first column is stored in a variable named path, and the
second column is stored in a variable named context. Finally, this code closes the
reader object.
At this point, the Writeimage() method has all the data it needs to set up
an output stream to the database. To do that, it creates a FileStream object with
write access.
Now that the Writeimage() method has an input stream and an output
stream, it's ready to read data from the input stream (the image file) and write
data to the output stream (the BLOB in the database). To do that, this code
defines a block size of half a megabyte (524,288 bytes), a size that's usually
efficient for working with streams. Then, this code defines a buffer variable that
stores this array of bytes. Finally, it uses a loop to read from the input stream and
write to the output stream.
Note that this allows this class to read half of a megabyte into memory at a
time. For example, let's assume that you have an image file that's 4 megabytes.
In that case, it would take eight trips through the loop to write the file to the
database. On the other hand, the Writeimage() method presented earlier in this
chapter reads the entire file into memory before it begins to write the file. As a
result, it can use a lot of memory if you use it with images that are larger than 1
megabyte.
Note also that this method uses a transaction. If the method completes
successfully, the last statement in the try block commits the transaction, and the
image is written to the database. However, if the method encounters an error,
the catch block rolls back the transaction, and the image is not written to the
database.
Chapter 19 How to work with BLOBs 643
targetStream.Close();
sourceStream.Close();
transaction.Commit();
}
catch (Exception e) {
if (transaction != null)
transaction.Rollback();
throw e;
}
finally {
if (connection != null)
connection.Close();
}
}
SqlFileStream sourceStream =
new SqlFileStream(path, context, FileAccess.Read);
Perspective
In this chapter, you learned how to write BLOBs to a database table and
to read BLOBs from a database table. In addition, you learned how to use the
FILESTREAM storage feature. At this point, you have the core concepts and
skills for working with BLOBs.
Now, if you want to develop a more sophisticated application for working
with BLOBs, you should be able to do that. For example, you may want to
enhance the application presented in this chapter so it can be used to update
or delete an existing image. Or, you may want to enhance this application so it
works more efficiently.
Terms
binary large object (BLOB)
FILESTREAM storage
globally unique identifier (GUID)
1. Modify the first ProductDB class presented in this chapter so the Readimage()
method accepts a PictureBox control as a second argument like this:
public static void Readimage(int imageiD, PictureBox pictureBox)
Then, modify the Readimage() method so it streams data from the database to
the PictureBox control, and modify the code for the form so it works with this
new method. To do this, you can begin by copying code from the form into
the ProductDB class.
2. Modify the second ProductDB class so it works as described in exercise 1.
Appendix A
How to set up your
computer for this book
To run the SQL statements described in chapters 1 through 18 of this book,
you only need to have two software products installed: the SQL Server 2019
database engine and the SQL Server Management Studio (SSMS). Both
of these products are available from Microsoft's website for free, and you
can download and install them both on your computer as described in this
appendix.
Once you install these software products, you can install the files for this
book. To do that, you can download these files from www.murach.com. Then,
you can use the Management Studio to create the databases for this book.
After that, you can start experimenting with the SQL scripts for this book.
To use a .NET language such as C# or VB to work with BLOBs as
described in chapter 19, you can use Visual Studio. If you don' t already have
Visual Studio installed on your system, you can install Visual Studio 2019
Community as described in this appendix. This product is also available from
Microsoft's website for free.
Description
• For this book, we recommend that you install the Express edition of SQL Server
2019, but you can install the Express with Advanced Services edition if you want to
install the advanced features and don' t mind a larger download and install.
Notes
• This book assumes that an instance of SQL Server 2019 Express is installed with a
name of SQLEXPRESS. If it's installed on your system with a different name, you
may have to modify some of the examples to get them to run successfully on your
computer.
• SQL Server 2019 supports Windows 10 and later. Support for Windows 8 has been
dropped.
Chapter 2 presents the basic techniques for using the Management Studio.
You can use it to develop and run all of the SQL statements in this book.
The Databases directory contains the SQL scripts used to create the three
databases that are used throughout the book. To do that, you can use the
Management Studio to open and execute these scripts as described in the next
figure.
The Scripts directory contains the SQL code that's described throughout
this book. You can use the Management Studio to open these scripts. Then, you
can run them to view the results. Or, you can experiment with these scripts by
modifying them before you run them.
The Exercises directory contains the solutions to the exercises that are
presented at the end of each chapter. You can use these solutions to check that
the solutions you develop are correct. You can also use these solutions to find out
how to solve an exercise if you're unable to do it on your own. Keep in mind,
though, that you' ll get more out of the exercises if you try to solve them on your
own first.
The Projects directory contains two subdirectories that contain the Visual
Studio projects for chapters 1 and 19. These projects are available in two
versions: a C# version and a Visual Basic version. You can use Visual Studio
Community to open these projects.
Appendix A How to set up your computer for this book 653
Description
• All of the files for the databases and code described in this book are contained in a
self-extracting zip file (an exe file) that can be downloaded from www.murach.com.
The directory that contains the scripts for creating the databases
C: \ Murach\SQL Serv er 2 019 \ Data b ase s
L.; creat~_ap ql- loulhost:\ SQLEXPRESS.m aster (murach ne (53))- Microsoft SOL Se:rver Manageme:nt St... Quick launch (Ctri•Q)
llll Metsages
C1 row e f fec~ed )
C1 row ettecud)
1110% •
> (!) Qutry executed succ6sfully. localhost\SQlEXPRESS (15.0 ... murach\Anne ($3) master 00:00:02 0 rows
Description
• For these scripts to work, the database server must be running. By default, the
database server is automatically started when you start your system. If it isn' t
running on your system, you can start it as described in chapter 2.
Figure A-4 How to create and restore the databases for this book
656 Appendix A How to set up your computer for this book
Description
• You only need to install Visual Studio for chapter 19 of this book. If you don' t
already have Visual Studio installed on your system, you can install Visual Studio
Community, which is available for free from Microsoft.
properties, 66, 67, 378, 379 Correlated subquery, 196, 197, 202, 203
qualified name, 126, 127 Correlation name, 128, 129, 196, 197
specification, 90, 9 1 COUNT function, 160- 163
Column-level constraint, 346, 347 Covering index, 3 18
Command line switches, 452, 453 CREATE APPLICATION ROLE statement, 572, 573
Command object (ADO.NET), 40, 41 CREATE DATABASE statement, 25, 335, 338, 339
Command prompt utilities, 452, 453 CREATE FUNCTION statement, 335, 486-491
Comment, 32, 33 CREATE INDEX statement, 25, 335,342,343
Commit a transaction, 5 10, 5 11 CREATE LOGIN statement, 540,54 1
COMMITTRAN statement, 512-5 17 CREATE PROC statement, 462-467
Common table expressions (CTE), 208-2 11 CREATE PROCEDURE statement, 36, 37, 335
Comparison operator, I 04, I 05 CREATE ROLE statement, 566, 567
Compatibility level (database), 62, 63 CREATE SCHEMA statement, 546, 547
Compile, 460, 461 CREATE SEQUENCE statement, 356, 357
Completion list, 70, 71 CREATE SERVER ROLE statement, 560, 561
Complex query, 204-207 CREATE TABLE statement, 25, 335, 340, 341
Complex search condition, 170, 17 1 CREATE TRIGGER statement, 335, 494, 495
Component (business), 8, 9 CREATE TYPE statement, 478, 479
Components (client/server system), 4-7 CREATE USER statement, 544, 545
Composite index, 318, 3 19 CREATE VIEW statement, 34, 35, 335, 396, 397,
Composite primary key, 10, 11 400-403
Compound condition, 106, 107 CREATE XML SCHEMA COLLECTION statement,
Compound j oin condition, 132, 133 604,605
Compound search condition, I 06, 107, 170, 17 1 Criteria pane, 76, 77
Computer-aided software engineering (CASE), 3 10, 3 11 CROSS JOIN keywords, 148, 149
CONCAT functio n, 262-265 CTE, 208-211
CONCAT_WS function, 262-265 recursive, 2 10, 2 11
Concatenation, 94, 95 CUBE operator, 174, 175
Concurrency, 5 18-523 CUME_DIST function, 296-299
defined, 5 19 Cumulative total, 178, 179
problems with, 520, 52 1 Cursor, 436, 437
Condition CYCLE keyword
compound, 106, 107
compound join, 132, 133
join, 126, 127 D
Conditional processing, 430, 43 1
Data
Configurati on (network), 52, 53
Configuration Manager, 50-53 consistency, 500, 501
derived, 326, 327
Conformance with SQL-92, 18, 19
redundancy, 3 16, 3 17
Connect to server, 54, 55
structure, 304, 305, 3 16, 3 17, 328, 329
Connecting table, 3 12, 3 13
validation, 470-477
Connection o bject (ADO.NET), 40, 4 1
Data access API, 6, 7
Consistency (data), 500, 501
Data access model, 38, 39
Constant, see Literal
Data adapter (ADO.NET), 40, 4 1
Constraint, 346, 347
Data definition language (DOL), 22, 23, 334, 335
column-level, 340, 34 1
Data element, 306-309
compared to trigger, 500, 50 1
subdivide, 308, 309
FOREIGN KEY, 3 14, 3 15
Data files for a database, 376, 377
NOT NULL, 340, 34 1
Data integrity, 5 10, 5 11
PRIMARY KEY, 340, 341
Data manipulation language (DML), 22, 23
table-level, 340, 341
Data reader, 40, 4 1
UNIQUE, 340, 34 1
Data table (ADO.NET), 40, 4 1
Content (XML), 589
Data type, 14, 15, 240-249
CONTINUE statement, 421, 434, 435
and disk storage, 14
Control character, 258, 259
and performance, 14
Control-of-flow language, 36, 37
ANSI-standard, 240, 241
Conventional file system, 16, 17
comparing expressions, 106
Conversion (data type), 250-257
conversion, 250, 25 1
CONVERT function, 98, 99, 254, 255
order of precedence, 250, 25 I
Core specification (SQL-99 standard), 18, 19
662 Database DISTINCT keyword
ON clause p
MERGE statement, 234, 235
trigger, 494, 495 Package (SQL-99 standard), 18, 19
ON DELETE clause, 350, 35 1 Page lock, 524, 525
ON phrase, 126, 127 Parameter, 98, 99, 458,459, 462-467, 484
ON PRIMARY clause, 338, 339 pass a table, 478, 479
ON UPDATE clause, 350, 35 1 pass a value, 466, 467
One-to-many relationship, 12, 13, 3 12, 3 13 stored procedure, 36
One-to-one relationship, 12, 13,3 12,3 13 Parent element (XML), 588, 589
Online help, see SQL Server documentation Parent/child relationship, 16, 17
Open-source database, 20, 2 1 Parentheses, 96, 97, 106, 107
OPENXML statement, 6 14, 6 15 Parsing, 266, 267
Operating syste m Partially-quali fied object name, 130, 13 1
AIX, 2 1 PARTITION BY clause
Linux, 20, 2 1 aggregate fu nction, 178, 179
Unix, 20, 2 1 analytic fu nction, 296, 297
Windows, 20,21 ranking function, 292, 293
zJOS, 2 1 Pass a parameter, 466, 467
Operators Password (strong), 540, 541
addition, 97 PathName function, 638, 639
AND, 106, 107 PATINDEX function, 262-265
arithmetic, 96, 97 Pattern (string), 112, 113
comparison, l 04, I 05 PERCENT keyword, I 02, 103
concatenation, 94, 95 PERCENT_RAN K function, 296-299
division, 97 PERC ENTILE_CONT function, 296-299
equal, 104, 105 PERCENTILE_DISC function, 296-299
greater than , 104, 105 Permission (defined), 536, 537
greater than or equal to, 104, 105 Permissions, 548-557
less than, 104, I05 database, 554, 555, 582, 583
less than or equal to, 104, 105 object, 550, 55 1
logical, 106, 107 schema, 552, 553
modulo, 97 server, 556, 557
mul tiplication, 97 Phantom read, 520-523
NOT, 106- 11 5 Position (passing parameters by), 466, 467
not equal, I 04, I 05 PostgreSQL database system, 20
OR, 106, 107 PowerShell (Windows), 452
order of precedence, 96, 97, I 06, I07 Precedence, 96, 97
string, 94, 95 Precisio n, 242, 243
subtraction, 97 Precompile, 460, 46 1
UNION, 150, 15 1, 404, 405 Predicate, 86, 87
Optional parameter, 464-467 subquery, 184, 185
OR operator, 106, 107 Primary key, I0, I I, 382, 383
Oracle, 18-2 1 and referential integrity, 3 14, 315
ORDER BY clause, 86, 87, 102, 103, 11 6- 12 1 composite, 10, ll
by string column, 266, 267 constraint, 340, 34 1,346, 347
in view, 400-403 how to identify, 3 12, 3 13
of a ranking function, 292-295 PRINT statement, 420, 42 1
Order of precedence Procedural programming, 458, 459
data types, 250, 25 1 Promote a lock, 526, 527
operators, 96, 97 Properties of a column, 66, 67
ORM framework, 38, 39 Pseudocode, 206, 207
Orphan, 3 14, 3 15 Public database role, 564, 565
OS/390 operating system 20, 2 1 public server role, 558
OSQL utility, 452, 453
Outer join, 28, 29, 140-145
combined with inner j oin, 146, 147 Q
examples, 142, 143
explicit syntax, 140, 141 Qualified column name, 126, 127
more than two tables, 144, 145 Qualified object name, 130, 13 1
Output parameter, 36, 464-467 Qualifier (scope), 552, 553
OYER clause, 178, 179
Query RTRIM function 669
s Server, 4, 5
application , 8, 9
Save a change script, 390, 39 1 connect, 54, 55
Save point, 5 12, 513, 5 16, 5 17 database, 50, 51
SAVE TRAN statement, 512, 5 13 linked, 130, 13 1
Scalar aggregate, 164, 165 login, 54, 55
Scalar function, 160 permissions, 556, 557
Scalar variable, 422, 423 software, 6, 7
Scalar-valued functi on (user-defined), 484-487 web, 8, 9
Scale, 242, 243 Server authentication, 54, 55
Scan (table), 3 18 Server name, 130, 131
Schema, 400, 40 I Server permission, 536
default, 56 Server permissions (DENY), 570, 57 1
lock, 526, 527 Server role, 576, 577
name, 130, 13 1 fi xed,558,559
permission, 536, 552, 553 information, 562, 563
view, 41 2, 41 3 user-defined, 560, 56 1
working with, 546, 547 Service (web), 8, 9
Schema (XML), 590,59 1 Service Manager, 50, 51
Schema Modification (Sch-M) lock, 526, 527 Services (SQL Server), 52, 53
Schema permissions (DENY), 570, 571 Session setting, 444, 445
Schema Stability (Sch-S) lock, 526, 527 Set (result), 26, 27
SCHEMABINDING keyword, SET clause (UPDATE), 224, 225
UDF, 486, 487 SET statement, 421-423
view, 400-403, 406, 407 ANSI_NULLS, 444, 445
Scientific notation, 242 ANSI_PADDING, 445
Scope DATEFORMAT, 444, 445
table objects, 428, 429 NOCOUNT, 445
variable, 422 ROWCOUNT, 444, 445
Scope quali fier, 552, 553 TRANSACTION ISOLATION LEVEL, 522, 523
Script, 368-371 , 4 18-447,458,459 Shared (S) lock, 526, 527
change,390,391 Shared locks, 526, 527
generating, 388-39 1 Shared with Intent Exclusive (SIX) lock, 527, 528
Script to create the AP database, 368-37 1 Significant digit, 242, 243
Search Simple table-valued function, 484, 485, 488, 489
by date value, 280, 28 1 Single precision number, 242, 243
by time value, 282, 283 Single quotes
for null, 114, 115 for string literal, 94, 95
for real numeric value, 270, 27 1 in column name, 92, 93
Search condition, 86, 87 within string literal, 94, 95
compound, I06, I07, 170, 17 1 Single- line comment, 32, 33
subquery, 188- 199 Sixth normal form, 320, 32 1
Second normal form, 320, 32 1, 324, 325 smalldatetime data type, 246, 247
Securables, 536, 537 smallint data type, 242, 243
Security, 536-583 smallmoney data type, 242, 243
Transact-SQL compared to Management Studio, SNAPSHOT isolation level, 120
536,537 SNAPSHOT keyword, 522, 523
using view, 398, 399 Snippets, 344, 345
securityadmin role, 558, 559 surround-with, 440, 44 1
SELECT clause, 86- 10 I Software components (client/server system), 6, 7
SELECT statement, 26, 27, 86- 1 19 SOME keyword, 194, 195
and subquery, 202, 203 Sort, see ORDER BY clause
in view, 400, 40 I sp_AddLinked Server, 130, 13 1
INTO, 2 16, 217, 448, 449 sp_AddRoleMember, 565
variable assignment, 422, 423 sp_AddSrvRoleMember, 559
Self-j oin, 134, 135 sp_Columns, 483
SEQUEL, 18, 19 sp_DropRoleMember, 565
Sequence,356-359 sp_DropServer, 13 1, 132
SERlALIZABLE keyword, 522, 523 sp_DropSrvRoleMember, 559
sp_Help, 483
sp_HelpDb Statements 671
NV\~
Mike Murach, Publisher
~~
Ben Murach, President
~ www.murach.com
rl twitter.com/MurachBooks
(I 1-800-221-5528 ~ facebook.com/murachbooks
cqw
(Weekdays, 8 am to 4 pm PacificTime)
1m linkedin.com/company/
mike-murach-&-associates
[email protected]
(® instagram.com/murachbooks
The software for this book
SQL Server 2019 Express (a free download)
SQL Server Management Studio (a free download)
Visual Studio Community (a free download)
SQL Server 2019 only runs on Windows 10 and later. As a result, if you're using
Windows 8, you'll need to upgrade your operating system before you can install
SQL Server 2019 Express.
For information about downloading and installing these products, please see
appendix A.
www.murach.com fm m
ltMurachBooks
$59.50 USA
Shelving: Programming/Database/SOL