You are on page 1of 18

INTERACTIVE SQL EXAMPLES

create a table to store information about weather observation stations:


-- No duplicate ID fields allowed
CREATE TABLE STATION
(ID INTEGER PRIMARY KEY,
CITY CHAR(20),
STATE CHAR(2),
LAT_N REAL,
LONG_W REAL);
populate the table STATION with a few rows:
INSERT INTO STATION VALUES (13, 'Phoenix', 'AZ', 33, 112);
INSERT INTO STATION VALUES (44, 'Denver', 'CO', 40, 105);
INSERT INTO STATION VALUES (66, 'Caribou', 'ME', 47, 68);
query to look at table STATION in undefined order:
SELECT * FROM STATION;
ID
13
44
66

CITY
Phoenix
Denver
Caribou

STATE
AZ
CO
ME

LAT_N
33
40
47

LONG_W
112
105
68

query to select Northern stations (Northern latitude > 39.7):


-- selecting only certain rows is called a "restriction".
SELECT * FROM STATION
WHERE LAT_N > 39.7;
ID
44
66

CITY
Denver
Caribou

STATE
CO
ME

LAT_N
40
47

LONG_W
105
68

query to select only ID, CITY, and STATE columns:


-- selecting only certain columns is called a "projection".
SELECT ID, CITY, STATE FROM STATION;
ID
13
44
66

CITY
Phoenix
Denver
Caribou

STATE
AZ
CO
ME

query to both "restrict" and "project":


SELECT ID, CITY, STATE FROM STATION
WHERE LAT_N > 39.7;
ID
44
66

CITY

STATE

Denver
Caribou

CO
ME

create another table to store normalized temperature and precipitation data:


-- ID field must match some STATION table ID
(so name and location will be known).
-- allowable ranges will be enforced for other values.
-- no duplicate ID and MONTH combinations.
-- temperature is in degrees Fahrenheit.
-- rainfall is in inches.
CREATE TABLE STATS
(ID INTEGER REFERENCES STATION(ID),
MONTH INTEGER CHECK (MONTH BETWEEN 1 AND 12),
TEMP_F REAL CHECK (TEMP_F BETWEEN -80 AND 150),
RAIN_I REAL CHECK (RAIN_I BETWEEN 0 AND 100),
PRIMARY KEY (ID, MONTH));
populate the table STATS with some statistics for January and July:
INSERT INTO STATS VALUES (13, 1, 57.4, 0.31);
INSERT INTO STATS VALUES (13, 7, 91.7, 5.15);
INSERT INTO STATS VALUES (44, 1, 27.3, 0.18);
INSERT INTO STATS VALUES (44, 7, 74.8, 2.11);
INSERT INTO STATS VALUES (66, 1, 6.7, 2.10);
INSERT INTO STATS VALUES (66, 7, 65.8, 4.52);
query to look at table STATS in undefined order:
SELECT * FROM STATS;
ID
13
13
44
44
66
66

MONTH

TEMP_F
1
7
1
7
1
7

RAIN_I
57.4
91.7
27.3
74.8
6.7
65.8

.31
5.15
.18
2.11
2.1
4.52

query to look at table STATS, picking up location information by joining with table STATION on
the ID column:
-- matching two tables on a common column is called a "join".
-- the column names often match, but this is not required.
-- only the column values are required to match.
SELECT * FROM STATION, STATS
WHERE STATION.ID = STATS.ID;
ID
13
13
44
44
66
66

CITY
Phoenix
Phoenix
Denver
Denver
Caribou
Caribou

ST
AZ
AZ
CO
CO
ME
ME

LAT_N
33
33
40
40
47
47

LONG_W
112
112
105
105
68
68

ID
13
13
44
44
66
66

MONTH
1
7
1
7
1
7

TEMP_F
57.4
91.7
27.3
74.8
6.7
65.8

RAIN_I
.31
5.15
.18
2.11
2.1
4.52

query to look at the table STATS, ordered by month and greatest rainfall, with columns rearranged:
SELECT MONTH, ID, RAIN_I, TEMP_F
FROM STATS
ORDER BY MONTH, RAIN_I DESC;
MONTH

ID

RAIN_I

1 66
1 13
1 44
7 13
7 66
7 44

TEMP_F
2.1
.31
.18
5.15
4.52
2.11

6.7
57.4
27.3
91.7
65.8
74.8

