问题描述
假设我有一个 Transaction
模型,其中包含以下字段 [token,pair_token,amount,related_transaction]
,我想在 MysqL 中生成这样的查询:
SELECT token_id,pair_token_id,(
SELECT ABS(t3.amount / t2.amount) price
FROM
Transaction t2 join Transaction t3 on t2.related_transaction_id=t3.id
WHERE t2.id=MAX(t1.id))
FROM
Transaction t1
WHERE
t1.token_id in (1,2,3,5,6) and t2.pair_token_id in (4) and t1.timestamp > CURRENT_TIMESTAMP - interval 24 hour
GROUP BY
token_id,pair_token_id;
此查询查找两个相关交易的最后一个值,该值等于一个代币及其 pair_token 组合的价格。为了在 django 中获得类似的结果,我使用了 Subquery
如下:
trs = Transaction.objects.filter(token_id__in=[1,3],pair_token_id__in=[4],timestamp__gt=Now()-timedelta(hours=24))
last_price = Transaction.objects.annotate(price=Abs(F('related_transaction__amount') / F('amount')))
trs = trs.values('token_id','pair_token_id').annotate(
price=Subquery(last_price.filter(id=Max(OuterRef('id'))).values('price'))).\
values('token_id','pair_token_id','price')
SELECT
`Transaction`.`token_id`,`Transaction`.`pair_token_id`,(
SELECT ABS((U1.`amount` / U0.`amount`)) AS `price`
FROM
`Transaction` U0 LEFT OUTER JOIN `Transaction` U1 ON (U0.`related_transaction_id` =U1.`id`)
HAVING U0.`id` = MAX(`Transaction`.`id`)) AS `price`
FROM
`Transaction`
WHERE (`Transaction`.`pair_token_id` IN (4) AND `Transaction`.`timestamp` > (CURRENT_TIMESTAMP - INTERVAL 86400000000 MICROSECOND) AND `Transaction`.`token_id` IN (1,6))
MysqL 会为此查询生成错误,而且肯定是这样,我不知道如何避免在使用 having
时生成 Subquery
查询。如果我在没有任何 Subquery
的情况下使用此查询,则会生成 group by 子句,但在使用 Subquery
group by 术语时将被删除并出现 having
。
我正在使用 django 3.1.1
和 MysqL 8.0.19
更新:
交易模型:
class Token:
name = models.CharField(max_length=20)
class Transaction:
token = models.ForeignKey(
Token,blank=False,null=False,on_delete=models.CASCADE
)
pair_token = models.ForeignKey(
Token,blank=True,null=True,on_delete=models.SET_NULL
)
related_transaction = models.ForeignKey(
Transaction,on_delete=models.PROTECT
)
amount = models.DecimalFeild(
max_digits=10,price_decimals=3
)
t1 = Token.objects.create(name='T1')
t2 = Token.objects.create(name='T2')
t3 = Token.objects.create(name='T3')
tr11 = Transaction.objects.create(
token=t1,pair_token=t2,amount=Decimal('2.4')
)
tr12 = Transaction.obejcts.create(
token=t2,pair_token=t1,related_transaction=tr11,amount=Decimal('3')
)
tr21 = Transaction.objects.create(
token=t1,amount=Decimal('1.4')
)
tr22 = Transaction.obejcts.create(
token=t2,related_transaction=tr21,amount=Decimal('3')
)
# If I want to get price of t2 in t1 I must divide tr21.amount / tr22.amount
# Notice that there are many transactions related to each pair and I want to just list of the last price of each pairs.
解决方法
以下应该给你你想要的结果。
首先我们创建一个子查询来计算交易的价格,通过外部令牌/pair_token 进行过滤,然后进行排序,以便最大 id 为第一
latest_prices = Transaction.objects.filter(
related_transaction__isnull=False
).annotate(
price=Abs(F('related_transaction__amount') / F('amount'))
).filter(
token=OuterRef('token'),pair_token=OuterRef('pair_token')
).order_by('-id')
然后使用这个子查询来注释每一行的 token/pair_token 的最新价格,并使用 distinct
获取唯一值
Transaction.objects.filter(
related_transaction__isnull=False
).annotate(
price=Subquery(latest_prices.values('price')[:1])
).values('token_id','pair_token_id','price').distinct()