LAD - Laboratorio di Archeologia Digitale
Sapienza Università di Roma

← Blog

Using SQL's CASE to extract data useful for statistical analysis from a database

Using SQL's CASE to extract data useful for statistical analysis from a database

This short article describes the use of the SQL CASE statement, presenting an archaeological case study.

CASE is a very powerful and often overlooked construct of the SQL language, which allows a series of given conditions to be evaluated and returns a value as soon as the first condition is satisfied, exactly like if-then-else statements in many other programming languages.

When a condition is met, the process stops and returns the corresponding given result. If no condition is met, the value contained in the ELSE clause is returned.

If, finally, the ELSE part is not provided, the value NULL is returned (which is different from the string 'NULL').

The syntax of CASE is as follows:

CASE
WHEN condition_1 THEN "something"
WHEN condition_2 THEN "something else"
...
ELSE "default value"
END

An example is worth a thousand explanations, so in the following paragraphs I introduce a real case study, the very one that led me to discover this function.

Specifically, I was working on a database relating to the Roman necropolis of the Roman city of Suasa and needed to extract some data to create fairly simple charts. In particular, I needed to quickly extract the total number of tombs broken down by phase, and within each phase the total number of inhumation burials and cremation burials. The end goal was to obtain a bar chart showing, for each phase, the total number of tombs for each of the two burial rites.

Below is an excerpt of the structure of the reference table from which to extract this information. The table is more complex, but here I only extract the columns that interest us.

Query:

SELECT id,
nome,
rito,
fase
FROM suasa__tombe;

Result:

idtomb numberritofase
1503cremazione10
2511cremazione8
4501cremazione11
5502cremazione11
6508cremazione10
7509cremazione10
8512inumazione7
10514cremazione10
11510cremazione10a
18518cremazione7
19519cremazione7
20520cremazione7
21521cremazione10
22522cremazione7
23524cremazione7
24523cremazione10
25528cremazione7
26530cremazione11
27533cremazione7
28531cremazione10
29529cremazione11
30526cremazione7
32534cremazione10
36535cremazione6
37537cremazione10b
38538inumazione12
39543cremazione10
40542cremazione10
41540cremazione10a
42544cremazione10
43539cremazione10b
44541inumazione12
45545inumazione12
46547cremazione10
48549cremazione10
49548cremazione10
50552cremazione10a
52550cremazione10
53555cremazione7
54556cremazione7
56557cremazione10
57558cremazione10
59554inumazione12
60560inumazione12
61561cremazione10
62567cremazione10a
63563inumazione12
64568cremazione10a
65566inumazione10a
66565inumazione12
67571cremazione9
68569cremazione10a
69570cremazione10a
70572cremazione9
71562inumazione12
72564inumazione12
73573cremazione11
74574cremazione11
75576cremazione11
76575cremazione11
77577cremazione11
78578cremazione10a
79579cremazione11
80580inumazione11
81581cremazione10b
82584cremazione10
83587cremazione10
84589inumazione10b
87582cremazione10b
88585cremazione11
89586cremazione11
90590cremazione9
91591cremazione9
92601cremazione9
93603cremazione9
95583cremazione9
96593cremazione10
97594cremazione10
98602cremazione7
99607cremazione7
100599cremazione9
101596inumazione10b
102598cremazione9
103595cremazione10b
104597inumazione10b
105600cremazione8
106606inumazione8
107608cremazione10a
108605cremazione9

Here, then, is the query to extract the data we need:

SELECT SUM(CASE WHEN rito = 'inumazione' THEN 1 ELSE 0 END) AS inumazioni,
SUM(CASE WHEN rito = 'cremazione' THEN 1 ELSE 0 END) AS cremazioni,
fase
FROM suasa__tombe
GROUP BY fase;

And finally, here are the results of the query, ready to be turned into a chart:

inumazionicremazionifase
02210
1910a
3510b
11211
9012
016
1127
128
0109

In this case we are using the CASE statement in the column definition, together with the SUM function, which calculates totals.

In detail, SUM(CASE WHEN rito = 'inumazione' THEN 1 ELSE 0 END) AS inumazioni defines a column to which the label or alias inumazioni is assigned, via the AS statement (follow this link for more information on AS), purely to make the output data easier to read.

The column contains a sum, whose elements are defined by CASE. For each row of the database, it is evaluated whether the boolean expression rito = 'inumazione' is true or false — in other words, whether the rito field contains exactly the value inumazione. If the answer is positive (boolean value TRUE), then (THEN) the number 1 is returned to the sum function (one unit is added to the inhumation count); otherwise (ELSE) 0 is returned (nothing is effectively added to the inhumation count).

The same reasoning applies to the second column, defined as: SUM(CASE WHEN rito = 'cremazione' THEN 1 ELSE 0 END) AS cremazioni, where only the label changes, and of course the reference value of the rito field.

We spoke above of counting values for each row, because in this query we are grouping values, specifically grouping them by the fase field, as is clear from the last part of the main query, GROUP BY fase, which adds a grouping principle (follow this link for more information on GROUP BY).

With CASE you can use all SQL operators (e.g. =, <, >, <=, >=, LIKE, etc.) and it is possible to chain multiple conditions using the usual AND or OR. What matters is that the expression between WHEN and THEN always returns a boolean value of TRUE or FALSE. The case shown above was fairly simple, but it is also possible to have several WHEN...THEN statements.

References