query to look at temperatures for July from table STATS, lowest temperatures first, picking up city
name and latitude by joining with table STATION on the ID column:
SELECT LAT_N, CITY, TEMP_F
FROM STATS, STATION
WHERE MONTH = 7
AND STATS.ID = STATION.ID
ORDER BY TEMP_F;

LAT_N

CITY

TEMP_F

47
40
33

Caribou
Denver
Phoenix

65.8
74.8
91.7

query to show MAX and MIN temperatures as well as average rainfall for each station:
SELECT MAX(TEMP_F), MIN(TEMP_F), AVG(RAIN_I), ID
FROM STATS
GROUP BY ID;
MAX(TEMP_F)

MIN(TEMP_F)
91.7
74.8
65.8

AVG(RAIN_I)
57.4
27.3
6.7

ID
2.73 13
1.145 44
3.31 66

query (with subquery) to show stations with year-round average temperature above 50 degrees:
-- rows are selected from the STATION table based on related values in the STATS table.
SELECT * FROM STATION
WHERE 50 < (SELECT AVG(TEMP_F) FROM STATS
WHERE STATION.ID = STATS.ID);
ID
13
44

CITY
Phoenix
Denver

ST
AZ
CO

LAT_N
33
40

LONG_W
112
105

create a view (derived table or persistent query) to convert Fahrenheit to Celsius and inches to
centimeters:
CREATE VIEW METRIC_STATS (ID, MONTH, TEMP_C, RAIN_C) AS
SELECT ID,
MONTH,
(TEMP_F - 32) * 5 /9,
RAIN_I * 0.3937
FROM STATS;
query to look at table STATS in a metric light (through the new view):
SELECT * FROM METRIC_STATS;

ID

MONTH

13
13
44
44
66
66

TEMP_C
1
7
1
7
1
7

RAIN_C
14.1111111
33.1666667
-2.6111111
23.7777778
-14.055556
18.7777778

.122047
2.027555
.070866
.830707
.82677
1.779524

another metric query restricted to January below-freezing (0 Celsius) data, sorted on rainfall:
SELECT * FROM METRIC_STATS
WHERE TEMP_C < 0 AND MONTH = 1
ORDER BY RAIN_C;
ID
44
66

MONTH
1
1

TEMP_C
-2.6111111
-14.055556

RAIN_C
.070866
.82677

Interactive SQL Update Examples


update all rows of table STATS to compensate for faulty rain gauges known to read 0.01 inches low:
UPDATE STATS SET RAIN_I = RAIN_I + 0.01;
and take a look:
SELECT * FROM STATS;
ID
13
13
44
44
66
66

MONTH

TEMP_F
1
7
1
7
1
7

RAIN_I
57.4
91.7
27.3
74.8
6.7
65.8

update one row, Denver's July temperature reading, to correct a data entry error:

.32
5.16
.19
2.12
2.11
4.53

UPDATE STATS SET TEMP_F = 74.9


WHERE ID = 44
AND MONTH = 7;
and take a look:
SELECT * FROM STATS;
ID

MONTH

13
13
44
44
66
66

TEMP_F
1
7
1
7
1
7

RAIN_I
57.4
91.7
27.3
74.9
6.7
65.8

.32
5.16
.19
2.12
2.11
4.53

make the above changes permanent:


-- they were only temporary until now.
COMMIT WORK;
update two rows, Denver's rainfall readings:
UPDATE STATS SET RAIN_I = 4.50
WHERE ID = 44;
and take a look:
SELECT * FROM STATS;
ID
13
13
44
44
66
66

MONTH

TEMP_F
1
7
1
7
1
7

Oops! We meant to update just the July reading! Undo that update:
-- undoes only updates since the last COMMIT WORK.
ROLLBACK WORK;
and take a look:

RAIN_I
57.4
91.7
27.3
74.9
6.7
65.8

.32
5.16
4.5
4.5
2.11
4.53

SELECT * FROM STATS;


ID

MONTH

13
13
44
44
66
66

TEMP_F
1
7
1
7
1
7

RAIN_I
57.4
91.7
27.3
74.9
6.7
65.8

.32
5.16
.19
2.12
2.11
4.53

now update Denver's July rainfall reading and make it permanent:


UPDATE STATS SET RAIN_I = 4.50
WHERE ID = 44
AND MONTH = 7;
COMMIT WORK;
and take a look:
SELECT * FROM STATS;
ID
13
13
44
44
66
66

MONTH

TEMP_F
1
7
1
7
1
7

RAIN_I
57.4
91.7
27.3
74.9
6.7
65.8

