SQL Exam 6

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 20

Table-valued functions exercise-(6)

An inline table-valued fn to show events for a year


1. The aim of this exercise is to create a table-valued function to show events for
any given year. For example, you should be able to run this query:

SELECT
*
FROM
dbo.fnEventsForYear(1918) AS e

To show the events which took place in 1918:

The events which take place in this year.

To help you, here is the syntax of a table-valued function:

CREATE FUNCTION FunctionName(


@Parameter datatype
)
RETURNS TABLE
AS
RETURN
SELECT ...

If you get this working, create another query which joins to the results of your table-
valued function to show the category and country for each event in a given year:

What you should see for 1918.

Optionally, save all this as Annual events, then close down your query.
Table function to summarise a continent for month
2. The aim of this exercise is to create a function that returns summary information
about a given continent during a chosen month. For example, you should be
able to run this command:

SELECT * FROM dbo.fnContinentSummary('Europe','April')

Passing in Europe and April like this should give the following results:

April unsurprisingly is an important month.

To do this, you will need to join


the tblCategory, tblEvent, tblCountry and tblContinent tables together, and select
COUNT(DISTINCT()) from each table. To help you, here's what your function code
should look like:

CREATE FUNCTION ChooseName


(@Para1 DataType, @Para2 DataType)
RETURNS Table
AS
RETURN
--Select statement that will form the table

Optionally save your query as Counting By Continents.sql then close it down.

Use a table-valued function to show episodes for a given doctor


3. USE Doctor Who training database.

Create a table-valued function to list out all of the episode details for a given doctor.
Here's the syntax, as a reminder:

CREATE FUNCTION FunctionName(


@Parameter1 DataType1,
...,
@ParameterN DataTypeN
)
RETURNS TABLE
AS
-- return data from a single SELECT statement
RETURN
SELECT
ColumnList
FROM
TableName

You should be able to run this query to show the episodes in which anyone
called Chris appeared:

-- show the Christopher Eccleston episodes


SELECT * FROM dbo.fnEpisodesByDoctor('Chris')

Here's what this should show:

The first few of the 13 episodes in which Christopher Eccleston appeared.

Create a query based on this (you'll need to give the results returned by the function a
table alias, and create a join from this to the tblAuthor table) to show the details
(including author) for episodes made by doctors called Chris:

The 13 episodes made by Christopher Eccleston, bringing in the author name from
the tblAuthortable.

Optionally, save this query as Episodes by doctor.sql, then close it down.


A complicated multi-statement table-valued function!
USE Doctor Who training database.

4. You'll find this exercise much easier to do if you have access to the following
scalar functions:

Function Argument Returns

fnCompanions EpisodeId A comma-delimited list of the companions in this episode

fnEnemies EpisodeId A comma-delimited list of the enemies in this episode

You may have already created these as part of a (much) earlier exercise on variables; otherwise, this
should be your first task!

Write a function (the one in the answer is called fnSilly, for reasons which will become
apparent) which takes two arguments:

Argument Heading 2 Example

@CompanionName Part or all of a companion name Wilf

@EnemyName Part or all of an enemy name Ood

The easiest way to describe what your function should return is by example. If you run
this SQL:

-- show episodes featuring either Wilfred Mott or The Ood


SELECT * FROM dbo.fnSilly('wilf','ood')

Then you should see this:

There are 2 episodes featuring Wilf, and 4 featuring the Ood (although one of the episodes is the
same in each case).
The important thing to notice is that where an episode is selected by reason of its companion,
the Appearing column shows a comma-delimited list of the companions appearing in the episode;
where it is selected by reason of its enemy, the Appearing column shows a comma-delimited list of
the enemies appearing in the episode.

