You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

获取用户各token最新Qty_holding:SQL及Django查询修正求助

问题描述

我需要从userholding表中查询每个用户对应每个token的最新Qty_holding值,表结构及数据如下:

+----+-------------+--------------+----------------------------+------------+--------+
| id | Qty_holding | Qty_reserved | created                    | tokenid_id | uid_id |
+----+-------------+--------------+----------------------------+------------+--------+
| 1  | 10          | 0            | 2018-01-18 10:52:14.957027 | 1          | 1      |
| 2  | 20          | 0            | 2018-01-18 11:20:08.205006 | 8          | 1      |
| 3  | 110         | 0            | 2018-01-18 11:20:21.496318 | 14         | 1      |
| 4  | 10          | 0            | 2018-01-23 14:26:49.124607 | 1          | 2      |
| 5  | 3           | 0            | 2018-01-23 15:00:26.876623 | 11         | 2      |
| 6  | 7           | 0            | 2018-01-23 15:08:41.887240 | 11         | 2      |
| 7  | 11          | 0            | 2018-01-23 15:22:48.424224 | 11         | 2      |
| 8  | 15          | 0            | 2018-01-23 15:24:03.419907 | 11         | 2      |
| 9  | 19          | 0            | 2018-01-23 15:24:26.531141 | 11         | 2      |
| 10 | 23          | 0            | 2018-01-23 15:27:11.549538 | 11         | 2      |
| 11 | 27          | 0            | 2018-01-23 15:27:24.162944 | 11         | 2      |
| 12 | 7.7909428   | 0.11459088   | 2018-01-23 15:27:24.168643 | 1          | 2      |
| 13 | 3           | 0            | 2018-01-23 15:36:51.412340 | 14         | 2      |
| 14 | 7.5585988   | 0.11459088   | 2018-01-23 15:36:51.417177 | 1          | 2      |
| 15 | 6           | 0            | 2018-01-24 08:43:46.635069 | 14         | 2      |
| 16 | 7.3262548   | 0.11459088   | 2018-01-24 08:43:46.639984 | 1          | 2      |
| 17 | 9           | 0            | 2018-01-24 10:09:08.207816 | 14         | 2      |
| 18 | 7.0939108   | 0.11459088   | 2018-01-24 10:09:08.212842 | 1          | 2      |
| 19 | 6           | 3            | 2018-01-24 13:43:08.929586 | 14         | 2      |
| 20 | 3           | 6            | 2018-01-24 14:49:56.960112 | 14         | 2      |
| 21 | 0           | 9            | 2018-01-24 14:50:33.423671 | 14         | 2      |
| 22 | 30          | 9            | 2018-01-24 14:51:14.865453 | 14         | 2      |
| 23 | 4.7704708   | 0.11459088   | 2018-01-24 14:51:14.870256 | 1          | 2      |
| 24 | 27          | 12           | 2018-01-24 14:56:56.914009 | 14         | 2      |
| 25 | 24          | 15           | 2018-01-24 14:57:56.475939 | 14         | 2      |
| 26 | 21          | 15           | 2018-01-24 14:58:06.750903 | 14         | 2      |
| 27 | 18          | 15           | 2018-01-24 15:02:43.203878 | 14         | 2      |
| 28 | 4.7705074   | 0.11459088   | 2018-01-24 15:02:43.224901 | 1          | 2      |
| 29 | 24          | 0            | 2018-01-24 15:03:40.421943 | 11         | 2      |
| 30 | 4.9535074   | 0.11459088   | 2018-01-24 15:03:40.441552 | 1          | 2      |
| 31 | 1           | 0            | 2018-01-26 10:35:33.173801 | 18         | 2      |
+----+-------------+--------------+----------------------------+------------+--------+

我最初使用的MySQL语句:

select uid_id,tokenid_id,Qty_holding from accounts_userholding group by uid_id,tokenid_id order by created desc;

但返回的Qty_holding并非最新值。同时编写的等效Django查询:

UserHolding.objects.values('tokenid_id','uid_id','Qty_holding').order_by().annotate(Count('tokenid_id'),Count('uid_id'))

也没有得到正确结果,恳请帮忙修正。

问题分析

你的MySQL查询问题在于:当使用GROUP BY时,如果没有对非分组字段(比如Qty_holding)使用聚合函数,MySQL会随机返回分组内的某一行值,而不是你期望的最新行。ORDER BY created desc只是对最终结果排序,不会影响分组时的行选择。

Django的查询同理,单纯用annotate(Count(...))并不能筛选出最新记录,只是统计数量,完全没关联到created时间来获取最新行。

MySQL 修正方案

这里提供两种常用的正确写法:

方法1:子查询获取最新时间后关联

先查询每个uid_id和tokenid_id对应的最大created时间,再关联原表获取对应的Qty_holding:

SELECT uh.uid_id, uh.tokenid_id, uh.Qty_holding
FROM accounts_userholding uh
INNER JOIN (
    SELECT uid_id, tokenid_id, MAX(created) AS latest_created
    FROM accounts_userholding
    GROUP BY uid_id, tokenid_id
) latest_uh ON uh.uid_id = latest_uh.uid_id 
            AND uh.tokenid_id = latest_uh.tokenid_id 
            AND uh.created = latest_uh.latest_created;

方法2:使用窗口函数(MySQL 8.0+)

用ROW_NUMBER()窗口函数按uid_id和tokenid_id分组,按created降序排序,取每组的第一行(最新记录):

SELECT uid_id, tokenid_id, Qty_holding
FROM (
    SELECT 
        uid_id, tokenid_id, Qty_holding,
        ROW_NUMBER() OVER (PARTITION BY uid_id, tokenid_id ORDER BY created DESC) AS rn
    FROM accounts_userholding
) t
WHERE rn = 1;

这个方法更简洁,适合MySQL 8.0及以上版本。

Django ORM 修正方案

对应上面的两种方法,Django也有两种实现方式:

方法1:子查询关联最新时间

from django.db.models import Subquery, OuterRef, Max

# 先获取每个(uid_id, tokenid_id)的最新created时间
latest_created_subquery = UserHolding.objects.filter(
    uid_id=OuterRef('uid_id'),
    tokenid_id=OuterRef('tokenid_id')
).values('uid_id', 'tokenid_id').annotate(
    latest_created=Max('created')
).values('latest_created')

# 筛选出符合最新时间的记录
latest_holdings = UserHolding.objects.filter(
    created=Subquery(latest_created_subquery)
).values('uid_id', 'tokenid_id', 'Qty_holding')

方法2:使用窗口函数(Django 2.0+)

如果你的Django版本支持窗口函数(2.0及以上),可以直接用Window和RowNumber:

from django.db.models import Window, F
from django.db.models.functions import RowNumber

# 给每条记录添加行号,按用户和token分组,按created降序排序
ranked_holdings = UserHolding.objects.annotate(
    rn=Window(
        expression=RowNumber(),
        partition_by=[F('uid_id'), F('tokenid_id')],
        order_by=F('created').desc()
    )
)

# 筛选出每组的第一行(最新记录)
latest_holdings = ranked_holdings.filter(rn=1).values('uid_id', 'tokenid_id', 'Qty_holding')

这样就能正确获取每个用户对应每个token的最新Qty_holding值了。

内容的提问来源于stack exchange,提问作者Manish Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:43:53