93
A Beginner’s Guide To SQL How to hold a meaningful conversation with a database June 2014 Charlotte McGary

A Beginner's Guide To SQL - charlotteswebworld.comcharlotteswebworld.com/docs/SQL_Primer.pdf · A Beginner’s Guide To . SQL . How to hold a meaningful conversation with a database

  • Upload
    dodat

  • View
    223

  • Download
    0

Embed Size (px)

Citation preview

A Beginner’s Guide To

SQL

How to hold a meaningful conversation with a database

June 2014

Charlotte McGary

Copyright © 2013 Charlotte McGary. All Rights Reserved. No portion of this book may be reproduced by any means whatsoever without the expressed written permission of the author.

i

Table of Contents LESSON 1 ...................................................................................................... 1

An Overview of Databases ............................................................................................. 2 How data is stored ....................................................................................................... 2

Tables ...................................................................................................................... 2 Columns .................................................................................................................. 2 Rows ....................................................................................................................... 3 Primary Keys .......................................................................................................... 3 Data types................................................................................................................ 3 NULL Values .......................................................................................................... 5 Schemas and Owners .............................................................................................. 5 Comments ............................................................................................................... 5

Asking simple questions with SQL................................................................................. 6 Selecting data .............................................................................................................. 6

USE keyword syntax............................................................................................... 6 SELECT keyword syntax ....................................................................................... 6 FROM keyword syntax ........................................................................................... 7 Running your first query ......................................................................................... 7

Sorting data ................................................................................................................. 8 The WHERE clause .................................................................................................. 10 Operators ................................................................................................................... 11

Examples ............................................................................................................... 11 Wildcards .................................................................................................................. 13 The LIKE operator .................................................................................................... 13 The IN operator ......................................................................................................... 15

Review 1 ....................................................................................................................... 16 LESSON 2 ....................................................................................................19

Using Aliases ................................................................................................................ 20 Calculating data ............................................................................................................ 21

Data Type Precedence............................................................................................... 21 The COUNT function ............................................................................................... 22 The DISTINCT Keyword ......................................................................................... 22 The AVG function .................................................................................................... 23 The SUM function .................................................................................................... 24 The MIN and MAX functions ................................................................................... 24

Converting Data Types ................................................................................................. 26 CAST( ) and CONVERT( ) ...................................................................................... 26 Converting to the FLOAT Data Type ....................................................................... 27 Converting to the DECIMAL Data Type.................................................................. 27 Converting to the INT Data Type ............................................................................. 28 The ROUND( ) Function .......................................................................................... 29

ii

Conversion Tips ........................................................................................................ 29 The GROUP BY Clause ............................................................................................... 31

The HAVING clause................................................................................................. 33 Review 2 ....................................................................................................................... 36

LESSON 3 ................................................................................................... 38 Retrieving data from multiple tables............................................................................. 39

Inner Joins ................................................................................................................. 43 Outer Joins ................................................................................................................ 46

LEFT and RIGHT keywords ................................................................................ 47 Self Joins ................................................................................................................... 49

Concatenating Strings ................................................................................................... 51 Review 3 ....................................................................................................................... 52

LESSON 4 ................................................................................................... 56 Combining query results ............................................................................................... 57

The UNION operator ................................................................................................ 57 Using Static Strings for Column Values ................................................................... 58

Date and Time Functions and Conversions .................................................................. 60 Date and time functions ............................................................................................ 60

The GETDATE() function .................................................................................... 60 The DATEPART() function ................................................................................. 60

Converting dates to strings........................................................................................ 63 More Date Conversion Examples ............................................................................. 66

Review 4 ....................................................................................................................... 67 APPENDICES ............................................................................................. 69

Appendix A: SQL Server Management Studio ............................................................ 70 Accessing the DBMS ................................................................................................ 70 Connecting to your database server .......................................................................... 70

Appendix B: The Pubs Database .................................................................................. 73 Working with pubs ........................................................................................................ 73 Appendix C: Solutions to Review Exercises ................................................................ 74

Review 1 ................................................................................................................... 74 Review 2 ................................................................................................................... 77 Review 3 ................................................................................................................... 80 Review 4 ................................................................................................................... 86

Appendix D: Additional Resources .............................................................................. 89 Books ........................................................................................................................ 89 Web Tutorials............................................................................................................ 89

A Beginner’s Guide to SQL/Charlotte McGary 1

LESSON 1 In this lesson you will learn the about how data is stored in a database and how to retrieve information from a database with simple SQL queries.

2 A Beginner’s Guide To SQL/Charlotte McGary

An Overview of Databases Simply put, a database is an organized collection of related data. For example, a library might store data related to book titles, authors, and who currently has a book that is checked out. An astronomer would likely store data related to stars, such as name, type, location, and so on. The data could go into files in a file cabinet, but isolation the information you need from a large amount of data would be slow and tedious. You can think of a database stored electronically as a virtual file cabinet, and SQL as a way of finding needed information quickly and efficiently. We’ll discuss what SQL is shortly. Don’t confuse the term database with Database Management System (DBMS). The database contains all the data, whereas the DBMS is the software used to create, change, and query the database. The examples in this book use Microsoft SQL Server Management Studio to access the sample databases.

How data is stored There are methods for storing data in a database, just like there are methods for filing folders in a filing cabinet. This section discusses the basic structure of a database and the rules it must follow.

Tables If you’ve ever seen an Excel spreadsheet, then a typical database structure will look familiar. A database is usually divided into separate tables with a grid structure, each table containing related data (like a file in a file cabinet). For example, a car dealership would have one table for customers and a separate one for inventory. The customer table can contain only customer information, and the inventory table can contain only information about cars in the dealer’s inventory. Most of the information you need from a database will be stored in tables. You can retrieve information from a single table (the simplest query) or multiple tables. This lesson will cover querying single tables.

Columns A column is one field in a table. Many examples in this book use a publisher’s database named pub that contains an authors table with the following columns (fields):

au_id au_lname au_fname address city state zip contract

A Beginner’s Guide To SQL/Charlotte McGary 3

NOTE: Column names cannot have any embedded spaces. Putting spaces in names can cause all sorts of difficulties. Therefore, to separate words in table and field names, use an underscore ( _ ) instead of a space.

Rows A row is one record of data, similar to a row of data in Microsoft Excel. Using our authors table from the previous example, the following two records show five of the eight columns of data related to authors Abraham Bennet and Marjorie Green.

au_id au_lname au_fname city state 409-56-7008 Bennet Abraham Berkeley CA 213-46-89152 Green Marjorie Oakland CA

Primary Keys In order to get accurate information from your database, there has to be some way to uniquely identify each row in a table. This is accomplished using a “primary key.” In the authors table, the primary key is the au_id field. This means that each value in the au_id column must be unique within that table.

NOTE: • If you delete a record, you cannot reuse that primary key for any other record in the

table. • You cannot change the value of a primary key once it’s been entered. • A primary key cannot be a NULL value.

Data types Each field has a specific data type associated with it. The data type determines what type of data is allowed in that column. In the above example, the state column allows only character values. A list of commonly used data types follows:

Data Type Description Example CHAR(n) Fixed-length string of n characters. A field containing a state abbreviation

could have a data type of CHAR(2) with an example value of “WA”.

VARCHAR(n) Variable-length string with maximum of n characters.

To allow a string of up to 20 characters for a person’s last name, you would create that field with the data type VARCHAR(20).

4 A Beginner’s Guide To SQL/Charlotte McGary

Data Type Description Example TINYINT Integer value ranging from 0

through 255. If you have fields storing red, green, and blue HTML color values, which can only range from 0 to 255, you could create each of those three fields with the data type TINYINT.

SMALLINT Integer value ranging from -32,768 through 32,767.

If you have fields for X and Y coordinates that are limited to this number range, you could create these fields with the SMALLINT data type.

INT Integer value ranging from -2,147,483,648 through 2,147,483,647.

If you have a field storing the population count for a single country as a whole number, you could use the INT data type.

FLOAT(p) Floating point value with precision p, where p ranges from1-24 (uses up 4 bytes/7 digits) or 25-53 (uses 8 bytes/15 digits). Therefore the values can range from -1.79E + 308 through -2.23E - 308, 0 and 2.23E + 308 through 1.79E + 308.

If your field stores the population of the whole planet (currently 6,973,738,433), you would need a data type that can hold a number larger than the maximum INT value. In this case, you could use the FLOAT data type.

DECIMAL(p,s) Fixed precision decimal value with a maximum of s digits to the right of the decimal point a maximum of p digits total (p = number of digits left of decimal point + s). Values can range from -10^38 + 1 through 10^38 – 1.

If want to store or display a real number value with a fixed number of digits after the decimal point, you would need to use the DECIMAL data type. A value such as Pi displayed to decimal places (3.141592) would need a data type of at least DECIMAL(7,6).

DATETIME Date and time with date ranging from January 1, 1753, through December 31, 9999 and time ranging from 00:00:00 through 23:59:59.997

For a field which contains the date and time a student registered for a class, use the DATETIME data type.

DATE Date only, ranging from 0001-01-01 through 9999-12-31.

For a field containing a person’s date of birth, you can use the DATE data type.

TIME Time only, ranging from 00:00:00.0000000 through 23:59:59.9999999.

For a field containing the time in 24-hour format, use the TIME data type.

MONEY Monetary value (8 bytes) ranging from -922,337,203,685,477.5808 through 922,337,203,685,477.5807.

For a field containing values related to the US government’s deficit, we can only hope we never need a data type larger than MONEY.

SMALLMONEY Monetary value (4 bytes) ranging from -214,748.3648 through 214,748.3647

For a field containing entries in a personal checking account, such as six dollars and fifty cents (6.50) the SMALLMONEY datatype will be sufficient for 99% of us.

A Beginner’s Guide To SQL/Charlotte McGary 5

NOTE: CHAR vs. VARCHAR Unless you have a fixed-length data type such as a zip code, state abbreviation, or a phone number, it is more efficient to use the VARCHAR data type for strings. The CHAR data type will reserve storage space for the maximum number of declared characters, whether or not they are all used (fills unused area with trailing spaces). The VARCHAR data type only store the characters actually used, saving memory and disk storage. You can see the data type of each column in our authors table by clicking on the plus (+) symbol to the left of each of the following in the SQL Server Management Studio Object Explorer pane: +Databases +pubs +Tables +dbo.authors +Columns

