SQL 中的代码检查
本主题列出了 SQL 中所有可用的 JetBrains Rider 代码检查。
您可以在 编辑器 | 检查设置 | 检查严重性 | 其他语言 设置页面  Ctrl+Alt+S 上切换特定检查或更改其严重性级别。
检查 | 描述 | 默认严重性 |
|---|---|---|
添加无默认值的非空列 | 报告尝试创建没有默认值的 NOT NULL 列。 示例(Microsoft SQL Server): CREATE TABLE foo (a INT, b INT)
ALTER TABLE foo ADD c INT NOT NULL;
默认情况下,列可以包含 NULL 值。 在示例中,我们使用了 NOT NULL 约束,强制列不接受 NULL 值。 如果我们禁止使用 NULL 值,则必须设置 SQL 在创建新记录时可以使用的默认值。 ALTER TABLE foo ADD c INT NOT NULL DEFAULT 42;
您可以通过使用“添加默认值”快速修复功能快速添加默认值。 | |
与聚合相关的问题 | 报告 SQL 聚合函数的无效用法。 考虑以下情况:
| |
模糊引用 | 报告具有相同名称但属于不同表的列。 示例(MySQL): CREATE TABLE foo(id INT PRIMARY KEY);
CREATE TABLE bar(id INT PRIMARY KEY);
SELECT foo.id, bar.id FROM foo, bar WHERE id > 0;
SELECT foo.id, bar.id FROM foo, bar WHERE foo.id > 0;
| |
自动递增重复 | 报告包含两个具有自动递增的列的表。 在 MySQL、Microsoft SQL Server 和 Db2 方言中,一个表只能有一个具有自动递增选项的字段,并且该字段必须是键。 示例(MySQL): CREATE TABLE my_table
(
id INT AUTO_INCREMENT,
c2 INT AUTO_INCREMENT,
);
CREATE TABLE my_table
(
id INT AUTO_INCREMENT PRIMARY KEY,
c2 INT,
);
| |
检查 USING 子句中的列 | 报告 USING 子句中不存在于两个表中的列。 示例(MySQL): CREATE TABLE t1 (i INT, j INT);
CREATE TABLE t2 (k INT, l INT);
SELECT * FROM t1 JOIN t2 USING (j);
在 USING 子句中,列名必须同时存在于两个表中,SELECT 查询将自动使用给定的列名连接这些表。 由于我们在 SELECT * FROM t1 JOIN t2 ON t1.j = t2.l;
| |
列被别名遮蔽 | 报告 SELECT 别名与 FROM 子句中的列名相匹配的情况。 示例(MySQL): CREATE TABLE foo (a INT, b INT, c INT);
SELECT a b, c FROM foo;
| |
列应在 GROUP BY 子句中 | 报告未在 GROUP BY 子句中或未在聚合函数调用中的列。 示例(Microsoft SQL Server): CREATE TABLE t1 (a INT, b INT);
SELECT a, b FROM t1 GROUP BY a;
如果您运行 SELECT 查询,您将收到错误,因为 Microsoft SQL Server 期望 SELECT a, b FROM t1 GROUP BY a, b;
SELECT a, max(b) max_b FROM t1 GROUP BY a;
| |
恒定条件 | 报告 WHERE 或 JOIN 子句中始终为 TRUE 或始终为 FALSE 的条件。 示例(MySQL): CREATE TABLE t1 (a TEXT, b INT, c BOOLEAN);
SELECT a FROM t1 WHERE 'Cat' = 'Cat';
| |
恒定表达式 | 报告始终为 true、false 或 null 的条件和表达式。 示例(MySQL): CREATE TABLE t1 (a TEXT, b INT, c BOOLEAN);
SELECT a FROM t1 WHERE 'Cat' = 'Cat';
SELECT a FROM t1 WHERE 'Cat' = null;
| |
当前控制台模式已内省 | 报告当前会话中未内省的模式和数据库。 例如,当您尝试在未内省的模式中创建表时,可能会出现此警告。 内省是一种检查数据源的方法。 当您执行内省时,会检查数据源中的结构信息,以检测表、列、函数及其属性等元素。 | |
没有 WHERE 子句的删除或更新语句 | 报告没有 WHERE 子句的 DELETE 或 UPDATE 语句的用法。 没有 WHERE 子句,DELETE 会删除表中的所有数据,而 UPDATE 会覆盖表中所有行的值。 示例(MySQL): CREATE TABLE t1 (a TEXT, b INT, c BOOLEAN);
update t1 set a = 'Smith';
delete from t1;
| |
已弃用的类型 | 报告已弃用并可能在未来版本的 DBMS 中消失的类型的用法。 报告的类型:
示例(Oracle): CREATE TABLE ot.foo(
a NUMBER GENERATED BY DEFAULT AS IDENTITY,
b LONG NOT NULL
);
| |
在 SELECT 中重复的列名 | 报告 SELECT 列表中列别名的重复名称。 示例(Sybase ASE): CREATE TABLE t1 (a TEXT, b INT, c INT);
SELECT a AS x, b AS x FROM t1;
| |
每个派生表都应有别名 | 报告没有别名的派生表。 示例(MySQL): CREATE TABLE table1 (id INT, name VARCHAR(20), cats FLOAT);
CREATE TABLE table2 (id INT, age INTEGER);
SELECT id AS ID, name, cats, age
FROM (SELECT table1.id, name, cats, age
FROM table1
JOIN table2 ON table1.id = table2.id);
根据 Derived Tables at dev.mysql.com ,别名是强制性的。 您可以通过使用“引入别名”快速修复功能添加别名。 应用快速修复后: SELECT id AS ID, name, cats, age
FROM (SELECT table1.id, name, cats, age
FROM table1
JOIN table2 ON table1.id = table2.id);
| |
函数签名 | 报告内置函数的签名问题。 检查将报告参数数量错误、无效关键字、错误的数据类型以及其他问题。 示例(MySQL): CREATE TABLE foo (a INT, b INT, c INT)
SELECT IFNULL() FROM foo; -- error
SELECT IFNULL(a) FROM foo; -- error
SELECT IFNULL(a, b) FROM foo; -- OK
SELECT IFNULL(a, b, c) FROM foo; -- error
在 MySQL 中, | |
标识符应加引号 | 报告在查询中使用 SQL 保留关键字作为标识符名称的情况。 示例(Microsoft SQL Server): CREATE TABLE select (identity INT IDENTITY NOT NULL, order INT NOT NULL);
我们使用 应用快速修复后: CREATE TABLE [select] ([identity] INT IDENTITY NOT NULL, [order] INT NOT NULL);
| |
格式错误的日期/时间字面量 | 报告日期和时间字面量中的错误。 此检查在 MySQL、Oracle、Db2 和 H2 中可用。 示例(MySQL): SELECT TIME '10 -12:13:14' FROM dual;
SELECT TIME ' 12 : 13 : 14 ' FROM dual;
SELECT TIME '12 13 14' FROM dual;
SELECT TIME '12-13-14' FROM dual;
SELECT TIME '12.13.14' FROM dual;
SELECT TIME '12:13:' FROM dual;
SELECT TIME '12:13' FROM dual;
SELECT TIME '12:' FROM dual;
在此示例中,日期忽略了 MySQL 对日期和时间字面量的标准。 因此,它们将被高亮显示。 有关 MySQL 中日期和时间字面量的更多信息,请参阅 Date and Time Literals at dev.mysql.com。 以下日期和类型字面量对 MySQL 是有效的。 SELECT TIME '12:13:14' FROM dual;
SELECT TIME '12:13:14.555' FROM dual;
SELECT TIME '12:13:14.' FROM dual;
SELECT TIME '-12:13:14' FROM dual;
SELECT TIME '10 12:13:14' FROM dual;
SELECT TIME '-10 12:13:14' FROM dual;
| |
非法游标状态 | 报告 SQL 例程中非法的游标状态。
示例(Microsoft SQL Server): CREATE TABLE t(col INT);
CREATE PROCEDURE foo() AS
BEGIN
DECLARE my_cursor CURSOR FOR SELECT * FROM t;
DECLARE a INT;
FETCH my_cursor INTO a;
CLOSE my_cursor;
END;
根据 CLOSE (Transact-SQL) at docs.microsoft.com ,CLOSE 必须在打开的游标上执行,且不允许在仅声明或已关闭的游标上执行 CLOSE。 因此,我们需要打开游标以修复此警告。 CREATE PROCEDURE foo() AS
BEGIN
DECLARE my_cursor CURSOR FOR SELECT * FROM t;
DECLARE a INT;
OPEN my_cursor;
FETCH my_cursor INTO a;
CLOSE my_cursor;
END;
| |
隐式字符串截断 | 报告超出定义字符长度的变量。 示例(Microsoft SQL Server): CREATE PROCEDURE test() AS
BEGIN
DECLARE myVarOk VARCHAR(5) = 'abcde';
DECLARE myVarExceeded VARCHAR(5) = 'abcde12345';
SET myVarOk = 'xyz';
SET myVarExceeded = '123456789';
END;
应用快速修复后: CREATE PROCEDURE test() AS
BEGIN
DECLARE myVarOk VARCHAR(5) = 'abcde';
DECLARE myVarExceeded VARCHAR(10) = 'abcde12345';
SET myVarOk = 'xyz';
SET myVarExceeded = '123456789';
END;
| |
索引依赖于列 | 报告尝试从索引表中删除列的情况。 此检查在 Microsoft SQL Server 和 Sybase ASE 中可用。 示例(Microsoft SQL Server): CREATE TABLE test_index
(
col INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL UNIQUE,
col4 VARCHAR(200)
);
CREATE UNIQUE INDEX aaaa ON test_index (col, col2);
ALTER TABLE test_index
DROP COLUMN col;
您无法删除 | |
向 NOT NULL 列插入 NULL | 报告将 NULL 值插入仅接受 NOT NULL 值的列的情况。 示例(Microsoft SQL Server): CREATE TABLE br2 (
id INT NOT NULL,
col1 NVARCHAR (20) NOT NULL,
col2 NVARCHAR (20) NOT NULL,
);
--
INSERT INTO br2 (id, col1, col2)
VALUES (1, NULL, NULL);
您不能在 INSERT INTO br2 (id, col1, col2)
VALUES (1, 42, 'bird');
| |
插入生成列 | 报告将值分配给生成列的 INSERT 语句。 生成列可以读取,但其值不能直接写入。 示例(PostgreSQL): CREATE TABLE foo
(
col1 INT,
col2 INT GENERATED ALWAYS AS (col1 + 1) STORED
);
INSERT INTO foo(col1, col2) VALUES (1, 2);
您不能将
| |
误导性引用 | 报告 SQL 代码中的模糊引用。 例如,当一个名称同时引用表列和例程参数时。 此类代码的执行可能会由于不直观的解析逻辑导致错误或意外结果。 通常,具有更局部范围的名称优先级更高。 示例(PostgreSQL): CREATE TABLE foo
(
id INT,
name VARCHAR(5)
);
CREATE FUNCTION func(name VARCHAR(5)) RETURNS INT AS
$$
DECLARE
b INT;
BEGIN
-- `name` is ambiguous as it is used as a column name and a parameter
SELECT COUNT(*) INTO b FROM foo t WHERE t.name = name;
RETURN b;
END;
$$ LANGUAGE plpgsql;
在 PostgreSQL 中,您可以使用 CREATE TABLE foo
(
id INT,
name VARCHAR(5)
);
CREATE FUNCTION func(name VARCHAR(5)) RETURNS INT AS
$$
#variable_conflict use_column
DECLARE
b INT;
BEGIN
SELECT COUNT(*) INTO b FROM foo t WHERE t.name = name;
RETURN b;
END;
$$ LANGUAGE plpgsql;
| |
缺少列别名 | 报告输出表达式(例如 SELECT 语句)中没有显式别名的查询。 示例(PostgreSQL): CREATE TABLE foo(a INT, b INT);
SELECT 1, a + 1 AS A2, MAX(b) AS M
FROM foo;
| |
缺少返回语句 | 报告没有 RETURN 语句的函数。 示例(Oracle): CREATE FUNCTION foo RETURN int AS
BEGIN
END;
CREATE FUNCTION foo RETURN int AS
BEGIN
RETURN 1;
END;
| |
查询中的多行限制/偏移子句 | 报告单个查询中使用多个行限制子句的情况。 示例(Microsoft SQL Server): create table foo(a int);
select top 1 * from foo order by a offset 10 rows fetch next 20 rows only;
SELECT TOP 子句用于指定仅返回 1 条记录。 FETCH 子句指定在处理 OFFSET 子句后要返回的行数。 但由于我们已经有了 SELECT TOP 限制子句,FETCH 子句可能是多余的。 | |
应使用命名参数 | 报告在例程调用中未使用名称的参数。 默认情况下,此检查被禁用。 有关命名参数和未命名参数之间差异的更多信息,请参阅 Binding Parameters by Name (Named Parameters) at docs.microsoft.com。 示例(Microsoft SQL Server): CREATE FUNCTION foo(n INT, m INT) RETURNS INT AS
BEGIN
RETURN n + m;
END;
CREATE PROCEDURE test AS
BEGIN
foo n = 1, m = 2;
--- The following call misses parameter names and will be highlighted
foo 1, 2;
END;
| |
未配置数据源 | 报告数据库工具窗口(视图 | 工具窗口 | 数据库)中缺少数据源的情况。 | |
NULL 比较 | 报告可以替换为 IS NULL 或 IS NOT NULL 运算符的 NULL 比较。 示例(Microsoft SQL Server): CREATE TABLE foo ( id int );
SELECT * FROM foo WHERE NULL = NULL;
SELECT * FROM foo WHERE NULL != NULL;
SELECT * FROM foo WHERE NULL IS NULL;
SELECT * FROM foo WHERE NULL IS NOT NULL;
| |
多余的 ELSE NULL 子句 | 报告多余的 ELSE NULL 子句。 示例(MySQL): SELECT CASE WHEN 2 > 1 THEN 'OK' ELSE NULL END AS alias FROM foo;
| |
多余的别名表达式 | 报告重复表中列名称的别名表达式,这些表达式可能是多余的。 示例(PostgreSQL): CREATE TABLE foo(a INT, b INT);
SELECT * FROM foo foo(a, b);
SELECT * FROM foo foo(a);
SELECT * FROM foo foo(x);
SELECT * FROM foo foo(x, y);
前两个别名使用了与 | |
COALESCE 调用中的多余代码 | 报告 COALESCE 函数中除第一个表达式外的所有不为 NULL 的参数。 示例(MySQL): SELECT COALESCE(NULL, NULL, NULL, 42, NULL, 'string') as a;
第一个 NOT NULL 参数是 | |
多余的排序方向 | 报告 ORDER BY 子句中多余的排序方向,如 ASC 和 DESC。 示例(MySQL): CREATE TABLE foo(a INT, b INT, c INT);
SELECT * FROM foo ORDER BY a ASC, b DESC, c ASC;
ORDER BY 关键字默认按升序排序记录。 因此, | |
查询中的多余行限制 | 报告查询中多余的行限制子句,如 FETCH 和 LIMIT。 示例(PostgreSQL): CREATE TABLE foo(a INT);
SELECT * FROM foo WHERE EXISTS(SELECT * FROM foo LIMIT 2);
SELECT * FROM foo WHERE EXISTS(SELECT * FROM foo FETCH FIRST 2 ROWS ONLY);
要修复此警告,您可以向限制子句添加 OFFSET。 如果缺少 OFFSET,则 LIMIT 是多余的,因为 LIMIT 的使用不会影响 EXISTS 的操作结果。 在有 OFFSET 的情况下,我们跳过前 SELECT * FROM foo WHERE EXISTS(SELECT * FROM foo OFFSET 1 ROW LIMIT 2);
SELECT * FROM foo WHERE EXISTS(SELECT * FROM foo OFFSET 1 ROW FETCH FIRST 2 ROWS ONLY);
| |
SQL 方言检测 | 报告未为 SQL 文件分配方言的情况。 例如,当您打开一个新的 SQL 文件而未为其分配方言时,您会看到一条通知,建议使用最匹配的方言。 单击“使用 <dialect>”链接以使用建议的方言。 或者,单击“更改方言为”链接以选择其他方言。 | |
SQL 源代码修改检测 | 报告数据库对象的源代码已被更改的情况。 当您执行数据库或对象内省时,将触发此检查。 当您打开对象的源代码、运行语句并执行代码重构时,会运行内省。 此外,您可以通过右键单击对象并选择“刷新”来运行内省。 此检查涵盖以下情况:
| |
具有副作用的语句 | 报告在只读连接期间可能导致数据库修改的语句。 要为连接启用只读模式,请右键单击数据库工具窗口(视图 | 工具窗口 | 数据库)中的数据源,然后选择属性。 在数据源和驱动程序对话框中,单击选项选项卡并选择只读复选框。 示例(MySQL): CREATE TABLE foo(a INT);
INSERT INTO foo VALUES (1);
由于 | |
触发器中的可疑代码 | 报告触发器中过渡表变量的错误用法。 示例(HSQLDB): CREATE TABLE foo(a INT);
CREATE TRIGGER trg
AFTER DELETE ON foo
BEGIN
SELECT * FROM NEW;
END;
CREATE TRIGGER trig AFTER INSERT ON foo
REFERENCING OLD ROW AS newrow
FOR EACH ROW WHEN (a > 1)
INSERT INTO foo VALUES (1)
在 HSQLDB 中,DELETE 触发器只能与 OLD 状态一起使用,而 INSERT 触发器只能具有 NEW 状态。 因此,在前面的示例中, | |
类型兼容性 | 报告与类型相关的错误。 | |
SQL 中的 Unicode 使用 | 报告未使用 如果没有 N 前缀,字符串将被转换为数据库的默认代码页。 此默认代码页可能无法识别某些字符。 有关更多信息,请参阅 nchar 和 nvarchar (Transact-SQL) at docs.microsoft.com。 示例(Microsoft SQL Server): SELECT 'abcde' AS a;
SELECT N'abcde' AS b;
SELECT 'абвгд' AS c;
SELECT N'абвгд' AS d;
| |
不可达代码 | 报告 SQL 例程中不可达的语句。 示例(Microsoft SQL Server): CREATE FUNCTION foo() RETURNS INT AS
BEGIN
THROW;
RETURN 1;
END;
在 Microsoft SQL Server 中, | |
未解析的引用 | 报告未解析的 SQL 引用。 示例(MySQL): CREATE TABLE users(id INT, name VARCHAR(40));
CREATE TABLE admins(id INT, col1 INT);
SELECT users.id, admins.id FROM admins WHERE admins.id > 1;
| |
'delete' 语句中的不安全 'join' 子句 | 报告可能修改整个数据库的语句中缺少条件检查的情况。 例如,在 DELETE 语句中使用没有 ON 或 WHERE 的 JOIN 子句。 如果 JOIN 缺少条件检查,DELETE 将删除整个表的内容。 示例(MySQL): CREATE TABLE foo (a INT,b INT,c INT);
CREATE TABLE bar (a INT,b INT,c INT);
DELETE table1 FROM foo table1 INNER JOIN bar table2;
| |
未使用的公共表表达式 | 报告查询中未使用的公共表表达式(CTE)。 示例(PostgreSQL): CREATE TABLE foo(a INT);
WITH a AS (SELECT 1 AS x FROM foo)
SELECT 1 + 2 FROM foo;
通过使用 WITH,我们创建了一个名为 | |
未使用的子查询项 | 报告未在外部查询表达式中引用的列、别名和其他子查询项。 示例(PostgreSQL): CREATE TABLE for_subquery(id INT);
SELECT a, q FROM (SELECT 1 AS a, 10 AS b, 2 + 3 AS q, id
FROM for_subquery) x;
我们从子查询中引用了 | |
未使用的变量 | 报告未使用的参数、变量或参数。 示例(PostgreSQL): CREATE FUNCTION foo(PARAMUSED INT, PARAMUNUSED INT) RETURNS INT AS
$$
BEGIN
RETURN PARAMUSED;
END
$$ LANGUAGE plpgsql;
| |
GOTO 语句的用法 | 报告向后 GOTO 语句和用于退出循环的 GOTO 语句的用法。 通常不推荐广泛使用 GOTO 语句。 有关更多信息,请参阅 GOTO statement in SQL procedures at ibm.com。 建议使用循环代替使用 GOTO 跳回到前一语句。 建议使用其他控制流语句(例如 RETURN 或 BREAK)代替使用 GOTO 退出 WHILE 循环。 示例(Oracle): CREATE PROCEDURE test(n INT) AS
DECLARE
x INT;
BEGIN
x := 0;
GOTO a;
<<a>> x := 1;
IF (n = 0) THEN
GOTO a;
END IF;
WHILE TRUE
LOOP
GOTO b;
END LOOP;
<<b>> x := 3;
END;
| |
在触发器中使用事务管理语句 | 报告在触发器主体中使用事务管理语句(如 COMMIT 或 ROLLBACK)。 如果在触发器主体中使用 COMMIT 或 ROLLBACK 语句,触发器将无法编译。 失败的原因是触发器在事务期间启动。 当触发器启动时,当前事务尚未完成。 由于 COMMIT 终止事务,COMMIT 和 ROLLBACK 语句都会导致异常。 在触发器中执行的更改应由启动触发器的所属事务提交(或回滚)。 示例(Oracle): CREATE TABLE employee_audit
(
id INT NOT NULL,
update_date DATE NOT NULL,
old_name VARCHAR2(100),
new_name VARCHAR2(100)
);
CREATE TABLE employees
(
id INT NOT NULL,
name VARCHAR2(100) NOT NULL
);
CREATE OR REPLACE TRIGGER trig_commit
AFTER UPDATE OF name
ON employees
FOR EACH ROW
BEGIN
INSERT INTO employee_audit VALUES (:old.id, SYSDATE, :old.name, :new.name);
COMMIT;
END;
CREATE OR REPLACE TRIGGER trig_rollback
AFTER UPDATE OF name
ON employees
FOR EACH ROW
BEGIN
INSERT INTO employee_audit VALUES (:old.id, SYSDATE, :old.name, :new.name);
ROLLBACK;
END;
| |
使用 CASE 替代 COALESCE 函数及反之亦然 | 报告 CASE 和 COALESCE 调用可以互换的情况。 此检查具有以下意图操作:用 'COALESCE' 调用替换,或反之用 CASE 表达式替换。 示例(MySQL): SELECT
-- this CASE may be replaced by COALESCE
CASE
WHEN C1 IS NOT NULL THEN C1
ELSE 0
END
FROM dual;
在示例中,CASE 语句可以替换为 如果您更喜欢使用 CASE 表达式,请在检查页面选择“优先使用 CASE 表达式而非 COALESCE 函数”选项。 | |
使用 CASE 替代条件函数及反之亦然 | 报告 CASE 和 IF 可以互换的情况。 示例(MySQL): SELECT CASE
WHEN C1 IS NULL THEN 1
ELSE 0
END
FROM dual;
为了使代码更简洁,您可以将 CASE 结构替换为 IF。 您可以通过应用“用 'IF' 调用替换”意图操作来实现。 示例代码将如下所示: SELECT IF(C1 IS NULL, 1, 0)
FROM dual;
要将 IF 恢复为 CASE,请单击 IF 并应用“用 CASE 表达式替换”意图操作。 | |
使用命名和位置参数 | 报告位置参数在命名参数之后的调用。 适用于 PostgreSQL、Oracle 和 Db2。 示例(PostgreSQL 中): CREATE FUNCTION foo(a int, b int, c int) RETURNS int
LANGUAGE plpgsql AS
$$
BEGIN
RETURN a + b + c;
END
$$;
SELECT foo(a => 1, b => 2, c => 3);
-- `3` goes after the named argument
SELECT foo(1, b => 2, 3);
-- `1` and `3` go after the named argument
SELECT foo(b => 2, 1, 3);
| |
VALUES 子句基数 | 报告 VALUES 中的参数数量与目标表中的列数量不匹配的情况。 示例(MySQL): CREATE TABLE foo(a INT, b INT, c INT);
INSERT INTO foo VALUES (1,2,3,4)
|