Appendix 7: Antipatterns
These are a few of the antipatterns I’ve seen while travelling through various CQL source files. They are in various categories.
Refer also to Appendix 8: Best Practices.
Common Schema
For these examples let’s create a couple of tables we might need for examples
CREATE TABLE foo (
id integer primary key,
name text
);
CREATE TABLE bar (
id integer primary key,
rate real
);
Declarations
DECLARE v LONG NOT NULL;
SET v := 1;
better:
LET v := 1L; -- long literals have the L suffix like in C
Similarly:
DECLARE v REAL NOT NULL;
SET v := 1;
better:
LET v := 1.0; -- use scientific notation or add .0 to make a real literal
Casts
Redundant casts fatten the code and don’t really add anything to readability. Sometimems it’s necessary to cast NULL to a particular type so that you can be sure that generated result set has the right data type, but most of the casts below are not necessary.
SELECT
CAST(foo.id as INTEGER) as id,
CAST(foo.name as TEXT) as name,
CAST(NULL as REAL) as rate
FROM foo
UNION ALL
SELECT
CAST(bar.id as INTEGER) as id,
CAST(NULL as TEXT) as name,
CAST(bar.rate as REAL) as rate
FROM bar
Better [at some point, redundant casts actually started generating errors.]
SELECT
foo.id,
foo.name,
CAST(NULL as REAL) as rate
FROM foo
UNION ALL
SELECT
bar.id,
CAST(NULL as TEXT) as name,
bar.rate
FROM bar
It’s possible to do the following to make this even cleaner:
-- somewhere central
#define NULL_TEXT CAST(NULL as TEXT)
#define NULL_REAL CAST(NULL as REAL)
#define NULL_INT CAST(NULL as INTEGER)
#define NULL_LONG CAST(NULL as LONG)
Then you can write
SELECT
foo.id,
foo.name,
NULL_REAL as rate
FROM foo
UNION ALL
SELECT
bar.id,
NULL_TEXT as name,
bar.rate
FROM bar
Booleans
TRUE and FALSE can be used as boolean literals.
SQLite doesn’t care about the type but CQL will get the type information it needs to make the columns of type BOOL
SELECT
foo.id,
foo.name,
NULL_REAL as rate,
TRUE as has_name, -- this is a bit artificial but you get the idea
FALSE as has_rate
FROM foo
UNION ALL
SELECT
bar.id,
NULL_TEXT as name,
bar.rate,
FALSE as has_name,
TRUE as has_rate
FROM bar
Boolean expressions and CASE/WHEN
It’s easy to get carried away with the power of CASE
expressions, I’ve seen this kind of thing:
CAST(CASE WHEN foo.name IS NULL THEN 0 ELSE 1 END AS BOOL)
But this is simply
foo.name IS NOT NULL
In general, if your case alternates are booleans a direct boolean expression would have served you better.
CASE and CAST and NULL
Somtimes there’s clamping or filtering going on in a case statement
CAST(CASE WHEN foo.name > 'm' THEN foo.name ELSE NULL END AS TEXT)
Here the CAST
is not needed at all so we could go to
CASE WHEN foo.name > 'm' THEN foo.name ELSE NULL END
NULL
is already the default value for the ELSE
clause so you never need ELSE NULL
So better:
CASE WHEN foo.name > 'm' THEN foo.name END
Filtering out NULLs
Consider
SELECT *
FROM foo
WHERE foo.name IS NOT NULL AND foo.name > 'm';
There’s no need to test for NOT NULL
here, the boolean will result in NULL
if foo.name
is null
which is not true so the WHERE
test will fail.
Better:
SELECT *
FROM foo
WHERE foo.name > 'm';
Not null boolean expressions
In this statement we do not want to have a null result for the boolean expression
SELECT
id,
name,
CAST(IFNULL(name > 'm', 0) AS BOOL) AS name_bigger_than_m
FROM FOO;
So now we’ve made several mistakes. We could have used the usual FALSE
defintion to avoid the cast.
But even that would have left us with an IFNULL that’s harder to read. Here’s a much simpler formulation:
SELECT
id,
name,
name > 'm' IS TRUE AS name_bigger_than_m
FROM FOO;
Even without the TRUE
macro you could do IS 1
above and still get a result of type BOOL NOT NULL
Using IS
when it makes sense to do so
This kind of boolean expression is also verbose for no reason
rate IS NOT NULL AND rate = 20
In a WHERE
clause probably rate = 20
suffices but even if you really need a NOT NULL BOOL
result the expression above is exactly what the IS
operator is for. e.g.
rate IS 20
The IS
operator is frequently avoided except for IS NULL
and IS NOT NULL
but it’s a general equality operator
with the added semantic that it never returns NULL
. NULL IS NULL
is true. NULL IS [anything not null]
is false.
Left joins that are not left joins
Consider
SELECT foo.id,
foo.name,
bar.rate
FROM foo
LEFT JOIN bar ON foo.id = bar.id
WHERE bar.rate > 5;
This is no longer a left join because the WHERE
clause demands a value for at least one column from bar
.
Better:
SELECT foo.id,
foo.name,
bar.rate
FROM foo
INNER JOIN bar ON foo.id = bar.id
WHERE bar.rate > 5;