NULL Values The term NULL value means no data has been entered. This is not the same as a space character or a zero, as both “ ” and “0” represent actual values. If you create a local database, it is tied to you (the owner) and will not be accessible to others at a global level. These tables are usually prefaced with “dbo.” (data base owner), the default database user.

Schemas and Owners Tables must follow certain rules that are instituted at the time the table is created. For example, a schema will define how tables can be related, what type of data can be stored, whether NULL values are allowed, and so on. If you create a local database, it is tied to you (the owner) and will not be accessible to others at a global level. These tables are usually prefaced with “dbo.” (data base owner), the default database user.

Comments It is often helpful to add comments to your saved queries, especially when you have a long series of SQL statements. Comments are used for various purposes: identifying the author of the query, explaining the purpose of different sequences of code, and so on. Comments do not appear in query results. The syntax for comments is as follows: -- Single line comment or /* Single line comment */ /* Multiple line comment */

6 A Beginner’s Guide To SQL/Charlotte McGary

Asking simple questions with SQL Short for Structured Query Language, SQL (pronounced sequel) is a language used for communicating with databases. It’s a powerful yet straight-forward and easy-to-learn language that can be used with almost any DBMS. All the example queries used in this document were executed using SQL Server Management Studio. See Appendix A for instructions on using this DBMS. Just as any language must follow a set of rules called grammar, SQL also has a set of rules it must follow. The English language has verbs, nouns, adjectives, and adverbs. SQL has some similar constructs. For example, suppose you’re an assistant to a publisher, and he keeps all his records in a filing cabinet. He has a folder in that cabinet labeled “Authors”, with the information related to each author printed on a separate piece of paper within that folder. Now if the publisher wants you to retrieve that information, he might say “Get me a list of all the authors we’ve published.” This lesson will show you how to translate that request into SQL. In the examples in this book, our digital filing cabinet is a database named pubs, which contains our digital folders (tables named authors, publishers, titles, and so on). You will be accessing the pubs database (see Appendix B) using SQL Server Management Studio. After starting SQL Server Management Studio, select the pubs database, click the plus “+” sign to the left of the database name, then click the plus sign to the left of “Tables.” Keep expanding plus signs until you see the names of all the columns in the dbo.authors table. (The prefix “dbo” stands for “database owner”, the default owner in SQL Server.)

Selecting data In order to retrieve one or more records, you must tell SQL what information you want and where that information resides. For the most simple queries, this can be accomplished using the keywords USE, SELECT, and FROM.

USE keyword syntax The USE keyword indicates which database contains the table(s) you’re querying. USE database_name

SELECT keyword syntax The SELECT keyword indicates which columns are to be included in the query.

A Beginner’s Guide To SQL/Charlotte McGary 7

To retrieve data from all columns in a record, simply type the word SELECT following by an asterisk (*)--a wildcard which represents all data. SELECT * To include only specific columns in your query type the word SELECT following by a list of column names, separated by commas. SELECT 1st_selected_col, 2nd_selected_col, … Nth_selected_col

FROM keyword syntax The FROM keyword indicates from which table to retrieve records. The syntax is simply the word FROM following by the table name. FROM table_name

Running your first query The first query you’re going to run retrieves data from the table dbo.authors. Click New Query on the Standard Toolbar.

In the query pane to the right of the Object Explorer, enter the following text (as you type, keywords automatically appear as blue text): USE pubs SELECT * FROM dbo.authors; In the above example, the USE keyword indicates which database you’re querying (pubs). The SELECT keyword followed by an asterisk (*) says to retrieve all columns from all records in the table you’re querying. The FROM keyword indicates the location of the data to be selected (the table dbo.authors). The semicolon (;) at the end indicates the query is complete (similar to a period at the end of a sentence).

8 A Beginner’s Guide To SQL/Charlotte McGary

To execute the query, click !Execute on the standard toolbar or press the F5 key. (The first five results are displayed below.)

Sorting data In the above example, the results of the query are displayed in the order the records were entered into the table. As we currently have only two records, that’s not a problem. But suppose we have the ten records in the following example and want to sort them alphabetically by “Name”? To sort alphabetically by a column name, use the ORDER BY clause. So, in our example, to sort in ascending order (the default) by author’s last name, execute the following query: SELECT * FROM dbo.authors ORDER BY au_lname; The first five results are displayed below:

If you want to sort in descending order by author’s last name, execute the following query: SELECT * FROM dbo.authors ORDER BY au_lname DESC; The first five results are displayed below:

A Beginner’s Guide To SQL/Charlotte McGary 9

You can also sort by multiple columns, To first sort by state, then by city name within each state, execute the following query: SELECT * FROM dbo.authors ORDER BY state, city; The first five and the last 10 results are displayed below:

You can also select specific columns. For example, to list only the author’s last names, you can select just the au_lname column. This is a matter of simply replacing the asterisk “*” in the previous example with the column name as follows: SELECT au_lname FROM dbo.authors; The first five results are displayed to the right: You can select records for more than one column by separating the column names with commas. To also select records containing the author’s first name, city, and state, modify the above query as follows: SELECT au_lname, au_fname, city, state FROM dbo.authors ORDER BY au_lname; The first five results are displayed below:

10 A Beginner’s Guide To SQL/Charlotte McGary

NOTE: The column named “state” is rendered in blue text because it is also a reserved word. In this case, SQL is smart enough to know you’re referring to your column name, but still lets you know it’s a reserved word. If you don’t want it to appear in blue text, just enclose the column name in square brackets like this: [state].

For a list of SQL Server reserved keywords, see http://technet.microsoft.com/en-us/library/ms189822.aspx.

The WHERE clause You’ve seen how to retrieve all records for a table and sort them by specific columns. But what if you want to retrieve only those records which meet certain criteria? That’s where the “WHERE” clause comes in. For example, to filter records so that just information for authors who live in California is displayed, execute the following query: SELECT * FROM dbo.authors WHERE state = 'CA'; Single quotes are used to delineate a string. You do not need to use quotes with a numeric value.

A Beginner’s Guide To SQL/Charlotte McGary 11

Operators There are several operators you can use to modify the search criteria (that is, the filter conditions) specified in a WHERE clause. The operators you’ll probably use the most are described in the following table:

Operator Description = Equal to <> or != Not equal to < Less than <= Less than or equal to > Greater than >= Greater than or equal to BETWEEN Between two values (inclusive) IS NULL Is a null value AND Appends condition where both

conditions must be met OR Appends condition where either

condition must be met NOT Negates the following condition LIKE Creates a search pattern with the (%)

or ( _ ) wildcard IN Determines whether the specified

value matches any value in a list

Examples Find all titles with a price less than $19.99 and sort in ascending order by price: SELECT title, price FROM dbo.titles WHERE price < 19.99 ORDER BY price;

12 A Beginner’s Guide To SQL/Charlotte McGary

List all titles except those with a price of 10.95 or 19.99: SELECT title, price FROM dbo.titles WHERE price != 10.95 AND price != 19.99; Find all titles whose price is between $10 and $25 and sort in ascending order by price: SELECT title, price FROM dbo.titles WHERE price BETWEEN 10 AND 25 ORDER BY price; Find all titles whose price is NOT between $10 and $25 and sort in ascending order by price: SELECT title, price FROM dbo.titles WHERE NOT price BETWEEN 10 AND 25 ORDER BY price; or SELECT title, price FROM dbo.titles WHERE price < 10 OR price > 25 ORDER BY price;

A Beginner’s Guide To SQL/Charlotte McGary 13

Find all publishers having a NULL value in the state column. SELECT pub_name, [state] FROM dbo.publishers WHERE [state] IS NULL; Find all publishers where the value in the state column is not NULL. SELECT pub_name, [state] FROM dbo.publishers WHERE NOT [state] IS NULL;

Wildcards Wildcards allow you more flexibility in your queries, as they don’t require an exact match. The asterisk (*) is a special wildcard used to represent everything all column names. It is not use to filter data with the LIKE operator. Wildcards can only be used with text (string) data types. When filtering data, the percent character (%) serves a purpose similar to the asterisk. It matches any number of characters occurring any number of times. The underscore character ( _ ) represents a single character.

The LIKE operator LIKE is actually a predicate—an expression used with a clause to return a value of true or false. But, as with the predicates AND, OR, NOT, and so on, it is more often referred to as an operator. It’s not that important a distinction, but because some reference materials refer them as such, you should be aware of the term. In keeping with the more popular usage and to avoid confusion, this document will refer to such expressions as operators. The LIKE operator is used with wildcards to create a search pattern instead of searching for a specific match. In a previous example, you used the WHERE clause to retrieve records for all authors who live in CA. But suppose you want to retrieve only those records for authors whose last name begins with the letter “M”. The LIKE operator, used in conjunction with wildcards (%) and ( _ ), allows you to do that.

14 A Beginner’s Guide To SQL/Charlotte McGary

SELECT * FROM dbo.authors WHERE au_lname LIKE 'G%';

To further filter the results so that records are displayed only authors with a last name beginning with “G” who also live in CA, modify your query as follows: SELECT * FROM dbo.authors WHERE au_lname LIKE 'G%' AND state = 'CA';

You will find the operators AND, OR, and NOT quite useful for this type of situation. List all cities whose name is two or more words and sort in ascending order: SELECT city FROM dbo.authors WHERE city LIKE '% %' ORDER BY city; The above query looks for any number of characters at the beginning of the name, and any number of characters at the end of the name, as long as there is at least one space somewhere between them. To only list cities with three or more words in the name, modify the string associated with the LIKE operator by adding a second space and a third % as follows: SELECT city FROM dbo.authors WHERE city LIKE '% % %' ORDER BY city; List all authors who have a two-word last name that includes a space: SELECT * FROM dbo.authors WHERE au_lname LIKE '___ %'; The above query uses 3 successive ( _ ) wildcard characters (no spaces between the underscores) followed by a space character and any number of characters. Each

A Beginner’s Guide To SQL/Charlotte McGary 15

underscore represents a single character, while the % symbol represents any number of characters.

The IN operator In the first section of this document, you learned how to use the WHERE clause with the AND and OR operators to specify more than one condition. If you have more than two conditions, however, the IN operator makes your query less cumbersome to write. For example, suppose you want to display the author’s last name, first name, city and state if the author lives in CA, OR, or UT. You could write the following query using the OR operator: SELECT au_lname, au_fname, city, [state] FROM dbo.authors WHERE [state] = 'MD' OR [state] = 'KS' OR [state] = 'OR'; Or, you could get the same results with less typing with the IN operator as follows: SELECT au_lname, au_fname, city, state FROM dbo.authors WHERE state IN ('MD', 'KS', 'OR'); As stated previously, the result is the same from both queries:

