django - 原始SQL查询兼容性

时间:2013-12-12 15:35:50

标签: sql django sqlite postgresql

我有一个超过80行的原始SQL查询,结果我无法使用查询集重现。我现在想要使这个查询与SQLite和PostgreSQL兼容,因此它可以在开发和生产环境中运行。

是否有任何建议或内置方式可以继续?

实施例

因此,例如,我得到了这个PostgreSQL语法错误:

operator does not exist: boolean = integer

我调整了语法并使用了一些条件来为每个案例获取正确的SQL字符串,但这对我来说感觉不对:

db_engine = ''
engine = settings.DATABASES['default']['ENGINE']
if re.match('.*sqlite3$', engine):
    db_engine = 'sqlite3'
elif re.match('.*psycopg2$', engine):
    db_engine = 'postgresql'
elif re.match('.*mysql$', engine):
    db_engine = 'mysql'

cursor = connection.cursor()
cursor.execute("""
    ... some big query here ...
    WHERE 1=1
        AND some_boolean_field = %(value)s
""" % {
    'value': 'TRUE' if db_engine == 'postgresql' else '1'
})

1 个答案:

答案 0 :(得分:2)

SQLite doesn't have boolean。因此,如果您想要在两个数据库中都能正常运行的查询,我建议您在编写原始SQL时避免使用boolean类型。

boolean实际上并没有给你带来任何好处,除非你有很多布尔字段打包在一起,因为它们每个都已经有一个字节,并且由于对齐原因而倾向于填充到一个字。

观察原始类型尺寸:

regress=> SELECT pg_column_size(TRUE), pg_column_size(SMALLINT '1');
 pg_column_size | pg_column_size 
----------------+----------------
              1 |              2
(1 row)

并且在考虑行标题之后存在相当小的差异:

regress=> SELECT 
             pg_column_size( ROW(SMALLINT '1', SMALLINT '1', SMALLINT '0', SMALLINT '1') ),
             pg_column_size( ROW(true, true, false, true) );
 pg_column_size | pg_column_size 
----------------+----------------
             32 |             28
(1 row)

这是四个连续布尔字段的最佳值。实际上,对齐限制往往意味着你大多数时候也可能只使用一小部分。

如果你想使用boolean然后与SQLite一起玩,你需要将所有内容都转换成整数。这将是SQLite上的无操作(因此它将在那里工作)并且在PostgreSQL上将boolean转换为int。

 AND CAST(some_boolean_field AS integer) = CAST (%(value)s AS integer)

这可以让你摆脱PostgreSQL布尔的特殊情况;只需传递1表示true / 0表示false并使用:

 AND CAST(some_boolean_field AS integer) = %(value)s

....或者,当然,您可以在开发和生产中使用相同的引擎测试PostgreSQL。这样,当你推动改变时,你不会得到令人讨厌的惊喜。我写了一些关于调整PostgreSQL安装的方法,用于抛弃测试in this earlier post