web/website/content/posts/2023-01-28-format-pretty-reports/_index.md
I can no longer bring myself to write bare SQL - PRQL makes building queries so easy. So here's how I use PRQL functions and aliases for naming variables to "pretty up" a SQL report.
I have an SQLite database with a table named PropertyData and I want to show
the change of column App_Total2020 and App_Total2021 across years,
displaying their values along with their percent change.
Of course, property values are in dollars. So I could simply display them as
450000. But they are more compelling if they're written as $450,000. Writing
the SQL code and format strings for each column would be tedious and
error-prone. PRQL allows me to create a dollars function and then use it
multiple times:
# dollars displays a numeric value as whole dollars with commas
let dollars = d -> s"""printf("$%,d",{d})"""
select {
(dollars App_Total2020),
(dollars App_Total2021),
}
I also want to compute the percent change between values. It's easy to create a
percent_diff function:
# percent_diff computes the amount (percent) the new differs from old
let percent_diff = old new -> 100.0*( new - old ) / old
One final function: the percent_diff function returns a floating point number
with many digits after the decimal place. I only want to display one place in my
results, with a trailing %. So I wrote a format_percent function that uses a
printf() to format the value.
# format_percent prints a floating point number with "%"
let format_percent = v -> s'printf("%1.1f%", {v})'
Use a PRQL alias to assign each column a nice name. This becomes its column heading. The examples above might be:
select {
Appraisal2020 = (dollars App_Total2020),
`Appraisal 2021` = (dollars App_Total2021),
}
Note how the second example puts the column heading in backticks to preserve spaces.
I want to sort results by the (numeric) percent change, but I don't want to
display that percentage value (with multiple decimal places) in the final table.
So I split the query into pieces: the first select collects all the necessary
columns, adding a new column using percent_diff. The query then sorts the
values and passes those results to a second select that's responsible for
formatting the column headings and contents (using aliases and
format_percent).
Here is my workflow for a typical query using the tools listed below: 1
.prql suffix for the file.Here's the PRQL for this example, followed by the result from my database. Note: Paste the PRQL query below into the Playground to see what the PRQL compiler produces.
# dollars displays a numeric value as dollars with commas
let dollars = d -> s"""printf('$%,d',{d})"""
# percent_diff computes the amount (percent) the new differs from old
let percent_diff = old new -> 100.0*( new - old ) / old
# format_percent prints a floating point number with "%"
let format_percent = v -> s"printf('%1.1f%', {v})"
# Step 1: First calculate important columns
from PropertyData
select {
Map, Lot,
App_Total2020,
App_Total2021,
pct_change = (percent_diff App_Total2020 App_Total2021),
}
# Step 2: Sort the resulting table by pct_change
sort {-pct_change}
# Step 3: Format the column headings and contents
select {
Map, Lot,
Appraisal2020 = (dollars App_Total2020),
`Appraisal 2021` = (dollars App_Total2021),
`Percent Change` = (format_percent pct_change),
}
take 20
The image below shows the result of that query:
select statement,dollars function formats values with $ and , as expected,percent_diff function computes the percent change between the old and
new values,format_percent function formats the value with a single decimal place
and appends a %.My Tools: I use Visual Studio Code to maintain a folder of PRQL queries for re-use. I use the PRQL VS Code extension to compile PRQL into SQL. My data is in a SQLite database, and I use DB Browser for SQLite to run queries. ↩