Still bored? You could always work out how to combine the companions and enemies
in the Appearing column for a duplicated episode, to avoid it appearing twice in the list
(this isn't included in the answer!).

Optionally, save your query as Getting silly.sql, then close it down.

Create a function to list episodes for a given series and author


USE Doctor Who training database.

5. Create a function to list out the episodes for any given series number and part of
an author name. This could begin:

CREATE FUNCTION fnChosenEpisodes(


@SeriesNumber int,
@AuthorName varchar(100)
)
RETURNS TABLE
AS

For example, you could run this SQL query:

SELECT * FROM dbo.fnChosenEpisodes(1,'russell')

This would show the 9 series one episodes written by Russell T. Davies:

The first 2 or 3 of the 9 episodes in series 1 written by Russell T. Davies

Functions can't have optional parameters, but you can pass dummy values and detect
them. Amend your function so that it shows:

 Episodes for any series if you pass the first argument as Null;
 Episodes for any author if you pass the second argument as Null.

To help you know whether you've succeeded, here's what 4 possible combinations
should show:
-- there is 1 episode written by Steven Moffat for series 2
SELECT COUNT(*) FROM dbo.fnChosenEpisodes(2,'moffat')
-- -- there are 14 episodes in series 2 (for any author)
SELECT COUNT(*) FROM dbo.fnChosenEpisodes(2,null)
-- there are 32 episodes written by Steven Moffat (for any series)
SELECT COUNT(*) FROM dbo.fnChosenEpisodes(null,'moffat')
-- there are 117 episodes (any series, any author)
SELECT COUNT(*) FROM dbo.fnChosenEpisodes(null,null)

Optionally, save your query as Series and author.sql, then close it down.

A multi statement table valued function to count vowels


6. Create a function which takes in a vowel and returns a table containing all of the
categories, countries and continents which contain that vowel:

The top 4 results when you run your function using the vowel I as input, for example.

The function will need one parameter to take in the vowel and a table variable to store
the output from each separate select.

CREATE FUNCTION NameGoesHere


(@Para1 datatype)
RETURNS @TableName TABLE
(Column 1 datatype
,Column 2 datatype)
AS
BEGIN
INSERT INTO @TableName
--SELECTS GO IN HERE
INSERT INTO @TableName
--SELECTS GO IN HERE
INSERT INTO @TableName
--SELECTS GO IN HERE
RETURN
END

If you call your function once for each vowel within a SELECT statement, you could
display the results for each vowel:
A shock result - E wasn't the most common vowel.

Optionally save this as Vowel please bob.sql and close it down.

SQL exercises on DERIVED TABLES AND CTES –(13)

Create a CTE to make it easier to group by an expression


1. The aim of this exercise is to show the number of events whose descriptions
contain the words this and/or that:

Only 3 events have the holy grail: both THIS and THAT.

To do this you can find all events whose EventDetails column contains the
word this or that respectively, using the LIKE keyword.

In a single query solution you would have to use a CASE expression to determine the
value of IfThisand IfThat, then group by the same CASE expression. This is messy,
and makes it hard to make subsequent changes to your expression (as you have to do
it in two places). Read on!

Create a query to solve this problem in two passes:

Pass What to do

Create a CTE (called ThisAndThat?) to determine the values of the IfThis and IfThat flags
1
for each event.

2 Use this CTE to get the required results, as shown at the start of this exercise.

As an aide-memoire, here is the syntax for a CTE:

-- create CTE
WITH CteName AS (
SELECT ...
)
-- then immediately use it
SELECT ... FROM CteName

If you get this working and still have spare time, try changing or extending your query to
show the 3 events whose details contain both this and that:

Other than the text they contain, there's no obvious link between these 3 events.

Optionally, save this query as This and that.sql, then close it down.

Using derived tables


2. The aim of this exercise is to use a derived table to hold data before joining this
another table. Start by selecting only events which end with a letter
between N and Z.

Your query should return 300 results (including the CountryID column will allow us to join to
the tblCountry table).

Now turn this select into a derived table called Second_Half_Derived. The layout will
be something like this:

--Outer select
SELECT
EventName
FROM
(
--Your select goes here
) AS Second_half_Derived

Now join tblCountry to the derived table as you would for a normal table:
This should return 230 results - scroll down for some happier events.

Optionally save this as Deriving results.sql and close it down.

Show companions for specific authors' episodes, using a CTE


USE Doctor Who training database.

3. The aim of this exercise is to use a CTE (Common Table Expression) to extract
data from the database in two passes:

1. Get a list of all of the episodes written by authors with MP in their names.
2. Get a list of the companions featuring in these episodes.

You get no points for pointing out that you could do all of this in a single query!

Start by creating a query to show just the episode id numbers for those Doctor Who
episodes written by authors with MP as part of their names:

This should return 4 episodes (one written by Keith Temple, as it happens, and 3 by Steve
Thompson).

Now create a CTE from this, using the standard syntax:


-- previous command must end with a semi-colon (;)
WITH NameForYourCte AS (
SELECT ...
)
-- now use this in a query
SELECT
...
FROM
NameForYourCte
-- join this to other tables as normal
INNER JOIN ...

Your final query should join to your CTE to show the companions featuring in these
episodes (you may need to use the word DISTINCT to avoid some companions' names
appearing twice):

The results: 4 companions feature in episodes written by authors whose names contain MP.

Optionally, save your query as If that is not useful.sql, then close it down.

Create a series of CTES to answer a fairly contrived question


4. The aim of this exercise is to use CTEs to answer the following questions:

Step What to do Returns

1 Get a list of those events which contain none of the letters in the word OWL 3 rows

Use this to get a list of all of those events which take place in the countries for
2 9 rows
the events in list 1.

Get a third list of all of the events which share the same categories as any of the 116
3
events in the second list. rows

If you display the results in date order, you should get this:

The first few of the 116 events returned by your final query.
In the event that you get this working, save your query as Owl-free events, then close it
down.

Use a CTE to avoid including the same CASE statement twice


5. The aim of this exercise is to count the number of events by a column
called Era which you'll calculate, without including this calculation twice:

Here's what NOT to do - we want to avoid repeating the same calculation twice.

To do this, you can do the calculation in two bites, using a CTE to hold the intermediate
stage. First create a query to show the era for each event:
You could peek at the expression at the start of this exercise to get help on how to calculate the era!

Now store this as a CTE, and write a query using it which shows the number of events
per era:

What the entire query should show when you run it.

Save this query as Epoch-making, and close it down.

Recursion - carnival menu using CTEs


6. The Carnival database contains a table called tblMenu. This contains all of the
menus for a website:

Each menu has an id number, and also contains within its record the id of its parent

Use a recursive CTE based on this table to show all of the menus, with breadcrumbs:
This is what the results should look like!

Save this as Recursion using CTEs, then close it down.

CTEs to show continents with many events but few countries


7. The aim of this exercise is to list out all the continents which have:

 At least 3 countries; but also


 At most 10 events.

It's worth noting that there are many ways to solve this in SQL, but as is so often the case CTEs seem
to give the most intuitive approach.

To do this, all that you need to do is to create the following CTEs:

CTE What it should contain

ManyCountries A list of continents having at least 3 countries

FewEvents A list of continents in which no more than 10 events occurred

You can then join them together to show that there is only one country in both camps.
We won't spoil the surprise, but it has (in the Wise Owl database, at any rate) 4
countries and 9 events.

Optionally, save this as Toto.sql, then close it down.


Show the number of events for the top 3 categories/countries
8. Create two CTEs in the same query as follows:

CTE What it should list out

TopCountries The ids of the 3 countries with the most events (use TOP 3)

TopCategories The ids of the 3 categories with the most events (use TOP 3 again)

Use these to show all possible combinations in a final SELECT statement which should
look like this:

-- combine these together (every possible combination)


SELECT
cy.CountryID,
cg.CategoryID
FROM
TopCountries AS cy
CROSS JOIN TopCategories AS cg

The cross join gives all possible combinations of rows in the two CTEs - see below for what the
results should look like.

You should now be looking at something like this:

There are 9 possible combinations.

If you still have the energy, use this to show the number of events for each
combination:
United Kingdom / Politics is the combination of country and category which has the most events.

Save this as Country category combinations, then close it down.

Use a CTE to show enemies in certain Dr Who episodes


USE Doctor Who training database.

9. The aim of this exercise is to show a list of all of the enemies appearing in Doctor
Who episodes featuring Rose Tyler, but not David Tennant.

It's possible - probable, even - that you could do this with a single SELECTstatement, but CTEs
make it so much easier to divide a complicated query into two simpler parts!

First create a query to show the episode id numbers for the 13 episodes which feature
Rose Tyler as a companion, but not David Tennant as the Doctor.

Linking to this as a CTE, show the 8 enemies which appear in these episodes (some
more than once):
The enemies corresponding to episodes featuring Rose Tyler, but not David Tennant.

Optionally, save this query as Wonderful CTEs.sql, then close it down

Solve a complex question using CTEs and/or subqueries


USE Doctor Who training database.

10. Create a query which lists the David Tennant episodes for which none of the
enemies appear in any non-David Tennant episodes:

The first few of the 31 David Tennant episodes which don't share any enemies with any
other non-Tennant episodes.

There are probably hundreds of ways to do this in SQL, and the answer given doesn't claim
to use a particularly efficient one.

Optionally, save this query as Tennant only, then close it down.

Use a CTE to show events containing this and that


11.This exercise shows how you can use a CTE to make a complicated question
simple, by dividing it into two or more parts.

First create a query to list out for each event id the positions (if any) of the
words this and that:
The first few events in the list - the last two columns give the position (if any) at which the
words this and that can be found.

As an example, CHARINDEX('too','This too shall pass',1) would return 6, since the


word too starts at the 6th letter of the given text (if you begin counting at the 1st character).

Now using this as a CTE, show the two events which contain both words in the right
order (ie thisfollowed by that). You'll need to join your CTE to the events table, so that
you can pick up on the event name and event date, and display them.

The EventDetails column for these two events contains the words this and that, in this order.

Here's the sort of thing you'd have had to write without a CTE (showing how useful
CTEs are!):
SELECT
e.EventName,e.EventDetails,
CHARINDEX('this',e.EventDetails,1) AS ThisPosition,
CHARINDEX('that',e.EventDetails,1) AS ThatPosition
FROM tblEvent AS e
WHERE
-- must contain both strings ...
CHARINDEX('this',e.EventDetails,1) > 0 and
CHARINDEX('that',e.EventDetails,1) > 0 and
-- ... in the right order
CHARINDEX('this',e.EventDetails,1)
CHARINDEX('that',e.EventDetails,1)

Save your query as This and that revisited, and close it down.
Two CTEs to show space category data
12. Space: the final frontier. But it's also one of the countries in the events database.
The aim of this exercise is to list out all the categories of events which occurred in
the Space country:

Space events, as it turns out, only use two categories.

You'll then list all of the events which didn't occur in Space, with their country names
and categories:

The first few of the 444 events which didn't happen in the Space country.

You can then show the 8 countries which had non-Space events in the same category
as one of the Space events. Read on!

To start with, write a select statement to return the CategoryName of events which
occur in the country Space only:

Perhaps war should be a CategoryName in Space, since the satellite USA-193 was shot down by a
long-range missile?

Now turn this into a CTE so the list is stored in a table that we can join to later. The
layout looks like this:
With CTE_Name
AS
(
Your select statement goes here
)
-- Additional CTEs don't need a second WITH separate with commas
,CTE_Name
AS
(
)

The second CTE should hold a list of all the countries and their categories excluding
(<>) Space. You can then join the two CTEs together based on the CategoryName to
show the final answer:

The 8 countries in which events occurred in terrestrial (ie non-Space) countries, but where these
events' categories were shared by Spaceevents!

Use DISTINCT to remove duplicate countries (some countries had more than one qualifying event).

Optionally save this as Categories Out Of This World.sql then close it down.

Using a simple CTE to filter data


13. The aim of this exercise is to use a CTE to hold some data, before joining another
table to your CTE. Start by selecting only events which start with a letter
between A and M.

This should return 300 rows - including the CategoryID column will allow us to join this CTE to
the tblCategory table.

Now turn this SELECT statement into a CTE called First_Half_CTE. The layout will be
something like this:

WITH First_Half_CTE
AS
(
--Select goes here
)

Now extend your query to join the tblCategory table to the CTE in the same way as you
would join a normal table:

This should return 300 categories (scroll down to see more cheerful events).

Optionally save this as Passing my CTE.sql and close it down

You might also like