在Python和SQLite中使用反引号(`)或双引号(")
我在Stack Overflow上看到一个关于Android的类似问题,但我想知道在使用Python时,选择表名或行ID时应该用反引号(`)还是双引号(")。
我试过用单引号,比如这样 select 'rowid', * from 'tbl' order by 'rowid'
。单引号在某些情况下有效,但并不是所有情况都能用。我了解到可以使用双引号或反引号,然后我查看了SQLite数据库浏览器,发现它使用了反引号。
我喜欢在Python中用双引号包围我的字符串,因为我之前是用Java的,所以用 cursor.execute("select 'rowid',* from 'table';")
这种方式对我来说很自然,使用反引号也一样简单(不过双引号需要用反斜杠转义,这样查询看起来会有点复杂)。
3 个回答
SQLite会把"mycol"
当作字符串处理,如果mycol不存在,这和反引号的处理方式不同
所以,如果你在查询时拼写错误,列名不对,SQLite会把它当作字符串,而不是给出错误提示,这样的处理方式让人觉得不太合理,而PostgreSQL会给出错误提示。不过,使用反引号会直接报错。
这种更好的错误检查让反引号在SQLite中比双引号更有优势,但缺点是它不符合标准SQL。
举个例子:
tmp.sql
CREATE TABLE "IntegerNames" ( value INTEGER NOT NULL, name TEXT NOT NULL );
INSERT INTO "IntegerNames" VALUES
(2, 'two'),
(3, 'three'),
(5, 'five')
;
SELECT '"value"';
SELECT * FROM "IntegerNames" WHERE "value" = 2;
SELECT '"valuee"';
SELECT * FROM "IntegerNames" WHERE "valuee" = 2;
SELECT '`value`';
SELECT * FROM "IntegerNames" WHERE `value` = 2;
SELECT '`valuee`';
SELECT * FROM "IntegerNames" WHERE `valuee` = 2;
SELECT 'value';
SELECT * FROM "IntegerNames" WHERE value = 2;
SELECT 'valuee';
SELECT * FROM "IntegerNames" WHERE valuee = 2;
运行:
rm -f tmp.sqlite && sqlite3 tmp.sqlite <tmp.sql
结果:
"value"
2|two
"valuee"
`value`
2|two
`valuee`
Error: near line 14: in prepare, no such column: valuee (1)
value
2|two
valuee
Error: near line 18: in prepare, no such column: valuee (1)
我们看到,当使用"valuee"
时,没有错误:它只是被当作字符串'value'
处理。然后SQLite会隐式地进行类型转换,'valuee'
与任何整数值都不相等,所以返回结果是空的。
在这个查询中,反引号的行为和未加引号的value
和valuee
是一样的,因为这不是一个关键字。不过需要注意的是,为了符合SQL标准,我认为你必须对包含大写字母的行使用反引号(至少在PostgreSQL中是这样),所以它们并不总是等价的,即使不考虑关键字的情况。
在SQLite 3.37.2和Ubuntu 22.04上测试过。
这种糟糕的行为在这里有详细说明:https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted,开发者们自己也承认这是个坏主意,最初是为了兼容MySQL。现在他们已经学会更频繁地参考PostgreSQL了 ;-)
8. 接受双引号字符串字面量
SQL标准要求标识符用双引号括起来,字符串字面量用单引号括起来。例如:
- "这是一个合法的SQL列名"
- '这是一个SQL字符串字面量'
SQLite接受以上两种写法。但是,为了与MySQL 3.x(在SQLite最初设计时是最广泛使用的关系数据库管理系统之一)兼容,SQLite也会把不匹配任何有效标识符的双引号字符串当作字符串字面量处理。这个错误的特性意味着拼写错误的双引号标识符会被当作字符串字面量,而不是生成错误。这也让刚接触SQL的开发者养成了使用双引号字符串字面量的坏习惯,而他们其实应该学习正确使用单引号字符串字面量。
事后看来,我们不应该试图让SQLite接受MySQL 3.x的语法,也不应该允许双引号字符串字面量的存在。不过,有无数应用程序使用了双引号字符串字面量,因此我们继续支持这个功能,以避免破坏旧有的系统。
从SQLite 3.27.0(2019-02-07)开始,使用双引号字符串字面量会在错误日志中发送警告信息。
从SQLite 3.29.0(2019-07-10)开始,可以通过sqlite3_db_config()中的SQLITE_DBCONFIG_DQS_DDL和SQLITE_DBCONFIG_DQS_DML操作在运行时禁用双引号字符串字面量。默认设置可以在编译时通过使用-DSQLITE_DQS=N选项进行更改。建议应用程序开发者在编译时使用-DSQLITE_DQS=0,以默认禁用双引号字符串字面量的错误特性。如果这不可能,那么可以使用如下C代码为单独的数据库连接禁用双引号字符串字面量:
sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DDL, 0, (void*)0); sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DML, 0, (void*)0);
或者,如果默认情况下禁用了双引号字符串字面量,但需要为某些历史数据库连接选择性启用,可以使用与上面相同的C代码,只需将第三个参数从0改为1。
在写SQL时,建议用双引号来引用标识符,比如列名或表名。这是SQL的标准做法。
反引号也可以用,但它们只适用于MySQL的语法。
单引号是用来表示字符串的,不是用来表示标识符的。所以如果你用单引号,你会得到字符串的实际值。
想了解更多,可以查看:SQLite 关键字
SQL标准规定,字符串必须用'单引号'
来表示,而标识符(比如表名和列名)在被引用时必须用"双引号"
。
为了和MySQL兼容,SQLite也允许在某些情况下用单引号表示标识符,用双引号表示字符串,但前提是上下文要清楚,意思不能混淆。(比如在SELECT 'rowid' ...
中,'rowid'是一个字符串,所以这里得到的就是字符串。)如果可以的话,最好还是使用标准的SQL引号。
为了和MySQL兼容,SQLite还允许用`反引号`
来表示标识符。
为了和微软的数据库兼容,SQLite还允许用[方括号]
来表示标识符。
(这些在所有SQLite版本中都适用。)