.32
5.16
.19
4.5
2.11
4.53

delete July data and East Coast data from both tables:
-- note that we use longitude values from the related STATION table to determine which STAT stations
were east of 90 degrees.
DELETE FROM STATS
WHERE MONTH = 7
OR ID IN (SELECT ID FROM STATION
WHERE LONG_W < 90);
DELETE FROM STATION WHERE LONG_W < 90;
COMMIT WORK;
and take a look:

SELECT * FROM STATION;


ID
13
44

CITY
Phoenix
Denver

ST

LAT_N

AZ
CO

33
40

LONG_W
112
105

SELECT * FROM STATS;


ID
13
44

MONTH
1
1

TEMP_F
57.4
27.3

RAIN_I
.32
.19

View METRIC_STATS, a Fahrenheit-to-Centigrade and inches-to-centimeters conversion of table


STATS, reflects the updates made to the underlying table.
SELECT * FROM METRIC_STATS;
ID
13
44

MONTH
1
1

TEMP_C
14.1111111
-2.6111111

RAIN_C
.125984
.074803

SELECT * FROM Employees

If we want only specific columns (as is usually the case), we can/should explicitly specify them in a commaseparated list, as in

SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees

which results in the specified fields of data for all of the rows in the table:

Explicitly specifying the desired fields also allows us to control the order in which the fields are returned, so that if
we wanted the last name to appear before the first name, we could write

SELECT EmployeeID, LastName, FirstName, HireDate, City FROM Employees

The WHERE Clause


The next thing we want to do is to start limiting, or filtering, the data we fetch from the database. By adding
a WHERE clause to the SELECT statement, we add one (or more) conditions that must be met by the selected
data. This will limit the number of rows that answer the query and are fetched. In many cases, this is where most
of the "action" of a query takes place.
We can continue with our previous query, and limit it to only those employees living in London:

SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees


WHERE City = 'London'

resulting in

If you wanted to get the opposite, the employees who do not live in London, you would write

SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees


WHERE City <> 'London'

It is not necessary to test for equality; you can also use the standard equality/inequality operators that you would
expect. For example, to get a list of employees who where hired on or after a given date, you would write

SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees


WHERE HireDate >= '1-july-1993'

and get the resulting rows

Of course, we can write more complex conditions. The obvious way to do this is by having multiple conditions in
the WHERE clause. If we want to know which employees were hired between two given dates, we could write

SELECT
FROM
WHERE

EmployeeID, FirstName, LastName, HireDate, City


Employees
(HireDate >= '1-june-1992') AND (HireDate <= '15-december-1993')

resulting in

Note that SQL also has a special BETWEEN operator that checks to see if a value is between two values (including
equality on both ends). This allows us to rewrite the previous query as

SELECT
FROM
WHERE

EmployeeID, FirstName, LastName, HireDate, City


Employees
HireDate BETWEEN '1-june-1992' AND '15-december-1993'

We could also use the NOT operator, to fetch those rows that are not between the specified dates:

SELECT
FROM
WHERE

EmployeeID, FirstName, LastName, HireDate, City


Employees
HireDate NOT BETWEEN '1-june-1992' AND '15-december-1993'

Let us finish this section on the WHERE clause by looking at two additional, slightly more sophisticated,
comparison operators.
What if we want to check if a column value is equal to more than one value? If it is only 2 values, then it is easy
enough to test for each of those values, combining them with theOR operator and writing something like

SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees


WHERE City = 'London' OR City = 'Seattle'

However, if there are three, four, or more values that we want to compare against, the above approach quickly
becomes messy. In such cases, we can use the IN operator to test against a set of values. If we wanted to see if
the City was either Seattle, Tacoma, or Redmond, we would write

SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees


WHERE City IN ('Seattle', 'Tacoma', 'Redmond')

producing the results shown below.

As with the BETWEEN operator, here too we can reverse the results obtained and query for those rows where
City is not in the specified list:
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
WHERE City NOT IN ('Seattle', 'Tacoma', 'Redmond')
Finally, the LIKE operator allows us to perform basic pattern-matching using wildcard characters. For Microsoft
SQL Server, the wildcard characters are defined as follows:

Wildcard

Description

_ (underscore)

matches any single character

matches a string of one or more characters

[]