NOTE: The column named “state” is rendered in blue text by SQL Server Management Studio because it is also a reserved word. In this case, SQL is smart enough to know you’re referring to your column name, but still lets you know it’s a reserved word. If you don’t want it to appear in blue text, just enclose the column name in square brackets like this: [state].

16 A Beginner’s Guide To SQL/Charlotte McGary

Review 1 Now that you’re familiar with some of the basic terminology and concepts of communicating with a database, let’s review what you’ve learned by creating some additional queries. Answers to all review exercises can be found in Appendix C. (No peeking at the answers before you try the exercises. ) 1. Using the pubs database, query the dbo.authors table to display all authors’ first and

last names in ascending order by last name, then by first name.

2. Using the pubs database, query the dbo.authors table to

display the first and last names of all authors whose last name begins with the letter “G”.

A Beginner’s Guide To SQL/Charlotte McGary 17

3. Using the pubs database, query the dbo.authors table to display last names, city, and state for all authors living in CA but do not live in Oakland. Sort in descending order by city.

4. Using the pubs database, query the dbo.titles table to display the title id, title, and

price for all records with a NULL value in their price field. (HINT: See the Operators section of this document.)

5. Using the pubs database, query the dbo.titles table to display all titles and prices,

where the price is between $2.00 and $10.00 (inclusive).

6. Using the pubs database, query the dbo.titles table to display all titles that do not start

with the letter “T” in ascending order by title.

18 A Beginner’s Guide To SQL/Charlotte McGary

7. Using the pubs database, query the dbo.titles table to display the title and price for all books whose price is 2.99, 14.99, or 19.99.

8. Using the pubs database, display a list of all publisher IDs and names, in ascending

order by publisher name. (This is a simple query. The challenge is in determining which table you need to query, and the column names you should select.)

9. Using the pubs database, query the dbo.employee table to display records only for

those employees not having a middle initial. Sort in descending order first by the employee’s last name.

19 A Beginner’s Guide To SQL/Charlotte McGary

LESSON 2 In this lesson you will learn about aliases, calculated columns, aggregate functions, converting data types, and grouping data.

20 A Beginner’s Guide To SQL/Charlotte McGary

Using Aliases An alias is just what the name implies: an alternate name for either a column or a table name. They can significantly reduce the amount text you have to type and make your queries easier for you to read. They are especially useful when creating a column from a calculation and when querying multiple tables. An alias does not change the name of the column; it’s just a temporary name used for display output. To create an alias, simply type the column name followed by the text “AS” and the name you want to use as an alias for your column or table. (Table aliases will be discussed in Lesson 3.) The following query will replace the column name “pub_name” with the text “Publisher’s Name” in the resulting display. USE pubs SELECT pub_name AS "Publisher's Name" FROM dbo.publishers; Quotes are required around any alias containing spaces. When querying multiple tables, you have to specify which column you’re selecting from which table when the same column name exists in more than one of the tables. In this situation, table aliases become quite useful. This will be discussed further in the section Retrieving data from multiple tables. In the next section we’ll be using aliases for columns displayed as a result of calculations.

A Beginner’s Guide To SQL/Charlotte McGary 21

Calculating data There are several functions used to retrieve data from a table then perform a calculation on that data. In this section we’ll discuss the calculated column values and the aggregate functions COUNT, SUM, MIN, and MAX. With the exception of the COUNT(*) function, aggregate functions do not include NULL values in their calculations.

Data Type Precedence When combining data types in calculations, the order in which the operations are performed and the resulting value’s data type are determined by the data type with the highest precedence. For a list of SQL Server data types and their precedence, see http://msdn.microsoft.com/en-us/library/ms190309(SQL.100).aspx. To illustrate the importance of data type precedence, consider the following two queries: Query #1: SELECT 2 + 4 * 3; Result is 14. Query #2: SELECT (2 + 4) * 3; Result is 18. In Query #1, because multiplication has a higher precedence than addition, SQL multiplies 4 time 3 first, then adds 2. In Query #2, the parentheses surrounding 2 + 4 indicates that these two numbers should be added together first, and that result will then be multiplied by 3. In the following query, “price” is the data type MONEY and 0.095 is FLOAT. The resulting data type of both calculated columns is FLOAT because that data type has a higher precedence than MONEY. USE pubs SELECT title, price, price * 0.095 AS "9.5% Tax", price + price * 0.095 AS "Total" FROM dbo.titles; The first 5 results are displayed below:

22 A Beginner’s Guide To SQL/Charlotte McGary

The COUNT function If you want to count the total number of records in a table, execute the following query: SELECT COUNT(*) FROM dbo.titles; Unfortunately, because the result of the calculation is not an existing column in the table, it will have no column name when displayed. That’s where our friend alias comes in. SELECT COUNT(*) AS "Number of Titles" FROM dbo.titles; Now we have a meaningful name for our results column. The above query counts the total number of records whether or not a record has a column containing a NULL value. To find, for example, the number of records with a non-null price, execute the following query: SELECT COUNT(price) AS "Records with non-null prices" FROM dbo.titles; Since there are two records containing a NULL value in the price column, the COUNT function displays 16 instead of 18 as the number of records found.

The DISTINCT Keyword This argument returns only unique values. For example, in the dbo.titles table, if you were to count the number of records having a non-null value in the pub_id column, the query would return 18 values, one for each record. To find the number of unique pub_id values, preface pub_id with DISTINCT. SELECT COUNT(pub_id), COUNT(DISTINCT pub_id) AS "Number of Publishers" FROM dbo.titles; The following query lists all records containing an author ID. If you execute the query, you will get 25 records. Some of the IDs are repeated because those authors have written more than one book. SELECT au_id FROM dbo.titleauthor ORDER BY au_id;

A Beginner’s Guide To SQL/Charlotte McGary 23

To find out exactly how many unique authors there are in the table, execute the following query: SELECT DISTINCT au_id FROM dbo.titleauthor ORDER BY au_id; The results will correctly show that 19 authors wrote the 25 books in the database.

The AVG function This function calculates the average value of a column of data. For example, to return the average price of all the titles, execute the following query: SELECT AVG(price) AS “Avg price” FROM dbo.titles; You can also use the results of aggregate functions in calculations. For example, to display the amount of money made per book, multiple the year-to-date sales by the cost of the book. The query is as follows: SELECT *, price * ytd_sales AS “Gross Income” FROM dbo.titles;

Notice something different about the above query? The asterisk (*) is used twice in the SELECT statement. In the first position it represents all columns. In the second position, it represents multiplication. The SELECT * selects all columns in the table. When you append , price * ytd_sales AS “Gross Income”, you are adding a column to the results that does not exist in the table.

24 A Beginner’s Guide To SQL/Charlotte McGary

If you just want to list the title_id, price, ytd_sales, and your calculated column, you could alter the query as follows: SELECT title_id, price, ytd_sales, price * ytd_sales AS “Gross Income” FROM dbo.titles;

The SUM function This function does exactly what you suspect: adds all the values in a column to get a total. To calculate the total number of books sold so far, you would sum the “ytd_sales” column as follows: SELECT SUM(ytd_sales) AS Number_of_books_sold FROM dbo.titles;

The MIN and MAX functions MIN and MAX also do exactly what you’d expect from their names: find the minimum and maximum values in a column. For example, to find the lowest and highest price of all books in the current inventory, execute the following query: SELECT MIN(price) AS Min_Price, MAX(price) AS Max_Price FROM dbo.titles; You could have run this as two separate queries, one for MIN and one for MAX, but it makes more sense to see both results side-by-side.

A Beginner’s Guide To SQL/Charlotte McGary 25

To display the minimum price, the maximum price, and the difference between them, execute the following query: SELECT MIN(price) AS "Min Price", MAX(price) AS "Max Price", MAX(price) - MIN(price) AS "Difference" FROM dbo.titles;

26 A Beginner’s Guide To SQL/Charlotte McGary

