Lint rules
Syntax Rules
case-keyword
Inconsistent capitalization of keywords
SDF Errors:
- SDF107
SQLFluff Alias: L010
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
# capitalization_policy is one of [consistent, upper, lower, pascal, snake, camel, off]
case-keyword: consistent # default
Anti-pattern
Referencing keywords in inconsistent capitalization. All references should be either uppercase or lowercase, not both.
select a
FROM my_table
Best-practice
SELECT a
FROM my_table
-- or
select a
from my_table
case-literal
Inconsistent capitalization of boolean
/null
literal
SDF Errors:
- SDF108
- SDF109
SQLFluff Alias: L040
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
# capitalization_policy is one of [consistent, upper, lower, pascal, snake, camel, off]
case-literal: consistent # default
Anti-pattern
Referencing literals in inconsistent capitalization. All references should be either uppercase or lowercase, not both.
SELECT
a,
null,
true,
FALSE
FROM my_table
Best-practice
SELECT
a,
NULL,
TRUE,
FALSE
FROM my_table
-- or
SELECT
a,
null,
true,
false
FROM my_table
case-type
Inconsistent capitalization of datatypes.
SDF Errors:
- SDF110
SQLFluff Alias: L063
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
# capitalization_policy is one of [consistent, upper, lower, pascal, snake, camel, off]
case-type: consistent # default
Anti-pattern
Referencing datatypes in inconsistent capitalization. All references should be either uppercase or lowercase, not both.
CREATE TABLE t (
a INT,
b varchar(100)
);
Best-practice
CREATE TABLE t (
a INT,
b VARCHAR(100)
);
-- or
CREATE TABLE t (
a int,
b varchar(100)
);
case-function
Inconsistent capitalization of function names
SDF Errors:
-SDF106
SQLFluff Alias: L030
Configuration:
workspace:
...
---
linter:
...
semantic-rules:
...
# capitalization_policy is one of [consistent, upper, lower, pascal, snake, camel, off]
case-function: null # default (off)
Anti-pattern
Referencing functions in inconsistent capitalization. All references should be either uppercase or lowercase, not both.
SELECT
max(a) AS max_a,
MIN(a) AS min_a
FROM my_table
Best-practice
SELECT
MAX(a) AS max_a,
MIN(a) AS min_a
FROM my_table
-- or
SELECT
max(a) AS max_a,
min(a) AS min_a
FROM my_table
disallow-these-identifiers
SDF Errors:
-SDF124
SQLFluff Alias: L029
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
# identifiers_to_block is an array of names to block from being identifiers. Can still be used in strings (e.g., '{blocked_identifier}').
disallow-these-identifiers: [] # default
flag-keywords-used-as-identifiers
Keywords should not be used as identifiers
SDF Errors:
-SDF125
SQLFluff Alias: L029
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
# ignore_words is an array of names to ignore in this rule
flag-keywords-used-as-identifiers: [] # default
Anti-pattern
Using built-in keywords or functions as identifiers.
SELECT
'my_table_name' AS table
FROM my_table
Best-practice
SELECT
'my_table_name' AS table_name
FROM my_table
disallow-these-chars-in-quoted-identifiers
Do not use special characters in identifiers
SDF Errors:
-SDF117
SQLFluff Alias: L057
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
# special_chars is a string of chars to block (e.g., "%
<>"")
disallow-these-chars-in-quoted-identifiers: ".#%{}
<>*?/!'
":@+`|=" # default
Anti-pattern
Using special characters within identifiers when creating or aliasing objects.
SELECT
num AS "My Num",
num > 100 AS "num > 100",
num / 100.0 AS "num %"
FROM my_table
Best-practice
SELECT
num AS my_num,
num > 100 AS num_greater_than_100,
num / 100.0 AS num_percentage
FROM my_table
flag-unnecessary-quoted-identifiers
Unnecessary quoted identifier
SDF Errors:
-SDF118
SQLFluff Alias: L059
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
flag-unnecessary-quoted-identifiers: on # default
Anti-pattern Using quotes when naming a valid identifier, that is not a reserved keyword.
SELECT 'a' AS "a"
Best-practice Use unquoted identifiers where possible.
SELECT 'a' AS a
flag-inconsistent-qualified-column-reference
Inconsistent column references in SELECT clause of both qualified and unqualified references
SDF Errors:
-SDF112
SQLFluff Alias: L028
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
# column_qualifier is one of [qualified, unqualified, consistent]
flag-inconsistent-qualified-column-reference: consistent # default
Anti-pattern
Using both qualified and unqualified column references in the SELECT
clause. Reference should be consistent.
SELECT
a,
my_table.b
FROM my_table
Best-practice
Use unquoted identifiers where possible.
-- qualified
SELECT
my_table.a,
my_table.b
FROM my_table
-- unqualified
SELECT
a,
b
FROM my_table
flag-unqualified-multi-source-column-reference
Columns reference should specify source table or view in queries with more than one source.
SDF Errors:
-SDF116
SQLFluff Alias: L027
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
flag_unqualified-multi-source-column-reference: on # default
Anti-pattern
Not specifying the source table or view when referencing columns in queries involving more than one source, thus creating ambiguity.
SELECT
col_a,
col_b
FROM table_a LEFT OUTER JOIN table_b USING (col_a)
Best-practice
SELECT
table_a.col_a,
table_b.col_b
FROM table_a LEFT OUTER JOIN table_b USING (col_a)
flag-inconsistent-ordinal-column-reference
Inconsistent column references in GROUP BY/ORDER BY clauses of both ordinal and non-ordinal
SDF Errors:
-SDF105
SQLFluff Alias: L054
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
flag-inconsistent-ordinal-column-reference: on # default
Anti-pattern
Using both explicit and implicit column references in the GROUP BY
or ORDER BY
clause. Reference should be consistent.
SELECT
a,
b,
SUM(b) AS sum_b
FROM my_table
GROUP BY
a, 2
ORDER BY
1, b
Best-practice
Use unquoted identifiers where possible.
-- explicit
SELECT
a,
b,
SUM(c) AS sum_c
FROM my_table
GROUP BY
a, b
ORDER BY
a, b
-- implicit
SELECT
a,
b,
SUM(c) AS sum_c
FROM my_table
GROUP BY
1, 2
ORDER BY
1, 2
flag-unnecessary-else
Redundant ELSE NULL
in a case when statement
SDF Errors:
-SDF119
SQLFluff Alias: L035
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
flag-unnecessary-else: on # default
Anti-pattern
Stating ELSE NULL
within a CASE WHEN
statement.
SELECT
CASE
WHEN num = 1 THEN '1'
WHEN num = 2 THEN '2'
ELSE NULL -- this is always implicit
END AS a
FROM my_table
Best-practice
SELECT
CASE
WHEN num = 1 THEN '1'
WHEN num = 2 THEN '2'
END AS a
FROM my_table
flag-unnecessary-case
CASE statement can be simplified
SDF Errors:
-SDF120
SQLFluff Alias: L043
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
flag-unnecessary-case: on # default
Anti-pattern
The following CASE
statement are unnecessary and can be simplified:
-- boolean outcome
SELECT
CASE
WHEN num > 1 THEN TRUE
ELSE FALSE
END AS is_num_greater_than_one
FROM my_table
-- handle NULLs
SELECT
CASE
WHEN num IS NULL THEN 0
ELSE num
END AS num_not_null
FROM my_table
Best-practice
Use simpler SQL syntax:
-- boolean outcome
SELECT
COALESCE(num > 1, FALSE) AS is_num_greater_than_one
FROM my_table
-- handle NULLs
SELECT
COALESCE(num, 0) AS num_not_null
FROM my_table
flag-unused-cte
Query defines a CTE (common-table expression) but does not use it
SDF Errors:
-SDF121
SQLFluff Alias: L045
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
flag-unused-cte: on # default
Anti-pattern
Defining CTEs that are not being used in the query (e.g., sub_2
in the example below).
WITH
sub1 AS (
SELECT 'a' AS a
),
sub2 AS (
SELECT 'b' AS b
)
SELECT a
FROM sub1
Best-practice
WITH
sub1 AS (
SELECT 'a' AS a
),
SELECT a
FROM sub1
flag-unnecessary-nested-case
Nested CASE statement in ELSE clause could be flattened.
SDF Errors:
-SDF122
SQLFluff Alias: L058
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
flag-unnecessary-nested-case: on # default
Anti-pattern
Unnecessarily nesting CASE
statements.
SELECT
CASE
WHEN num = 1 THEN '1'
ELSE
CASE
WHEN num = 2 THEN '2'
END
END AS my_num_to_varchar
FROM my_table
Best-practice
SELECT
CASE
WHEN num = 1 THEN '1'
WHEN num = 2 THEN '2'
END AS my_num_to_varchar
FROM my_table
flag-distinct-parenthesis
DISTINCT used with parentheses
SDF Errors:
-SDF123
SQLFluff Alias: L015
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
flag-distinct-parenthesis: on # default
Anti-pattern
Using DISTINCT
with parentheses. In the example below, all columns are affected by the DISTINCT
but can mistaken to think only column a
is.
SELECT DISTINCT(a), b
FROM my_table
Best-practice
SELECT DISTINCT a, b
FROM my_table
disallow-subquery-in
Join/From clauses should not contain subqueries. Use CTEs instead
SDF Errors:
-SDF113
SQLFluff Alias: L042
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
# subquery_scope is one of ['join', 'from', 'both']
disallow-subquery-in: [from, join] # default
Anti-pattern
Selecting from a subquery instead of defining the subquery in a CTE.
SELECT
sub.a
FROM (SELECT 'a' AS a) AS sub
Best-practice
WITH
sub AS (
SELECT 'a' AS a
)
SELECT
sub.a
FROM sub
flag-misordered-join-condition
Join conditions column references should follow tables reference order
SDF Errors:
-SDF114
SQLFluff Alias: ST09
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
flag-misordered-join-condition: on # default
Anti-pattern
Referencing columns in join ON
clause statement in a different order than
the tables are referenced in the JOIN
clause
SELECT
first_table.a,
second_table.a
FROM first_table
LEFT OUTER JOIN second_table ON (
second_table.a = first_table.a
)
Best-practice
SELECT
first_table.a,
second_table.a
FROM first_table
LEFT OUTER JOIN second_table ON (
first_table.a = second_table.a
)
flag-select-item-order
Select wildcards then simple targets before calculations and aggregates.
SDF Errors:
-SDF115
SQLFluff Alias: L034
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
flag-select-item-order: on # default
Anti-pattern
Wildcard listed after single targets
SELECT
a,
*,
b
FROM my_table
Best-practice
*,
a,
b
FROM my_table
flag-trailing-semicolon-after-statements
Statements should not end with a semi-colon. Multi-statements must be separated with a semi-colon but the final statement should NOT end with one.
SDF Errors:
-SDF111
SQLFluff Alias: L052
Configuration:
workspace:
...
---
linter:
...
syntax-rules:
...
flag-trailing-semicolon-after-statements: on # default
Anti-pattern
Ending the last statement with a semi-colon
-- single statement
SELECT a FROM my_table;
-- multi-statement
SELECT a FROM table_a;
SELECT b FROM table_b;
Best-practice
-- single statement
SELECT a FROM my_table
-- multi-statement
SELECT a FROM table_a;
SELECT b FROM table_b
Semantic Rules
case-column
Inconsistent capitalization of column names
Configuration:
workspace:
...
---
linter:
...
semantic-rules:
...
# capitalization_policy is one of [consistent, upper, lower, pascal, snake, camel, off]
case-column: null # default (off)
Anti-pattern
Referencing column names in inconsistent capitalization.
SELECT
col_name_a,
ColNameB,
col_nameC
FROM my_table
Best-practice
SELECT
col_name_a,
col_name_b,
col_name_c
FROM my_table
-- or
SELECT
ColNameA,
ColNameB,
ColNameC
FROM my_table
-- or other formats applied to all column names consistently
case-table
Inconsistent capitalization of table names
Configuration:
workspace:
...
---
linter:
...
semantic-rules:
...
# capitalization_policy is one of [consistent, upper, lower, pascal, snake, camel, off]
case-table: null # default (off)
Anti-pattern
Referencing table names in inconsistent capitalization.
SELECT
TableA.*,
table_b.*
FROM TableA JOIN table_b USING(my_col)
Best-practice
SELECT
table_a.*,
table_b.*
FROM table_a JOIN table_b USING(my_col)
-- or
SELECT
TableA.*,
TableB.*
FROM TableA JOIN TableB USING(my_col)
-- or other formats applied to all table names consistently
case-alias
Inconsistent capitalization of alias
Configuration:
workspace:
...
---
linter:
...
semantic-rules:
...
# capitalization_policy is one of [consistent, upper, lower, pascal, snake, camel, off]
case-alias: null # default (off)
Anti-pattern
Referencing alias in inconsistent capitalization.
SELECT
A.*,
b.*
FROM table_a AS A JOIN table_b AS b USING(my_col)
Best-practice
SELECT
a.*,
b.*
FROM table_a AS a JOIN table_b AS b USING(my_col)
-- or
SELECT
A.*,
B.*
FROM table_a AS A JOIN table_b AS B USING(my_col)
-- or other formats applied to all aliases consistently
disallow-implicit-conversions-in
Prevent type implicit casting or coercion
DSF126
Configuration:
workspace:
...
---
linter:
...
semantic-rules:
...
# implicit_conversion_operators is a list of ['eq', 'in']
disallow-implicit-conversions-in: ['eq', 'in'] # default
Anti-pattern Comparing two different types which causes implicit casting and confusing behavior.
SELECT
TRUE = 'TRUE', --> result: TRUE
TRUE = 'true', --> result: TRUE
'TRUE' = TRUE, --> result: FALSE
'true' = TRUE, --> result: TRUE
TRUE = '1', --> result: TRUE
'1' = TRUE, --> result: FALSE
Best-practice Comparison should be made between two values of the same type
Performance Rules
flag-function-application-in-where-on-indexed-columns
Avoid functions in where clauses over indexed, partitioned or clustered columns, to prevent filter push down and improve performance
Configuration:
workspace:
...
---
linter:
...
performance-rules:
...
flag-function-application-in-where-on-indexed-columns: null # default (off)
Anti-pattern Use a function on an indexed column, which can cause a filter push down and hinder performance
SELECT a, b
FROM my_table
WHERE DATE(datetime_partition) >= DATE('2024-01-01')
Best-practice
SELECT a, b
FROM my_table
WHERE datetime_partition >= '2024-01-01'
Check Rules
Include SDF Checks as custom lint rules.
Configuration:
To enable an SDF check (e.g., check name: view_name_must_start_with_v_
) as
part of SDF lint, add the following configuration to your linter:
workspace:
name: example_workspace_name
...
---
linter:
...
check-rules:
...
# {workspace_name}.checks.{check_name}: on
example_workspace_name.checks.view_name_must_start_with_v_: on
Learn how to create custom checks in our SDF Checks guide.