matches any single character within the specified range (e.g. [a-f]) or set (e.g. [abcde

[^]

matches any single character not within the specified range (e.g. [^a-f]) or set (e.g. [^

A few examples should help clarify these rules.

WHERE FirstName LIKE '_im' finds all three-letter first names that end with 'im' (e.g. Jim, Tim).

WHERE LastName LIKE '%stein' finds all employees whose last name ends with 'stein'

WHERE LastName LIKE '%stein%' finds all employees whose last name includes 'stein' anywhere in the
name.

WHERE FirstName LIKE '[JT]im' finds three-letter first names that end with 'im' and begin with either 'J' or
'T' (that is, only Jim and Tim)

WHERE LastName LIKE 'm[^c]%' finds all last names beginning with 'm' where the following (second)
letter is not 'c'.

Here too, we can opt to use the NOT operator: to find all of the employees whose first name does not start with
'M' or 'A', we would write

SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees


WHERE (FirstName NOT LIKE 'M%') AND (FirstName NOT LIKE 'A%')

resulting in

The ORDER BY Clause


Until now, we have been discussing filtering the data: that is, defining the conditions that determine which rows
will be included in the final set of rows to be fetched and returned from the database. Once we have determined
which columns and rows will be included in the results of our SELECT query, we may want to control the order in
which the rows appearsorting the data.
To sort the data rows, we include the ORDER BY clause. The ORDER BY clause includes one or more column
names that specify the sort order. If we return to one of our firstSELECT statements, we can sort its results by
City with the following statement:

SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees


ORDER BY City

By default, the sort order for a column is ascending (from lowest value to highest value), as shown below for the
previous query:

If we want the sort order for a column to be descending, we can include the DESC keyword after the column
name.
The ORDER BY clause is not limited to a single column. You can include a comma-delimited list of columns to sort
bythe rows will all be sorted by the first column specified and then by the next column specified. If we add the
Country field to the SELECT clause and want to sort by Country and City, we would write:

SELECT EmployeeID, FirstName, LastName, HireDate, Country, City FROM Employees


ORDER BY Country, City DESC

Note that to make it interesting, we have specified the sort order for the City column to be descending (from
highest to lowest value). The sort order for the Country column is still ascending. We could be more explicit about
this by writing

SELECT EmployeeID, FirstName, LastName, HireDate, Country, City FROM Employees


ORDER BY Country ASC, City DESC

but this is not necessary and is rarely done. The results returned by this query are

It is important to note that a column does not need to be included in the list of selected (returned) columns in
order to be used in the ORDER BY clause. If we don't need to see/use the Country values, but are only interested
in them as the primary sorting field we could write the query as

SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees


ORDER BY Country ASC, City DESC

with the results being sorted in the same order as before:

Counting the number of records in the PRODUCTS_TBL table:

SELECT COUNT(*) FROM PRODUCTS_TBL;

COUNT(*)

----------

1 row selected.

Counting the number of values for PROD_ID in the PRODUCTS_TBL table:

SELECT COUNT(PROD_ID) FROM PRODUCTS_TBL;

COUNT(PROD_ID)

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

1 row selected.

The SQL subquery syntax


There is no general syntax; subqueries are regular queries placed inside parenthesis.
Subqueries can be used in different ways and at different locations inside a query:
Here is an subquery with the IN operator

1.

SELECT column-names

2.

FROM table-name1

3.
4.
5.

WHERE value IN (SELECT column-name


FROM table-name2
WHERE condition)

Subqueries can also assign column values for each record:

1.

SELECT column1 = (SELECT column-name FROM table-name WHERE condition),

2.

column-names

3.

FROM table-name

4.

WEHRE condition

ORDERITEM
Id
OrderId
ProductId
UnitPrice
Quantity
PRODUCT
Id
ProductName
SupplierId
UnitPrice
Package
IsDiscontinued

SQL Subquery Examples

Problem: List products with order quantities greater than 100.

1.
2.
3.

SELECT ProductName
FROM Product
WHERE Id IN (SELECT ProductId

4.

FROM OrderItem

5.

WHERE Quantity > 100)

Results: 12 records
PoductName
Guaran Fantstica

Schoggi Schokolade
Chartreuse verte
Jack's New England Clam Chowder
Rogede sild
Manjimup Dried Apples
Perth Pasties

CUSTOMER
Id
FirstName
LastName
City
Country
Phone
ORDER
Id
OrderDate
OrderNumber
CustomerId
TotalAmount
1.
SELECT ProductName
2.
3.
4.

FROM Product
WHERE Id = ANY
(SELECT ProductId

5.
6.

FROM OrderItem
WHERE Quantity = 1)

You might also like