2

次の表を検討してください。

class Employee(Base):
    __tablename__ = "t_employee"

    id = Column(Integer(20), Sequence('%s_id_seq' % __tablename__), primary_key=True)
    first_name = Column(String(30))
    last_name = Column(String(30))
    email = Column(String(50))
    start_date = Column(Date, default=datetime.now)
    end_date = Column(Date)

sqlalchemy の raw SQL で日付の代わりに文字列を使用して選択するにはどうすればよいですか? 以下は mysql では機能しますが、Oracle では機能しません。

session.query(Employee).\
    filter("end_date IS NULL OR end_date>='%s'" % datetime.now()).all()

最良のシナリオは、Date または DateTime 列を処理するときはいつでも文字列または日付 (交換可能) を使用できる場合です (TypeDecorator を試してみましたが役に立ちませんでした)。

質問は生のSQLに言及していることに注意してください(これは述語を使用して実行できることを知っています)...

4

1 に答える 1

8

文字列の書式設定を使用して値を SQL に補間しないでください。オブジェクトの場合datetime、デフォルトの文字列形式がたまたま MySQL で機能しますが、それは偶然と運にすぎません。

この場合、未加工の SQL を使用せず、SQLAlchemy で datetime オブジェクトをバックエンド データベースが理解できるものに変換します。

from sqlalchemy import or_

session.query(Employee).filter(
    or_(Employee.end_date == None, Employee.end_date >= datetime.now())
).all()

生の SQL を使用する場合でも、sqlalchemy でその SQL を生成し、バインド パラメーターを使用します。

from sqlalchemy.sql.expression import bindparam, column
from sqlalchemy.types import DateTime
from sqlalchemy import or_

dtnow = bindparam('dtnow', datetime.now(), DateTime)
end_date = column('enddate', DateTime)

session.query(Employee).\
    filter(or_(end_date == None, end_date >= dtnow)).all()

そのフィルター式は、バックエンドが何であれ、データベース バックエンド用に適切にエスケープされた SQL に変換されます。バックエンドが設定されていない場合、式は次のようになります。

>>> str(or_(end_date == None, end_date >= dtnow))
'enddate IS NULL OR enddate >= :dtnow'

値は、datetime.now()実行時に SQL パラメータとしてバックエンド データベース カーソルに渡されます。

最後の手段はtext()typeを使用することです:

from sqlalchemy.sql.expression import bindparam, text

dtnow = bindparam('dtnow', datetime.now(), DateTime)
session.query(Employee).\
    filter(text('end_date is NULL or end_date >= :dtnow', bindparams=[dtnow])).all()

それ以外の場合は、生の SQL と SQLAlchemy ORM を完全に混在させることは避けます。データベース接続のみで raw SQL を直接使用します。

conn = session.connection()
conn.execute(text('SELECT * FROM t_employee WHERE end_date IS NULL OR end_date>=:dtnow'),
   dtnow=datetime.now())
于 2013-03-18T13:24:08.580 に答える