Добавил:
ИВТ (советую зайти в "Несортированное")rnПИН МАГА Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Database 2024 / Books / Искусство PostgreSQL.pdf
Скачиваний:
8
Добавлен:
20.11.2024
Размер:
1.62 Mб
Скачать

Chapter 7 SQL is Code j 64

respect or inspiration.

1select artist.name as artist,

2inspired.name as inspired,

3album.title as album,

4track.name as track

5

from

artist

6join track on track.name = artist.name

7join album on album.albumid = track.albumid

8

join artist inspired on inspired.artistid = album.artistid

9where artist.artistid <> inspired.artistid;

This gives the following result where we can see two cases of a singer naming a song af er their former band’s name:

artist │ inspired │ album │ track

═══════════════╪═══════════════╪════════════════════╪═══════════════

Iron Maiden

Paul

D'Ianno │

The Beast Live

Iron Maiden

Black Sabbath │

Ozzy

Osbourne │

Speak of the Devil │

Black Sabbath

(2 rows)

About the query itself, we can see we use the same table twice in the join clause, because in one case the artist we want to know about is the one issuing the track in one of their album, and in the other case it’s the artist that had their name picked as a track’s name. To be able to handle that without confusion, the query uses the SQL standard’s relation aliases.

In most cases, you will see very short relation aliases being used. When I typed that query in the psql console, I must admit I rst picked a1 and a2 for artist’s relation aliases, because it made it short and easy to type. We can compare such a choice with your variable naming policy. I don’t suppose you pass code review when using variable names such as a1 and a2 in your code, so don’t use them in your SQL query as aliases either.

Comments

The SQL standard comes with two kinds of comments, either per line with the double-dash pre x or per-block delimited with C-style comments using /* comment */ syntax. Note that contrary to C-style comments, SQL-style comments accept nested comments.

Let’s add some comments to our previous query:

Chapter 7 SQL is Code j 65

1 -- artists names used as track names by other artists

2select artist.name as artist,

3-- "inspired" is the other artist

4inspired.name as inspired,

5album.title as album,

6track.name as track

7

from

artist

8/*

9* Here we join the artist name on the track name,

10* which is not our usual kind of join and thus

11* we don't use the using() syntax. For

12* consistency and clarity of the query, we use

13* the "on" join condition syntax through the

14* whole query.

15*/

16join track

17on track.name = artist.name

18join album

19on album.albumid = track.albumid

20join artist inspired

21on inspired.artistid = album.artistid

22where artist.artistid <> inspired.artistid;

As with code comments, it’s pretty useless to explain what is obvious in the query. The general advice is to give details on what you though was unusual or di cult to write, so as to make the reader’s work as easy as possible. The goal of code comments is to avoid ever having to second-guess the intentions of the author(s) of it. SQL is code, so we pursue the same goal with SQL.

Comments could also be used to embed the source location where the query comes from in order to make nding it easier when we have to debug it in production, should we have to. Given the PostgreSQL’s application_name facility and a proper use of SQL les in your source code, one can wonder how helpful that technique is.

Unit Tests

SQL is code, so it needs to be tested. The general approach to unit testing code applies beautifully to SQL: given a known input a query should always return the same desired output. That allows you to change your query spelling at will and still check that the alternative still passes your tests.

Examples of query rewriting would include inlining common table expressions as

Chapter 7 SQL is Code j 66

sub-queries, expanding or branches in a where clause as union all branches, or maybe using window function rather than complex juggling with subqueries to obtain the same result. What I mean here is that there are a lot of ways to rewrite a query while keeping the same semantics and obtaining the same result.

Here’s an example of a query rewrite:

1with artist_albums as

2(

3select albumid, title

4

from

album

5

 

join artist using(artistid)

6where artist.name = 'AC/DC'

7)

8 select title, name, milliseconds

9from artist_albums

10

left join track

11

using(albumid)

12

order by trackid;

The same query may be rewritten with the exact same semantics (but di ferent run-time characteristics) like this:

1select title, name, milliseconds

2from (

3select albumid, title

4

from

album

5

 

join artist using(artistid)

6where artist.name = 'AC/DC'

7)

8as artist_albums

9left join track

10

using(albumid)

11

order by trackid;

The PostgreSQL project includes many SQL tests to validate its query parser, optimizer and executor. It uses a framework named the regression tests suite, based on a very simple idea:

1.

Run a SQL le containing your tests with psql

2.

Capture its output to a text le that includes the queries and their results

3.Compare the output with the expected one that is maintained in the repository with the standard diff utility

4.Report any di ference as a failure

You can have a look at PostgreSQL repository to see how it’s done, as an example we could pick src/test/regress/sql/aggregates.sql and its matching expected result le src/test/regress/expected/aggregates.out.

Chapter 7 SQL is Code j 67

Implementing that kind of regression testing for your application is quite easy, as the driver is only a thin wrapper around executing standard applications such as psql and diff. The idea would be to always have a setup and a teardown step in your SQL test les, wherein the setup step builds a database model and lls it with the test data, and the teardown step removes all that test data.

To automate such a setup and go beyond the obvious, the tool pgTap is a suite of database functions that make it easy to write TAP-emitting unit tests in psql scripts or xUnit-style test functions. The TAP output is suitable for harvesting, analysis, and reporting by a TAP harness, such as those used in Perl applications.

When using pgTap, see the relation-testing functions for implementing unit tests based on result sets. From the documentation, let’s pick a couple examples, testing against static result sets as VALUES:

1SELECT results_eq(

2'SELECT * FROM active_users()',

3$$

4VALUES (42, 'Anna'),

5

(19,

'Strongrrl'),

6

(39,

'Theory')

7$$,

8 'active_users() should return active users'

9);

and ARRAYS:

1SELECT results_eq(

2 'SELECT * FROM active_user_ids()', 3 ARRAY[ 2, 3, 4, 5]

4);

As you can see your unit tests are coded in SQL too. This means you have all the SQL power to write tests at your ngertips, and also that you can also check your schema integrity directly in SQL, using PostgreSQL catalog functions.

Straight from the pg_prove command-line tool for running and harnessing pgTAP tests, we can see how it looks:

1% pg_prove -U postgres tests/

2tests/coltap.....ok

3tests/hastap.....ok

4tests/moretap....ok

5tests/pg73.......ok

6tests/pktap......ok

7All tests successful.

8

Files=5, Tests=216, 1

wallclock secs

9

( 0.06 usr 0.02

sys + 0.08 cusr 0.07 csys = 0.23 CPU)

Соседние файлы в папке Books