Converting Data Types CAST( ) and CONVERT( ) There may be times when values you retrieve are not the correct data type you need for calculations. There are two functions which can convert data from one data type to another: CAST() and CONVERT(). CAST is the ANSI standard. CONVERT is specific to Microsoft SQL Server. CONVERT allows more flexibility than CAST through the use of a third argument (style). The data type you’re converting from must be compatible with the data type you’re converting to. For example, you can convert a numeric value into a character value, but attempting to convert a character value to a numeric value, you will get a conversion error (unless you’re converting a character value like a zip code which has only the characters 0-9). The syntax for CAST() is: CAST(value to be converted AS resulting data type) The syntax for CONVERT() is: CONVERT (resulting data type, value to be converted, style) Style is an optional integer value that tells the CONVERT function how to interpret the value to be converted. (For information on what the various values of style can be, see http://msdn.microsoft.com/en-us/library/ms187928.aspx.) We'll look at sample conversions to data types FLOAT, DECIMAL, and INT in the next section. Why convert, you ask? Good question. See if you can deduce the answer from the following example, where x = 2 and y = 3 and both x and y are the integer data type. Equation: x/y If x and y are data type FLOAT, where x = 2 and y = 3, the result will be 0.666666666666667. But if x and y are data type INT, the result will be 0. The reason is that when one integer value is divided by another integer value, if the result is not a whole number, anything after the decimal point is truncated, not rounded to the

A Beginner’s Guide To SQL/Charlotte McGary 27

nearest whole number. Therefore, to get the correct result when dividing values with INT data type, you will need to convert the values in your calculation.

Converting to the FLOAT Data Type The syntax for converting to the FLOAT data type is as follows: CONVERT (FLOAT, value to be converted) or CAST (value to be converted AS FLOAT) If you divide one INT data type by another INT data type, the result may surprise you, as in the following example: SELECT 3/2 AS "Int/Int"; The result is 1 because dividing integers truncates the result. To make sure you don’t get this kind of error, convert either the numerator or denominator value (or both) to a floating point number (FLOAT data type). SELECT CONVERT(FLOAT,3)/2 AS "Float/Int"; or SELECT 3/CONVERT(FLOAT,2) AS "Int/Float";

Converting to the DECIMAL Data Type The syntax to convert to the DECIMAL data type is as follows: CONVERT (DECIMAL(total number of digits, number of digits after decimal point), value to be converted) or CAST (value to be converted AS DECIMAL(total number of digits, number of digits after decimal point) Let’s revisit the query in Data Type Precedence:

28 A Beginner’s Guide To SQL/Charlotte McGary

USE pubs SELECT title, price, price * 0.095 AS "9.5% Tax", price + (price * 0.095) AS "Total" FROM dbo.titles; The first 5 of 18 results are displayed below:

The calculated results from the above query are of the FLOAT data type because FLOAT (the value 0.095) has a higher data type precedence than MONEY (the price column). To display the results with just two decimal places, convert the results to the DECIMAL data type as follows: USE pubs SELECT title, price, CONVERT(DECIMAL(10,2), price * 0.095) AS "9.5% Tax", CONVERT(DECIMAL(10,2), price + (price * 0.095)) AS "Total" FROM dbo.titles; The first 5 of 18 results are displayed below:

Converting to the INT Data Type The syntax for converting to the INT data type is as follows: CONVERT (INT, value to be converted)

or

CAST (value to be converted AS INT) To convert the average advance to an integer value (INT), execute the following query: USE pubs SELECT CONVERT(INT, AVG(advance)) AS "Avg Advance" FROM dbo.titles;

A Beginner’s Guide To SQL/Charlotte McGary 29

Notice that the CONVERT function automatically rounds the number during the conversion.

The ROUND( ) Function The ROUND function allows you to round a value to the nearest whole number. The syntax of the ROUND function is as follows: ROUND(value to be rounded, precision used when rounding) The precision parameter is illustrated further in the following example: SELECT 9.4/5.3 AS "No Rounding", ROUND(9.4/5.3, 0) AS "Round Precision 0", ROUND(9.4/5.3, 1) AS "Round Precision 1", ROUND(9.4/5.3, 2) AS "Round Precision 2", ROUND(9.4/5.3, 3) AS "Round Precision 3", ROUND(9.4/5.3, 4) AS "Round Precision 4", ROUND(9.4/5.3, 5) AS "Round Precision 5", ROUND(9.4/5.3, 6) AS "Round Precision 6";

As you can see, setting a precision of 6 decimal places in this example is the same as not rounding at all because you’re using every decimal place from the result.

Conversion Tips Always remember to convert integers to decimal or floating point values when the result of an integer division is used as the value in a denominator, (e.g. 10 / (2/3)). For example, you know that when you divide 2 by 3, you get 0.67. But that is not how SQL sees the result. Integer results are truncated, not rounded up or down, so anything after the decimal point is simply thrown away. Therefore, according to SQL, the result of the integer division 2/3 is 0, which results in a “divide by zero error” (see the example below). SELECT 2 / (1/3)

When and where you do your data type conversions within calculations will also affect the results, as shown in the following examples:

30 A Beginner’s Guide To SQL/Charlotte McGary

SELECT 2/3 AS "Result 1"; SELECT CONVERT(DECIMAL(6,2),2/3) AS "Result 2"; SELECT CONVERT(DECIMAL(6,2),2) / CONVERT(DECIMAL(6,2),3) AS "Result 3";

SELECT CONVERT(DECIMAL(6,2), CONVERT(DECIMAL(6,2),2) / CONVERT(DECIMAL(6,2),3))

AS "Result 4";

A Beginner’s Guide To SQL/Charlotte McGary 31

The GROUP BY Clause As you learned in the previous lesson, ORDER BY is used to sort data in ascending or descending order. GROUP BY, on the other hand, is typically used with aggregate functions (such as COUNT, AVG, MIN, MAX) to group identical data. This is best explained by comparing the following two examples. To find the number of titles for each publisher, you’d have to run the following query once for each publisher: SELECT COUNT(*) AS Title_count FROM dbo.titles WHERE pub_id = '1389'; The GROUP BY clause allows you to retrieve the number of titles for each publisher in one query: SELECT pub_id, COUNT(*) AS Title_count FROM dbo.titles GROUP BY pub_id; NOTE: Unlike the ORDER BY clause, a column name specified in the SELECT statement that’s not part of an aggregate function must appear in a GROUP BY clause. To display the number of publishers in each country: SELECT country, COUNT(country) AS "# Publishers" FROM dbo.publishers GROUP BY country; To display the minimum and maximum prices by publisher: SELECT pub_id AS "Publisher ID", MIN(price) AS "Min. Price", MAX(price) AS "Max. Price" FROM dbo.titles GROUP BY pub_id;

32 A Beginner’s Guide To SQL/Charlotte McGary

To display the number of books sold at each price: SELECT price, COUNT(price) AS "Books listed at price" FROM dbo.titles GROUP BY price; Publishers often pay authors a different royalty percentage based on the number of books sold. Using the dbo.roysched table, the average amount paid in royalties per book: SELECT title_id, CAST( (AVG(hirange)-AVG(lorange)) * AVG(royalty*.01) AS MONEY) AS "Avg. paid in royalties" FROM dbo.roysched GROUP BY title_id; Calculations performed in the above query: 1. Subtracted the average lorange from the average

hirange to get the total average range (since we’re grouping by title_id, this calculates all averages per book, not across all rows).

2. Found the average of all the royalties for that book, expressed as a percentage.

3. Multiplied the results of #1 by #2. 4. Converted the result to data type MONEY.

A Beginner’s Guide To SQL/Charlotte McGary 33

The HAVING clause As you saw in the previous lesson, the WHERE clause is used to filter data. You can filter grouped data also, but because the WHERE clause filters rows, it will not work on groups. The HAVING clause used with GROUP BY functions as a filter. To exclude books having a NULL value in the price column, modify the last query in the previous section as follows: SELECT price, COUNT(price) AS "#@ at this price" FROM dbo.titles GROUP BY price HAVING price IS NOT NULL; The following query displays all prices that have more than one title sold at that price: SELECT price, COUNT(price) AS "# Books at this price" FROM dbo.titles GROUP BY price HAVING COUNT(price) > 1; To retrieve the number of titles for all publishers having more than 5 titles and sort in ascending order by the title count, execute the following query: SELECT pub_id, COUNT(pub_id) AS Title_count FROM dbo.titles GROUP BY pub_id HAVING COUNT(pub_id) > 5 ORDER BY COUNT(pub_id); Notice that there are no quotes around the column alias Title_count. That is because you only need to include the quotes when you embed spaces in the name (i.e. “Title count”). To find the number of authors living in zip codes beginning with “94”, execute the following query: SELECT zip, COUNT(au_id) AS Au_count_by_zip FROM dbo.authors GROUP BY zip HAVING zip LIKE '94%'

34 A Beginner’s Guide To SQL/Charlotte McGary

The GROUP BY statement groups rows, but it doesn’t sort the grouped. To do that you need to include an ORDER BY clause. For example, if you want to sort the title count in ascending order, add an ORDER BY clause as follows: SELECT pub_id, COUNT(*) AS Title_count FROM dbo.titles GROUP BY pub_id HAVING COUNT(*) > 5 ORDER BY Title_count; To count the number of titles at each of the prices listed, ignoring those titles with prices between 10 and 15 dollars, execute the following query: SELECT price, COUNT(price) AS "Num. Books" FROM dbo.titles GROUP BY price HAVING NOT price BETWEEN 10 AND 15; To find the number of titles and the sum of the year-to-date sales for each type of book, ignoring books where the type category hasn’t yet been decided, execute the following query: SELECT [type] AS "Category", SUM(ytd_sales) AS "Sales to Date", COUNT(ytd_sales) AS "Number of Titles" FROM dbo.titles GROUP BY [type] HAVING [type] <> 'UNDECIDED';

A Beginner’s Guide To SQL/Charlotte McGary 35

Now consider the following example, which displays all titles having more than one author: SELECT title, au_ord FROM dbo.titleview WHERE au_ord > 1; Notice that one title “Sushi, Anyone?” is listed twice. That is because it has three authors: au_ord valuels of 1, 2, and 3. To find the total number of authors for titles having more than one author, change the previous query to find the highest au_ord value and group by title: SELECT title, MAX(au_ord) AS "# Authors" FROM dbo.titleview GROUP BY title HAVING MAX(au_ord) > 1; Because MAX(au_ord) is a calculated value, we had to give it a column alias (“# Authors”) in order to display a column header with the results.

36 A Beginner’s Guide To SQL/Charlotte McGary

Review 2 1. Using the pubs database, query the dbo.titles table to find the total amount of

advances paid for all books. Use the alias Total_Advances for the results column header.

2. Using the pubs database, query the dbo.titles table to find the total number of

psychology books. Use Psychology_Count as the column header for the results.

3. Using the pubs database, query the dbo.titles table to list the book type and the

number of publishers that carry each type of book, ignoring any title with the type UNDECIDED. Use the column alias “Num. pubs publishing this type” for your calculated value.

4. Using the pubs database, query the dbo.authors table to list the state and the number

of authors living in each state. Use the alias “Authors per state” for the results column. (HINT: You are counting the number of times each state appears in the table.)

A Beginner’s Guide To SQL/Charlotte McGary 37

5. Using the pubs database, query the dbo.titleauthor table to list the author’s ID and the number of titles for each author having more than one book title associated with his or her name. Use the alias Title_count for the results column.

HINTS:

• Use GROUP BY and the dbo.titleauthor table.

• We know we need author info (au_id) and that we’re counting titles (title_id). But we don’t just want a general count of all titles. We want the number of titles grouped by author. Additionally, we only want to display a record if the number of titles for that author is greater than 1.

6. Using the pubs database, query the dbo.titles table to list each publisher’s ID and the

number of different types of books it sells. (HINT: you want to count distinct values.) Use the alias “Types of book sold” for the results column. Save this query. You will modify it in Exercise #7.

7. Modify the query you created in Exercise 6 to display only publishers who publish more than 2 types of books.

38 A Beginner’s Guide To SQL/Charlotte McGary

LESSON 3 In this lesson you will learn how to query multiple tables using implicit inner joins, inner joins and outer joins. You will also learn how concatenate values from two or more columns.

A Beginner’s Guide To SQL/Charlotte McGary 39

Retrieving data from multiple tables In the previous sections, each of your queries involved a single table. But sometimes you need information from more than one table. Suppose you want to display every title and its publisher. The dbo.publishers table contains both the publisher’s ID and name, but no title information. The dbo.titles table contains the book titles and the publisher’s ID, but not the name of the publisher. Therefore, you’ll need to query both tables to get all the information you need. We could query the two tables as follows: SELECT title, pub_name AS Publisher FROM dbo.publishers, dbo.titles WHERE dbo.publishers.pub_id = dbo.titles.pub_id ORDER BY pub_name, title; However, it would require less typing to assign an alias to each of the table names as follows: SELECT title, pub_name AS Publisher FROM dbo.publishers AS p, dbo.titles AS t WHERE p.pub_id = t.pub_id ORDER BY pub_name, title;

The text “dbo.publishers AS p” tells SQL to use the character “p” instead of the full text “dbo.publishers” every time you need to identify the table a column belongs to. Without table aliases, you’d have to write out the full table name each time.

40 A Beginner’s Guide To SQL/Charlotte McGary

Let’s analyze the above query line-by-line. First we SELECT the columns we want to display (pub_name and title). Because the column name title exists only in the dbo.titles table, and the column name pub_name exists only in the dbo.publishers table, you do not have to tell SQL in which table to search for those columns. Next we specify which tables we’re querying FROM (dbo.publishers and dbo.titles). The WHERE clause specifies which column we’re using to join the tables (see also Inner Joins). In this example we’re matching the pub_id in both tables, so only titles in the dbo.titles table which have publisher IDs matching the publisher IDs in the dbo.publishers table will be displayed.

dbo.publishers dbo.titles

Only three publisher IDs in the dbo.titles table match publisher IDs in the dbo.publishers table, so results for only those three publishers will be displayed. But what if you also wanted to list the publisher’s ID along with the publisher’s name? You might think the following query would work: SELECT title, pub_name, pub_id FROM dbo.publishers AS p, dbo.titles AS t WHERE p.pub_id = t.pub_id ORDER BY title, pub_name; Unfortunately, executing the above query results in the following error:

A Beginner’s Guide To SQL/Charlotte McGary 41

Msg 209, Level 16, State 1, Line 1 Ambiguous column name 'pub_id'. Do you see why the above query generates that error? Because there is a pub_id in both tables, the DBMS doesn’t know which pub_id you’re referring to. We didn’t have that problem with the first query, because the columns “pub_name” and “title” were unique to each table. If you select identical column names that exist in more than one of the tables you’re querying, then you must indicate which table you’re pulling that column from. Modify the above query as follows to get the query to execute correctly. SELECT title, pub_name, t.pub_id FROM dbo.publishers AS p, dbo.titles AS t WHERE p.pub_id = t.pub_id ORDER BY pub_name, title;

Now let’s look at a query that involves retrieving information from three tables. It’s a little more complex, but it follows the same format as the example using two tables. Suppose you want a listing of all authors and the titles of their books. The dbo.authors table contains author IDs and author names. The dbo.titles table contains title IDs and titles. But neither table tells you which author wrote which book. The table that links the two is the dbo.titleauthor table, which contains both the author ID and the title ID, but no author names or title names. To list all titles written by a particular author, you will need to query all three tables. First let’s take a look at the dbo.titleauthor table (the first 5 records are shown to the right). This table contains an author ID and a title ID, but no names or titles. SELECT * FROM dbo.titleauthor;

42 A Beginner’s Guide To SQL/Charlotte McGary

Now look at the following query: SELECT au_lname, au_fname, title FROM dbo.authors AS a, dbo.titles AS t, dbo.titleauthor AS ta WHERE ta.title_id = t.title_id AND ta.au_id = a.au_id ORDER BY au_lname, au_fname, title; This query is an implicit Inner Join. The query checks each record of the dbo.authors table and looks for a matching au_id in the dbo.titleauthor table. If it finds a match, it will look at the corresponding title_id in that record of the dbo.titleauthor table, then search the dbo.titles table for a maching title_id. Even if it finds a matching au_id in the dbo.authors and dbo.titleauthor tables, it will not return a result unless the title_id in the dbo.titleauthor table also exists in the dbo.titles table.

dbo.authors dbo.titleauthor dbo.titles

A Beginner’s Guide To SQL/Charlotte McGary 43

Inner Joins In the first example in this lesson, you were actually joining two tables by testing whether they had matching pub_id values. This type of query is also known as an implicit Inner Join. It returns only rows that both tables have in common. Original query (implicit Inner Join): SELECT title, pub_name FROM dbo.publishers AS p, dbo.titles AS t WHERE p.pub_id = t.pub_id ORDER BY pub_name, title; Modified query (explicit Inner Join or simply Inner Join): SELECT title, pub_name FROM dbo.publishers AS p INNER JOIN dbo.titles AS t ON p.pub_id = t.pub_id ORDER BY pub_name, title; In the above case, instead of a WHERE clause, the ON clause specifies the relationship between the two tables. Both produce the same 18 records as a result:

44 A Beginner’s Guide To SQL/Charlotte McGary

The following example displays the publisher's name, title, type of book, and the advance paid for each book of type popular_comp where the advance is not null: USE pubs SELECT pub_name, title, [type], advance FROM dbo.titles AS t INNER JOIN dbo.publishers AS p ON t.pub_id = p.pub_id AND [type] = 'popular_comp' AND advance IS NOT NULL;

The following example displays the title and quantity of that title sold at each store: USE pubs SELECT stor_id, title, qty FROM dbo.sales AS s INNER JOIN dbo.titles AS t ON s.title_id = t.title_id ORDER BY title; The first 10 of 21 resulting records In a previous example, we queried three tables using an implicit Inner Join: SELECT au_lname, au_fname, title FROM dbo.authors AS a, dbo.titles AS t, dbo.titleauthor AS ta WHERE ta.title_id = t.title_id AND ta.au_id = a.au_id ORDER BY au_lname, au_fname, title; The following query produces the same results with an explicit Inner Join: SELECT au_lname, au_fname, title FROM dbo.authors AS a INNER JOIN dbo.titleauthor AS ta ON ta.au_id = a.au_id INNER JOIN dbo.titles AS t ON ta.title_id = t.title_id ORDER BY au_lname, au_fname, title; Notice that, with the explicit Inner Joins of more than two tables, the order of the joins is important. The table linking the other tables must either come first or between the other tables. In this example, the dbo.titleauthor table must be joined to the either the

A Beginner’s Guide To SQL/Charlotte McGary 45

dbo.authors or dbo.titles table before you can join the remaining table. For example, the following are acceptable sequences for the joins: FROM dbo.titleauthor AS ta INNER JOIN dbo.authors AS a ON ta.au_id = a.au_id INNER JOIN dbo.titles AS t ON ta.title_id = t.title_id FROM dbo.titles AS t INNER JOIN dbo.titleauthor AS ta ON ta.title_id = t.title_id INNER JOIN dbo.authors AS a ON ta.au_id = a.au_id But the following query produces an error because you haven’t yet indicated you are querying the dbo.titleauthor table: USE pubs SELECT au_lname, au_fname, title FROM dbo.authors AS a INNER JOIN dbo.titles AS t ON ta.title_id = t.title_id INNER JOIN dbo.titleauthor AS ta ON ta.au_id = a.au_id

The following example uses an Inner Join on more than two tables: SELECT ta.title_id AS "Title ID", COUNT(a.au_id) AS "Number Coauthors" FROM dbo.titleauthor as ta INNER JOIN dbo.authors AS a ON a.au_id = ta.au_id INNER JOIN dbo.titles AS t ON t.title_id = ta.title_id GROUP BY ta.title_id HAVING COUNT(a.au_id) > 1; The above query lists the IDs of all titles having more than one author.

46 A Beginner’s Guide To SQL/Charlotte McGary

You can also combine joins with the GROUP BY clause. In the following example, displays the total number of each book sold, summed across all stores: USE pubs SELECT title, SUM(qty) AS "Total Sold" FROM dbo.sales AS s INNER JOIN dbo.titles AS t ON s.title_id = t.title_id GROUP BY title ORDER BY title;

Outer Joins The difference between an Inner Join and an Outer Join basically boils down to the fact that an Inner Join returns only rows common to both tables, whereas an Outer Join can return rows unique to either the first table listed (LEFT OUTER JOIN) or the second table listed (RIGHT OUTER JOIN). For example, the previous Inner Join on the dbo.publishers and dbo.titles tables displayed 18 rows. In each row a publisher’s name and a corresponding title were listed. But there are actually 5 rows that weren’t displayed because there are 5 publishers with no title information associated with them. (Remember, a column with no data entered contains a NULL value, and NULL values cannot be matched with each other.) There were publisher names for each of those 5 rows, but no matching title, so an Inner Join cannot display those rows. An Outer Join can, however, as in the following example. SELECT pub_name, title FROM dbo.publishers LEFT OUTER JOIN dbo.titles ON dbo.publishers.pub_id = dbo.titles.pub_id ORDER BY pub_name, title;

A Beginner’s Guide To SQL/Charlotte McGary 47

As you can see, the outer join displayed the additional 5 rows containing NULL values.

LEFT and RIGHT keywords In the above example, we used LEFT OUTER JOIN to relate the two tables. The LEFT keyword will display all rows for the table to the left (dbo.publishers) whether or not there are matching rows in the table to the right of the Outer Join (dbo.titles). The RIGHT keyword does the opposite: it says to include all rows from the table on the right, whether or not there are matching rows in the table on the left. The RIGHT OUTER JOIN below displays the same 18 rows as the INNER JOIN because there are no titles in the title table that correspond to those 5 publishers without titles. SELECT pub_name, title FROM dbo.publishers RIGHT OUTER JOIN dbo.titles ON dbo.publishers.pub_id = dbo.titles.pub_id ORDER BY pub_name, title; In the following queries, the INNER JOIN, LEFT OUTER JOIN, and RIGHT OUTER JOIN all produce different results: SELECT au_id, stor_id FROM dbo.authors AS a INNER JOIN dbo.stores AS s ON a.[state] = s.[state] ORDER BY title; SELECT au_id, stor_id FROM dbo.authors AS a LEFT OUTER JOIN dbo.stores AS s ON a.[state] = s.[state] ORDER BY title; SELECT au_id, stor_id FROM dbo.authors AS a RIGHT OUTER JOIN dbo.stores AS s ON a.[state] = s.[state] ORDER BY title;

48 A Beginner’s Guide To SQL/Charlotte McGary

INNER JOIN LEFT OUTER JOIN RIGHT OUTER JOIN First 20 of 46 rows: First 20 of 53 rows: First 20 of 48 rows:

The INNER JOIN produces 46 records: only those records where there is a matching state in both the authors and stores table (includes authors and stores only if the store is in one of the states in which an author lives or an author lives in one of the states containing a store). The LEFT OUTER JOIN produces 53 records: all records in the authors table, even if there is no matching state in the stores table (includes authors in states other than where a store is). The RIGHT OUTER JOIN produces 48 records: all records in the stores table, even if there is no matching state in the authors table (includes stores in states other than where an author lives).

A Beginner’s Guide To SQL/Charlotte McGary 49

Self Joins A self-join joins a table to itself and compares values in one or more columns in the table. For example, to display all authors who live in the same city and state as the author with the author_id 213-46-8915 (last name “Green”), execute the following query. (Use the author’s unique ID instead of his last name, just in case there is more than one author with the same last name.) SELECT a1.au_lname, a1.au_fname, a1.city, a1.[state] FROM dbo.authors AS a1 INNER JOIN dbo.authors AS a2 ON a1.city = a2.city WHERE a2.au_id = '213-46-8915' ORDER BY a1.city, a1.au_lname, a1.au_fname;

You can also combine a self-join with either an inner or outer join. The following example finds all titles having the same type as “The Busy Executive’s Database Guide”, where the title has more than one author: USE pubs SELECT t1.title, t1.[type] FROM dbo.titles AS t1 INNER JOIN dbo.titles AS t2 ON t1.[type] = t2.[type] INNER JOIN dbo.titleview AS tv ON t1.title = tv.title WHERE t2.title = 'The Busy Executive''s Database Guide' AND au_ord > 1 ORDER BY title;

Notice the two successive single quotes ('') in the line WHERE t2.title = 'The Busy Executive''s Database Guide' Using two single quotes in a row tells SQL to treat these two quotes as one. This is called “escaping” a character. Otherwise, because we’re using a single quote to contain the title string, if we used one single quote for the apostrophe, then SQL would interpret the line as follows, producing an error.

50 A Beginner’s Guide To SQL/Charlotte McGary

A Beginner’s Guide To SQL/Charlotte McGary 51

Concatenating Strings Strings are simply several individual characters “strung” together, hence the word “string”. While we may think of strings and characters as the same thing, to a computer they are represented quite differently. That is, the letter a defined as the data type CHAR is not the same to a computer as the letter a defined as a VARCHAR data type. To concatenate strings simply means to connect two or more strings together as one string. For example, in the author table there are separate columns for an author’s first and last names. If you want to display both names in one column, you can connect the two columns in a SELECT statement. (The ' ' in the SELECT statement below inserts a space between the names.) SELECT au_fname + ' ' + au_lname AS "Author", title FROM dbo.authors AS a, dbo.titles AS t, dbo.titleauthor AS ta WHERE ta.title_id = t.title_id AND ta.au_id = a.au_id ORDER BY title;

52 A Beginner’s Guide To SQL/Charlotte McGary

Review 3 1. Use an explicit Inner Join to query the dbo.titles and dbo.publishers tables from the

pubs database in order to display the publisher’s name, the title, and the publication date for each title. Use table aliases and sort the results first by publisher name then by title.

HINT: To figure out what column to use when comparing tables, search for what is probably the one column name they have in common.

2. Modify the following query to change the display format of “Firstname Lastname” to “Lastname, Firstname” and give the result column the alias “Author”.

SELECT au_fname + ' ' + au_lname AS "Author", title FROM dbo.authors AS a, dbo.titles AS t, dbo.titleauthor AS ta WHERE ta.title_id = t.title_id AND ta.au_id = a.au_id ORDER BY title;

The modified query will display the following 25 records:

A Beginner’s Guide To SQL/Charlotte McGary 53

3. Using an implicit Inner Join, query the dbo.publishers and dbo.titles tables in the pubs database to list all publishers who pay their authors a royalty higher than 10 percent (the percentages are listed as whole numbers in the table).

4. Using either an implicit Inner Join or an explicit Inner Join, query the dbo.publishers and dbo.titles tables in the pubs database to list all publishers who pay their authors a royalty higher than 10 percent, but list each publisher’s name only once. Use the column alias “Pubs with high royalty” for the publisher name column.

HINT: Use DISTINCT.

54 A Beginner’s Guide To SQL/Charlotte McGary

5. Using an explicit Inner Join to query the dbo.titles and dbo.roysched tables in the pubs database, display all titles and the average “lorange” and “hirange” associated with each. Group by title and use the following aliases: Column Alias title Book Title average lorange Average Low average hirange Average High

6. You will query the dbo.titles and dbo.roysched tables in for this exercise. Use an

Outer Join to display titles and corresponding royalties from the dbo.titles table even when there is no matching title_id in the dbo.roysched table. Be sure to list each title only once.

7. Using an implicit Inner Join, query the dbo.authors, dbo.titles, and dbo.titleauthors

tables to list all authors (last name only), their book titles, and the royalty they were paid for each title, only if the royalty is not NULL. Use aliases for the table names, and sort the results in ascending order by author’s last name, then by title.

A Beginner’s Guide To SQL/Charlotte McGary 55

8. Write two queries joining the dbo.titles with the dbo.sales tables to display the title,

qty and stor_id columns. The first query will display the columns, even if there are no matching title IDs in the dbo.sales table. The second query will display the columns even if there are no matching values in the dbo.titles table. Use aliases for the table names, and sort the results in ascending order by qty.

56 A Beginner’s Guide To SQL/Charlotte McGary

LESSON 4 In this lesson you will learn how to combine query results with the UNION statement, how to use a static string value as output in a column, and how to use date and time conversion functions.

A Beginner’s Guide To SQL/Charlotte McGary 57

Combining query results The UNION operator One way to combine the results of two or more queries is to link them with the UNION operator. For example, suppose you want to list all authors who do not live in CA. But you also want to list all authors whose last name begins with “G” regardless of where they live. If execute the queries separately, you get the following results. Authors who do not live in CA: SELECT au_lname, au_fname, [state] FROM dbo.authors WHERE state <> 'CA'; Authors whose last name begins with “G”: SELECT au_lname, au_fname, [state] FROM dbo.authors WHERE au_lname LIKE 'G%'; Now combine the two queries with the UNION operator: SELECT au_lname, au_fname, [state] FROM dbo.authors WHERE state <> 'CA' UNION SELECT au_lname, au_fname, [state] FROM dbo.authors WHERE au_lname LIKE 'G%'; Notice that only the final query has the ending semi-colon “;”.

58 A Beginner’s Guide To SQL/Charlotte McGary

Unions must have at least two SELECT statements, with the UNION operator placed between each query, and each query must specify the same number of column names (or functions) and data type. You can use ORDER BY after the last query if you want to sort the results.

Using Static Strings for Column Values The following example finds the names of all authors with a CA address and all publishers with a CA address: SELECT au_fname + ' ' + au_lname FROM dbo.authors WHERE [state] = 'CA' UNION SELECT pub_name FROM dbo.publishers WHERE [state] = 'CA' Unfortunately, because we are creating a union using two different column names, there will be no column name in the results unless we create one using an alias: SELECT au_fname + ' ' + au_lname AS "Based in CA" FROM dbo.authors WHERE [state] = 'CA' UNION SELECT pub_name FROM dbo.publishers WHERE [state] = 'CA' You can also add a static string as a column value. For example, to insert a column at the beginning of each record that indicates if the result is an author’s name or a publisher’s name, modify the above query as follows: SELECT 'Author' AS "Author/Publisher", au_fname + ' ' + au_lname AS "Based in CA" FROM dbo.authors WHERE [state] = 'CA' UNION SELECT 'Publisher', pub_name FROM dbo.publishers WHERE [state] = 'CA'

A Beginner’s Guide To SQL/Charlotte McGary 59

You only add a column alias to the first SELECT statement. Subsequent queries added with the UNION statement will use that alias in the column position it was assigned to. The following example adds another UNION. This query finds the names of all authors living in CA, all publishers with a CA address, and all titles published in CA. A static string is used to create the first results column, which will display the text “Author”, “Publisher”, or “Title” depending on whether the result is from the dbo.authors, dbo.publishers, or dbo.titles table. SELECT 'Author' AS "Author/Publisher/Title", au_fname + ' ' + au_lname AS "Based or Published in CA" FROM dbo.authors WHERE [state] = 'CA' UNION SELECT 'Publisher', pub_name FROM dbo.publishers WHERE [state] = 'CA' UNION SELECT 'Title', t.title FROM dbo.titles AS t, dbo.publishers AS p WHERE t.pub_id = p.pub_id AND p.[state] = 'CA';

60 A Beginner’s Guide To SQL/Charlotte McGary

Date and Time Functions and Conversions Although every DBMS has data types for date and time, they don’t all store those values in the same format. The examples in this document refer to the format used by SQL Server. For a complete list of SQL Server date and time conversion functions, see http://technet.microsoft.com/en-us/library/ms186724.aspx. The data types we’ll be discussing in this section are as follows:

Data Type Description DATE Stores date TIME Stores time DATETIME Stores date and time to the millisecond SMALLDATETIME Stores date and time to the minute

Date and time functions There are several functions you can use in SQL server to retrieve and manipulate the date and time. The functions we’ll be covering in this section are as follows:

Function Description GETDATE() Retrieves the current system date and time DATEPART() Returns specified part of date

The GETDATE() function To retrieve the current date and time (SQL Server), execute the following query: SELECT GETDATE(); This shows the year-month-day followed by the hours:minutes:seconds, which translates to June 2, 2012 at approximately 3:26 pm and 33 seconds.

The DATEPART() function This function returns the part of the date you specify in the following format:

DATEPART(part, date)

A Beginner’s Guide To SQL/Charlotte McGary 61

The “part” portion of the DATEPART function can retrieve numerous parts of the date, but the ones we’ll be discussing are shown in the following table:

part value year yy month mm day dd hour hh minute mi second ss dayofyear dy

You can display the individual parts of the current date as follows: SELECT GETDATE() AS "Today's Date", DATEPART(yy, GETDATE()) AS "Year", DATEPART(mm, GETDATE()) AS "Month", DATEPART(dd, GETDATE()) AS "Day", DATEPART(hh, GETDATE()) AS "Hour", DATEPART(mi, GETDATE()) AS "Minute", DATEPART(ss, GETDATE()) AS "Second", DATEPART(dy, GETDATE()) AS "Days into Year";

To display specific parts of a column having the DATETIME data type, replace the GETDATE() function in the DATEPART conversion with the column name. For example, to retrieve all titles published in the year 2004 from the dbo.titles table in the pubs database, execute the following query: SELECT title, pubdate FROM dbo.titles WHERE DATEPART(yy, pubdate) = 1994;

To find all titles published in June, regardless of the year: SELECT title, pubdate FROM dbo.titles WHERE DATEPART(mm, pubdate) = 6;

62 A Beginner’s Guide To SQL/Charlotte McGary

To list the year, month, and day of publication in separate columns, followed by the title and sorted in ascending order by year, month, and title. You can accomplish this with the following query: SELECT DATEPART(yy, pubdate) AS "Year Published", DATEPART(mm, pubdate) AS "Month Published", DATEPART(dd, pubdate) AS "Day Published", title FROM dbo.titles ORDER BY "Year Published", "Month Published", "Day Published",title;

A Beginner’s Guide To SQL/Charlotte McGary 63

Converting dates to strings In our titles table, pubdate is the datetime data type. If we want to check for specific dates, we need to convert pubdate to a string in order to do the comparison. Although it wasn’t mentioned in our previous section on Converting Data Types, there is an additional, optional value you can use with the CONVERT function. The syntax is as follows:

CONVERT (resulting data type, value to be converted, style)

where style is an integer value that specifies how the expression is to be converted. We will most often be using the datetime style 120, which tells the CONVERT function to convert to yyyy-mm-dd hh:mi:ss format. For a complete listing of possible style values, see http://msdn.microsoft.com/en-us/library/ms187928.aspx. For example, to retrieve a list of all books published between January 1, 1994 and December 31, 2013, execute the following query: SELECT title AS Title, CONVERT(CHAR(10), pubdate, 120) AS "Date Published" FROM dbo.titles WHERE CONVERT(CHAR(10), pubdate, 120) BETWEEN '1994-01-01' AND '2013-12-31' ORDER BY "Date Published";

Another way to do retrieve the same information produced by the previous query: SELECT title AS Title, CONVERT(CHAR(10), pubdate, 101) AS "Date Published" FROM dbo.titles WHERE DATEPART(yy, pubdate) >= '1994' AND DATEPART(yy, pubdate) <= '2013' AND DATEPART(mm, pubdate) >= '01' AND DATEPART(mm, pubdate) <= '12' ORDER BY "Date Published"; If you just want to compare years, we could have selected the year with DATEPART instead of using CONVERT.

64 A Beginner’s Guide To SQL/Charlotte McGary

SELECT title AS Title, DATEPART(yy,pubdate) AS "Year Published" FROM dbo.titles WHERE DATEPART(yy,pubdate) BETWEEN 1994 AND 2013 ORDER BY "Year Published"; If you just want to display the date, time of publication, along with the time in hh:mm:ss format, execute the following query: SELECT title AS Title, CONVERT(DATE, pubdate) AS "Date Published", CONVERT(TIME, pubdate) AS "Time Published", CONVERT(VARCHAR, pubdate, 108) AS "Time as hh:mm:ss" FROM dbo.titles;

The above query just takes the year, month, and day portions of the DATETIME field. It does not rearrange the order in which the year, month, and day appear. To do that we have to nest our conversions and add the style parameter. To convert the date and time to just the date in the formats mm-dd-yy and mm-dd-yyyy execute the following query: SELECT title AS Title, CONVERT(VARCHAR(8),pubdate,10) AS "Date Published (10)", CONVERT(VARCHAR(10),pubdate,110) AS "Date Published (110)" FROM dbo.titles;

A Beginner’s Guide To SQL/Charlotte McGary 65

To convert to mm/dd/yy format, change the 10 to 1: SELECT title AS Title, CONVERT(VARCHAR(8), pubdate, 1) AS "Date Published" FROM dbo.titles; To convert to mm/dd/yyyy format, change the 10 to 101: SELECT title AS Title, CONVERT(VARCHAR(10), pubdate, 101) AS "Date Published" FROM dbo.titles; Always, remember to make the VARCHAR length large enough to accommodate the numbers and the separators. Using VARCHAR() without a number uses the maximum allowed size of 255 characters.

66 A Beginner’s Guide To SQL/Charlotte McGary

More Date Conversion Examples SELECT GETDATE() AS "Full Date", CONVERT(CHAR(10),GETDATE(),120) AS "yyyy-mm-dd", CONVERT(CHAR(12),GETDATE(),107) AS "Mon dd, yyyy";

SELECT CONVERT(VARCHAR,GETDATE(),100) AS "dd Mon yyyy (AM/PM)", CONVERT(VARCHAR,GETDATE(),113) AS "dd Mon yyyy (24-hr+ms)";

SELECT title, pubdate, DATEPART(yy,pubdate) AS "Year Only", DATEPART(mm,pubdate) AS "Month #", CONVERT(CHAR(3),pubdate,100) AS "Month Name" FROM dbo.titles;

A Beginner’s Guide To SQL/Charlotte McGary 67

Review 4 1. Use a UNION statement to unite two queries on the dbo.titles tables in the pubs

database. The first query will display the title, price, and advance for all titles with a price of 2.99. The second query will display the same information for all titles with an advance less than or equal to 5000.

2. Using the pubs database, query the dbo.employee table to display the last name, first name, and hire date for every employee who was hired in the year 1991, in descending order by hire date.

3. Using the pubs database, query the dbo.employee table to display the last name and hire date of every employee who was hired after 1992. Display in descending order by hire date.

68 A Beginner’s Guide To SQL/Charlotte McGary

4. Modify the query you created in Exercise 3 to display the hire date column name as “Hire Date” and the hire date values in the format mm-dd-yy.

5. Using the pubs database, query the dbo.employee table to display the last name of each employee whose last name begins with the letter “L” and the year that person was hired. Use “Year Hired” as the column name for the year. Sort in ascending order by employee’s last name.

6. Using the pubs database, query the dbo.employee table to display the last name of

every employee whose last name begins with the letter “L”, and the hire date in three separate columns—one for year, one for month, and one for day of year. Use “Year Hired”, “Month Hired”, and “Days into Year Hired”, respectively, as the column names for the date parts. Sort in ascending order by employee’s last name.

69 A Beginner’s Guide To SQL/Charlotte McGary

APPENDICES

70 A Beginner’s Guide To SQL/Charlotte McGary

Appendix A: SQL Server Management Studio Accessing the DBMS Most likely you will connect remotely to a Microsoft SQL Server 2008 R2 system where you will then be able to access the DBMS as follows: Click StartAll ProgramsMicrosoft SQL Server 2008 R2SQL Server Management Studio

Connecting to your database server After you open SQL Server Management Studio, you will see a “Connect to Server” dialog box similar to the following:

A Beginner’s Guide To SQL/Charlotte McGary 71

Click “Connect” and you will see a screen similar to the following:

To see a list of available databases on the server, click the “+” symbol to the left of the “Databases” folder. To create a query, select a database (“pubs” in this example), then click “New Query.” The query window will open with the default query name: Type your query in the query window. When finished, execute your query by clicking the F5 key or the “! Execute” button, which will no longer be grayed-out once you’ve typed in a query. The results of your query will appear in a grid below the query window. For example, to get a listing of all tables in the pubs database, execute the system query

72 A Beginner’s Guide To SQL/Charlotte McGary

EXEC sp_tables; which will produce the following results:

To save a query, right click on the tab showing the current query name, select “Save SQLQuery1.sql” then save it as the name you choose in whatever location you’ve set up to hold your query files. To run a saved query, click FileOpenFile, navigate to the .SQL file you have saved, then click “Open.” After the file appears in the query window, you can execute the query.

73 A Beginner’s Guide To SQL/Charlotte McGary

Appendix B: The Pubs Database Working with pubs Although AdventureWorks is the newest database (used with SQL Server 2008 R2), the older pubs database remains a favorite of those new to SQL because of its simplicity and intuitive design. It can be downloaded with the Northwind database at http://www.microsoft.com/en-us/download/details.aspx?id=23654. Below is a screen shot from SQL Server Management Studio showing a portion of the pubs database with the columns information for the authors, titleauthor, and titles tables expanded, along with a sample query:

A Beginner’s Guide To SQL/Charlotte McGary 74

Appendix C: Solutions to Review Exercises Review 1 1. Using the pubs database, query the dbo.authors

table to display all authors’ first and last names in ascending order by last name, then by first name.

SOLUTION:

USE pubs SELECT au_fname, au_lname FROM dbo.authors ORDER BY au_lname, au_fname;

2. Using the pubs database, query the dbo.authors table to display the first and last names of all authors whose last name begins with the letter “G”.

SOLUTION:

SELECT au_fname, au_lname FROM dbo.authors WHERE au_lname LIKE 'G%';

A Beginner’s Guide To SQL/Charlotte McGary 75

3. Using the pubs database, query the dbo.authors table to display last names, city, and state for all authors living in CA but do not live in Oakland. Sort in descending order by city.

SOLUTION:

SELECT au_lname, city, state FROM dbo.authors WHERE state = 'CA' AND city <> 'Oakland' ORDER BY city DESC;

4. Using the pubs database, query the dbo.titles table to display the title id, title, and price for all records with a NULL value in their price field. (HINT: See the operators section of this document.)

SOLUTION:

SELECT title_id, title, price FROM dbo.titles WHERE price IS NULL;

5. Using the pubs database, query the dbo.titles table to display all titles and prices, where the price is between $2.00 and $10.00 (inclusive).

SOLUTION:

SELECT title, price FROM dbo.titles WHERE price BETWEEN 2 AND 10;

6. Using the pubs database, query the dbo.titles table to display all titles that do not start

with the letter “T” in ascending order by title.

SOLUTION: SELECT title FROM dbo.titles WHERE NOT title LIKE 'T%' ORDER BY title;

76 A Beginner’s Guide To SQL/Charlotte McGary

7. Using the pubs database, query the dbo.titles table to display the title and price for all books whose price is 2.99, 14.99, or 19.99.

SELECT title, price FROM dbo.titles WHERE price IN (2.99, 14.99, 19.99) ORDER BY price;

8. Using the pubs database, display a list of all publisher IDs and names, in ascending order by publisher name. (This is a simple query. The challenge is in determining which table you need to query, and the column names you should select.)

SOLUTION:

SELECT pub_id, pub_name FROM dbo.publishers ORDER BY pub_name;

9. Using the pubs database, query the dbo.employee table to display records only for those employees not having a middle initial. Sort in descending order first by the employee’s last name.

SOLUTION:

SELECT * FROM dbo.employee WHERE minit = ' '; ORDER BY lname DESC ;

A Beginner’s Guide To SQL/Charlotte McGary 77

Review 2 1. Using the pubs database, query the dbo.titles table to find the total amount of

advances paid for all books. Use the alias Total_Advances for the results column header.

SOLUTION:

SELECT SUM(advance) AS Total_Advances FROM dbo.titles;

2. Using the pubs database, query the dbo.titles table to find the total number of psychology books. Use Psychology_Count as the column header for the results.

SOLUTION:

SELECT COUNT([type]) AS Psychology_Count FROM dbo.titles GROUP BY [type] HAVING [type] = 'psychology';

3. Using the pubs database, query the dbo.titles table to list the book type and the number of publishers that carry each type of book, ignoring any title with the type UNDECIDED. Use the column alias “Num. pubs publishing this type” for your calculated value.

SOLUTION:

SELECT [type], COUNT(pub_id) AS "Num. pubs publishing this type" FROM dbo.titles GROUP BY [type] HAVING [type] <> 'UNDECIDED';

78 A Beginner’s Guide To SQL/Charlotte McGary

4. Using the pubs database, query the dbo.authors table to list the state and the number of authors living in each state. Use the alias “Authors per state” for the results column. (HINT: You are counting the number of times each state appears in the table.)

SOLUTION:

SELECT state, COUNT(state) AS "Authors per state" FROM dbo.authors GROUP BY state;

or SELECT state, COUNT(au_id) AS "Authors per state" FROM dbo.authors GROUP BY state;

5. Using the pubs database, query the dbo.titleauthor table to list the author’s ID and the number of titles for each author having more than one book title associated with his name. Use the alias Num_titles for the results column.

HINTS: • Use GROUP BY and the dbo.titleauthor table. • We know we need author info (au_id) and that we’re counting titles (title_id).

But we don’t just want a general count of all titles. We want the number of titles grouped by author. Additionally, we only want to display a record if the number of titles for that author is greater than 1.

SOLUTION:

SELECT au_id, COUNT(title_id) AS Num_titles FROM dbo.titleauthor GROUP BY au_id HAVING COUNT(title_id) > 1;

A Beginner’s Guide To SQL/Charlotte McGary 79

6. Using the pubs database, query the dbo.titles table to list each publisher’s ID and the number of different types of books it sells. (HINT: you want to count distinct values.) Use the alias “Types of book sold” for the results column. Save this query. You will modify it in Exercise #7.

SOLUTION:

SELECT pub_id, COUNT(DISTINCT[type]) AS "Types of Books Sold" FROM dbo.titles GROUP BY pub_id

7. Modify the query you created in Exercise 6 to display only publishers who publish more than 2 types of books.

SOLUTION:

SELECT pub_id, COUNT(DISTINCT[type]) AS "Types of Books Sold" FROM dbo.titles GROUP BY pub_id HAVING COUNT(DISTINCT[type]) > 2;

80 A Beginner’s Guide To SQL/Charlotte McGary

Review 3 1. Use an explicit Inner Join to query the dbo.titles and dbo.publishers tables from the

pubs database in order to display the publisher’s name, the title, and the publication date for each title. Use table aliases and sort the results first by publisher name then by title.

HINT: To figure out what column to use when comparing tables, search for what is probably the one column name they have in common.

SOLUTION:

SELECT pub_name, title, pubdate FROM dbo.titles AS t INNER JOIN dbo.publishers AS p ON t.pub_id = p.pub_id ORDER BY pub_name, title;

2. Modify the following query to change the display format of “Firstname Lastname” to “Lastname, Firstname” and give the column the alias “Author”.

SOLUTION:

SELECT au_lname + ', ' + au_fname AS "Author", title FROM dbo.authors AS a, dbo.titles AS t, dbo.titleauthor AS ta WHERE ta.title_id = t.title_id AND ta.au_id = a.au_id ORDER BY title;

A Beginner’s Guide To SQL/Charlotte McGary 81

3. Using an implicit Inner Join, query the dbo.publishers and dbo.titles tables in the pubs database to list all publishers who pay their authors a royalty higher than 10 percent (the percentages are listed as whole numbers in the table) SOLUTION:

SELECT pub_name, royalty FROM dbo.publishers, dbo.titles WHERE dbo.publishers.pub_id = dbo.titles.pub_id AND royalty > 10;

82 A Beginner’s Guide To SQL/Charlotte McGary

4. Using either an implicit Inner Join or an explicit Inner Join, query the dbo.publishers and dbo.titles tables in the pubs database to list all publishers who pay their authors a royalty higher than 10 percent, but list each publisher’s name only once. Use the column alias “Pubs with high royalty” for the publisher name column. SOLUTION:

SELECT DISTINCT pub_name AS "Pubs with high royalty" FROM dbo.publishers, dbo.titles WHERE dbo.publishers.pub_id = dbo.titles.pub_id AND royalty > 10;

or

SELECT DISTINCT pub_name AS "Pubs with high royalty" FROM dbo.publishers INNER JOIN dbo.titles ON dbo.publishers.pub_id = dbo.titles.pub_id AND royalty > 10;

5. Using an explicit Inner Join to query the dbo.titles and dbo.roysched tables in the

pubs database, display all titles and the average “lorange” and “hirange” associated with each. Group by title and use the following aliases: Column Alias title Book Title average lorange Average Low average hirange Average High SOLUTION:

SELECT title AS "Book Title", AVG(lorange) AS "Average Low", AVG(hirange) AS "Average High" FROM dbo.titles AS t INNER JOIN dbo.roysched AS r ON t.title_id = r.title_id GROUP BY title;

A Beginner’s Guide To SQL/Charlotte McGary 83

6. You will query the dbo.titles and dbo.roysched tables in for this exercise. Use an Outer Join to display titles and corresponding royalties from the dbo.titles table even when there is no matching title_id in the dbo.roysched table. Be sure to list each title only once. SOLUTION:

SELECT DISTINCT title, t.royalty FROM dbo.titles AS t LEFT OUTER JOIN dbo.roysched AS r ON t.title_id = r.title_id ORDER BY title; With the LEFT OUTER JOIN, the two titles from the titles table that have no corresponding titlle_id in royalty schedule table are also displayed.

84 A Beginner’s Guide To SQL/Charlotte McGary

7. Using an implicit Inner Join, query the dbo.authors, dbo.titles, and dbo.titleauthors tables to list all authors (last name only), their book titles, and the royalty they were paid for each title, only if the royalty is not NULL. Use aliases for the table names, and sort the results in ascending order by author’s last name, then by title. SOLUTION:

SELECT au_lname, title, royalty FROM dbo.authors AS a, dbo.titles AS t, dbo.titleauthor AS ta WHERE a.au_id = ta.au_id AND t.title_id = ta.title_id AND NOT royalty IS NULL ORDER BY au_lname, title;

8. Write two queries joining the dbo.titles with the dbo.sales tables to display the title,

qty, and stor_id columns. The first query will display the columns, even if there are no matching title IDs in the dbo.sales table. The second query will display the columns even if there are no matching values in the dbo.titles table. Use aliases for the table names, and sort the results in ascending order by qty.

SOLUTIONS:

A Beginner’s Guide To SQL/Charlotte McGary 85

SELECT title, qty, stor_id SELECT title, qty, stor_id FROM dbo.titles AS t FROM dbo.titles AS t LEFT OUTER JOIN dbo.sales AS s RIGHT OUTER JOIN dbo.sales AS s ON t.title_id = s.title_id ON t.title_id = s.title_id ORDER BY qty; ORDER BY qty;

86 A Beginner’s Guide To SQL/Charlotte McGary

Review 4 1. Use a UNION statement to unite two queries on the dbo.titles tables in the pubs

database. The first query will display the title, price, and advance for all titles with a price of 2.99. The second query will display the same information for all titles with an advance less than or equal to 5000.

SELECT title, price, advance FROM dbo.titles WHERE price = 2.99 UNION SELECT title, price, advance FROM dbo.titles AS T WHERE advance <= 5000 ORDER BY title;

2. Using the pubs database, query the dbo.employee table to display the last name, first name, and hire date for every employee who was hired in the year 1991, in descending order by hire date. SOLUTION: SELECT lname, fname, hire_date FROM dbo.employee WHERE DATEPART(yy, hire_date) = 1991 ORDER BY hire_date DESC;

A Beginner’s Guide To SQL/Charlotte McGary 87

3. Using the pubs database, query the dbo.employee table to display the last name and hire date of every employee who was hired after 1992. Display in descending order by hire date. (You will be modifying this query in exercise #4 below.) SOLUTION: SELECT lname, hire_date FROM dbo.employee WHERE DATEPART(yy, hire_date) > 1992 ORDER BY hire_date DESC;

4. Modify the query you created in Exercise 3 to display the hire date column name as “Hire Date” and the hire date values in the format mm-dd-yy. SOLUTION: SELECT lname, CONVERT(VARCHAR(8), hire_date, 10) AS "Hire Date" FROM dbo.employee WHERE DATEPART(yy, hire_date) > 1992 ORDER BY hire_date DESC;

88 A Beginner’s Guide To SQL/Charlotte McGary

5. Using the pubs database, query the dbo.employee table to display the last name of each employee whose last name begins with the letter “L” and the year that person was hired. Use “Year Hired” as the column name for the year. Sort in ascending order by employee’s last name. SOLUTION: SELECT lname, DATEPART(yy, hire_date) AS "Year Hired" FROM dbo.employee WHERE lname LIKE 'L%' ORDER BY lname;

6. Using the pubs database, query the dbo.employee table to display the last name of every employee whose last name begins with the letter “L”, and the hire date in three separate columns—one for year, one for month, and one for day of year. Use “Year Hired”, “Month Hired”, and “Days into Year Hired”, respectively, as the column names for the date parts. Sort in ascending order by employee’s last name. SOLUTION: SELECT lname, DATEPART(yy, hire_date) AS "Year Hired", DATEPART(mm, hire_date) AS "Month Hired", DATEPART(dy, hire_date) AS "Days into Year Hired" FROM dbo.employee WHERE lname LIKE 'L%' ORDER BY lname;

89 A Beginner’s Guide To SQL/Charlotte McGary

Appendix D: Additional Resources Books

Title Author ISBN-13 Sams Teach Yourself SQL in 10 Minutes Ben Forta 978-0-672-32567-0 Sams Teach Yourself SQL in 24 Hours Ryan Stephens

Ron Plew Arie D. Jones

978-0-672-33018-6

SQL Third Edition Visual Quickstart Guide

Chirs Fehily 978-0-321-55357-7

Web Tutorials http://www.w3schools.com/sql/default.asp http://www.sql.org/sql-database/sql-tutorial/ http://www.sqlcourse.com/index.html http://beginner-sql-tutorial.com/sql.htm