Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 炼金术比较两个相关表格的两个日期_Python_Date_Sqlalchemy - Fatal编程技术网

Python 炼金术比较两个相关表格的两个日期

Python 炼金术比较两个相关表格的两个日期,python,date,sqlalchemy,Python,Date,Sqlalchemy,我有两个相关的表,User和UserDownload,现在我想过滤掉它创建的UserDownload,它大于用户创建的UserDownload加上一天,所以python代码是: result=db.session.query(UserDownload.uid).join(User,UserDownload.uid==User.id).filter(UserDownload.created\u at>=User.created\u at+timedelta(days=1)).all() 它的逻辑

我有两个相关的表,User和UserDownload,现在我想过滤掉它创建的UserDownload,它大于用户创建的UserDownload加上一天,所以python代码是:

result=db.session.query(UserDownload.uid).join(User,UserDownload.uid==User.id).filter(UserDownload.created\u at>=User.created\u at+timedelta(days=1)).all()

它的逻辑似乎正确,但结果很奇怪,有些结果,用户加上一天的创建时间比用户下载的创建时间少,但有些不是。我检查的原始sql查询字符串是:

选择user\u downloads.uid作为user\u downloads\n从user\u downloads加入user\u downloads.uid=users.id\n此处user\u downloads.created\u at>=users.created\u at+:created\u at\u 1

真的不知道:在1处创建意味着什么

例如,结果包含这样一个用户下载(我将UserDownload.uid替换为UserDownload,并通过其uid查询用户):


用户下载创建时间:datetime.datetime(2015,12,3,8,39,56)
user.created_于:datetime.datetime(2015,12,2,11,7,14)

根据您使用的后端,您需要您的过滤器来生成sql-like(用于mysql):

SQLAlchemy不会将
datetime
对象和
InstrumentedAttribute
对象之间的算术转换为
DATE\u ADD
(或根据后端而定的等效)函数。因此,您可以通过以下方式进行过滤:

UserDownload.created_at >= User.created_at + timedelta(days=1)
它被转换为:

...WHERE user_downloads.created_at >= users.created_at + :created_at_1
其中,它将
timedelta(days=1)
视为文本值,并对其进行参数化。这就是在_1处创建的
:是一个参数,该参数在查询中保留
timedelta
对象的位置,该对象将随查询一起传递给服务器(作为旁注,在MySQL中,
timedelta
实际上被转换成一个
datetime
对象,即epoch+1天,因为MySQL没有本机的
INTERVAL
类型)

因此,要让查询执行所需操作,需要使用
sqlalchemy.func
对象生成所需的服务器端函数。继续MySQL示例,合适的查询可能是:

from sqlalchemy import func, text

q = session.query(UserDownload.uid).\
    join(User, UserDownload.uid == User.id).\
    filter(
        UserDownload.created_at >=
        func.DATE_ADD(
            User.created_at,
            text('INTERVAL 1 DAY')
        )
    )
由此产生:

SELECT user_downloads.uid
FROM user_downloads INNER JOIN users ON user_downloads.uid = users.id
WHERE user_downloads.created_at >= DATE_ADD(users.created_at, INTERVAL 1 DAY)
我发现这个问题很有帮助:

来自评论

既然mysql可以处理timedelta(days=1)参数,为什么我要使用这个查询 失败了

好的,我将尝试更详细地介绍您的原始查询,但在我进行计算时,请给我一些自由度。让我们先忘掉
timedelta
,看看生成的sql是什么。因此:

session.query(UserDownload.uid).join(User, UserDownload.uid == User.id).filter(UserDownload.created_at >= User.created_at)
生成以下sql:

SELECT user_downloads.uid AS user_downloads_uid
FROM user_downloads INNER JOIN users ON user_downloads.uid = users.id
WHERE user_downloads.created_at >= users.created_at
这里不难理解。现在,当我们在
timedelta
中重新添加时,生成的sql完全相同,只是我们在
用户上添加了一个值。在
处创建,由绑定参数
在1处创建表示:

SELECT user_downloads.uid AS user_downloads_uid
FROM user_downloads INNER JOIN users ON user_downloads.uid = users.id
WHERE user_downloads.created_at >= users.created_at + %(created_at_1)s
那么,是先执行比较,还是先执行加法?运行此查询

print(engine.execute(text("SELECT 3 >= 2 + 2")).fetchall())
…返回
0
(即
False
),证明加法(
2+2
)在比较(
=
)之前已解决。因此,可以安全地假设在您的查询中也会发生这种情况,并由此将
created_at_1
的值添加到
用户。在与
user\u下载进行比较之前,created_at
。当我执行查询时,这是传递给服务器的参数值:

{'created_at_1': datetime.datetime(1970, 1, 2, 0, 0)}
因此,即使您向
用户添加
timedelta(days=1)
。在过滤器中创建时,SQLAlchemy实际上会传递一个
datetime
对象,相当于
+
,或者在本例中:
datetime(1970,1,1,0,0)+timedelta(days=1)
datetime(1970,1,2,0,0)
(这是您在上面的参数值字典中看到的值)

那么,
user.created_at+datetime(1970,1,2,0,0)
的值到底是多少呢?我用
created_at=datetime(1970,1,1,0,0)
向数据库中添加了一个
user
实例:

然后运行此查询:

engine.execute(text("SELECT created_at + :a FROM users"), a=datetime(1970, 1, 2, 0, 0)).fetchall()
它返回:

[(19700101001970.0,)]
这是
用户的值。在
处创建,没有任何格式,日期时间(1970,1,2,0,0)的年份部分连接到末尾。因此,这就是您的查询与
用户下载进行比较的地方。在
处创建是为了(相对)简而言之,我不打算研究比较
user\u download.created\u at
和该值是如何工作的,但希望我已经证明,您查询的最终结果不是比较
user\u download.created\u at
user.created\u at
加1天

当我使用诸如:User.query.filter(User.created\u at> datetime.now()+timedelta(days=1)),工作正常

使用该查询,生成的sql如下:

SELECT users.id AS users_id, users.created_at AS users_created_at
FROM users
WHERE users.created_at > %(created_at_1)s
传递给服务器的值为:

{'created_at_1': datetime.datetime(2018, 11, 21, 21, 38, 51, 670890)}

因此,您可以看到,
datetime+timedelta
部分在被传递到数据库之前已被解析,因此,数据库操作是将
datetime
列与
datetime
值进行简单比较。

您使用的是什么数据库?@supershot我使用Mysql作为数据库,sqlalchemy作为flask.SQ中的ormLAlchemy==1.0.12谢谢,这很有帮助。但是我仍然有一个问题,因为mysql可以处理timedelta(days=1)参数,为什么我使用的查询会失败。顺便说一句,当我使用诸如:User.query.filter(User.created_at>datetime.now()+timedelta(days=1))这样的查询时,很好。这是因为你不能在python对象和数据库中的列之间添加日期吗?我添加到上面的答案是为了尝试解决你的额外问题。顺便说一句,19700101001970.0对我来说是原始日期加上19min和70秒。当我将原始数据迁移到mongo并通过聚合查询它时,它看起来就像原始查询筛选出用户下载量大于pl处用户创建的下载量
SELECT users.id AS users_id, users.created_at AS users_created_at
FROM users
WHERE users.created_at > %(created_at_1)s
{'created_at_1': datetime.datetime(2018, 11, 21, 21, 38, 51, 670890)}