SQL 从两个不关联的表中取出 12条 数据 并按照时间排序

本文介绍了一种使用SQL从特定移动ID为'i908'的数据表中选取最新12条记录的方法。通过联合查询两个不同类型的表,并按照创建日期降序排列来实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

select top 12 * from
(select a.seqid,a.title,a.jar href, a.datecreated, a.mobileId
from wap_cms_java_detail a
where a.show = 1 and a.mobileId='i908'
union
select b.seqid, b.[name] title, b.href, b.datecreated, b.mobileId
from wap_cms_link b
where b.status = 1 and b.mobileId='i908') c
order by datecreated desc
<think>我们注意到用户要求:price要根据t1数据的入库时间最晚的那个作为合项的price 这意味着在分组内,我们需要取最新入库时间对应的price值,而是任意值。 因此,我们需要修改price的获取方式,确保取的是分组内入库时间(假设入库时间字段为create_time或类似字段)最新的记录对应的price。 由于原查询中使用了GROUP BY,我们需要在分组内对price进行特殊处理。这里能直接使用ANY_VALUE(),因为它是确定的。 我们可以使用子查询或窗口函数来确保取到最新记录的price。但考虑到整个查询已经比较复杂,这里建议使用聚合函数配合件判断来实现。 方法1:使用子查询(效率可能较低) 可以改写为在分组内按照入库时间排序取第一,但这样需要为每个分组单独排序,在MySQL中可以通过变量或子查询实现,但较复杂。 方法2:使用MAX()和GROUP_CONCAT()(当分组内记录数多时可行) 例如:SUBSTRING_INDEX(GROUP_CONCAT(price ORDER BY create_time DESC), ',', 1) 但这样需要将price转为字符串,且如果price是浮点数可能有精度问题。 方法3(推荐):使用窗口函数(MySQL 8.0+) 如果MySQL版本是8.0以上,可以使用窗口函数先按分组和入库时间排序,然后取第一,再使用DISTINCT或GROUP BY。但注意原查询已经用了GROUP BY,所以我们可以先在一个子查询中用窗口函数标记出行号,然后在外层过滤。 然而,原查询中除了price,其他非聚合列也需要处理。用户要求“强制显示”,但根据业务逻辑,可能也需要取最新记录的值?还是任意值?用户只指定了price按最新,其他列未说明。 根据问题描述,我们只对price按最新入库时间处理,其他非聚合列仍然可以按ANY_VALUE()处理(或者也按最新?但用户未明确,这里按只对price特殊处理)。 但注意:其他列可能和price来自同一行记录(即最新入库时间的那记录),否则数据可能一致。例如,最新入库的那记录的standard_asset_id等可能和之前的不同。所以需要明确:是否要求分组内所有列都取最新入库时间的那记录的值? 假设用户希望分组内所有列都取最新入库时间的那记录的值,那么我们可以这样修改: 步骤: 1. 在子查询中为每个分组内的记录按入库时间排序标记行号(row_number) 2. 外层查询只取行号为1的记录,然后进行分组(但实际上此时已经需要分组,因为每个分组只取一,但原查询的GROUP BY是多个列,所以需要聚合函数?) 但注意原查询中使用了GROUP BY,且计算了聚合达式(如COUNT?)但原查询中没有聚合函数,只有非聚合列和达式。重新观察原查询,发现计算字段(如firstInventoryWinAndLossNum)是依赖于单记录的(t2.account_num, t2.first_inventory_num等),而是分组聚合。 这里存在一个矛盾:原查询中使用了GROUP BY,但SELECT列中有多个非聚合列,这本身在严格模式下是允许的。用户之前的要求是强制显示这些非聚合列,现在又要求price取最新。 实际上,根据业务逻辑,这个GROUP BY的目的是将相同类目、名称、型号、管理部门ID的资产合为一行。那么合时,其他列(如user_dept_id, user_id等)可能来自不同的记录,因此需要指定如何合(取最大值、最小值、最新值等)。 因此,我们需要重构查询:使用窗口函数为每个分组内的记录按入库时间降序排序,然后取第一记录,同时保留所有需要的列。然后,再对这个结果集按照分组字段进行分组(但此时每个分组只有一记录,所以实际上需要再分组?)。或者,我们可以在子查询中完成分组和取最新记录,然后连接到原。 考虑到原查询中还有LEFT JOIN,为了改变原逻辑,我们采用以下方案: 1. 先在一个子查询中,对asset_escrow_ledger(t1)按分组字段(t5.categoryName, t5.NAME, t5.model, t1.management_dept_id)进行分组,找出每组中入库时间(假设字段名为create_time)最大的记录(即最新记录)的id(或唯一标识)。 2. 然后,将原查询中的t1用这个子查询进行过滤,只取每组最新的记录。 但是,这样会改变结果集的行数:原来每个分组有多记录,现在只取一。而原查询中,每个分组可能有多记录,但被GROUP BY合成一行(非聚合列任意取一)。现在用户要求price取最新,但其他列呢?如果其他列也取最新,那么整个行就是最新那记录,这样其实就需要GROUP BY了,因为每个分组只输出一记录(最新记录)。 所以,如果用户希望每个分组只显示最新的一记录,那么我们可以去掉GROUP BY,而用窗口函数在子查询中按分组排序取最新记录。但原查询中还有从t2和其他连接的数据,这些数据也需要对应到最新记录上。 因此,我们重构如下: 步骤: a. 先对主t1(asset_escrow_ledger)进行分组,获取每个分组(t5.categoryName, t5.NAME, t5.model, t1.management_dept_id)的最新记录的唯一标识(比如t1.id)。注意:这里需要先连接t5来获取分类信息,因为分组字段包含t5的列。 b. 然后,将原查询改写为:先获取每个分组的最新t1.id,然后以这个id集合来查询,这样每行就是每个分组的最新记录。 但是,这样会丢失同一个分组内其他记录的信息吗?原查询中,GROUP BY后每个分组只有一行,而计算字段(如firstInventoryWinAndLossNum)也是基于单记录的(因为t2是通过ledger_id与t1关联,而t1.id在分组内有多,但GROUP BY后只取了一)。所以,原查询实际上是将同一个分组内的多记录合成一,但计算字段却只用了其中一记录(因为非聚合列任意取一)的值,这本身可能就有问题。 现在,用户要求price取最新,那么其他列如果也取最新(即最新记录的那数据),那么整个行就代最新记录,这样更合理。 因此,我们修改为:先获取每个分组的最新记录(t1.id),然后原查询中WHERE变,但只取这些最新记录。这样就需要GROUP BY了,因为每个分组只取一记录。 具体修改: 1. 先写一个子查询,找出每个分组的最新记录对应的t1.id: ```sql SELECT t5.categoryName, t5.`NAME`, t5.model, t1.management_dept_id, MAX(t1.create_time) AS max_create_time -- 假设有create_time字段 FROM asset_escrow_ledger t1 LEFT JOIN ... -- 连接t5,和原查询一样 WHERE inventory_id = 7 AND ledger_id IN (...) GROUP BY t5.categoryName, t5.`NAME`, t5.model, t1.management_dept_id ``` 2. 然后,用这个子查询和原t1连接,获取最新记录: ```sql SELECT ... -- 原SELECT列 FROM asset_escrow_ledger t1 JOIN ( -- 上述子查询 ) AS latest ON t1.management_dept_id = latest.management_dept_id AND t1.create_time = latest.max_create_time -- 注意:还需要连接t5的字段,但子查询中已经group by了这些字段,所以可以用t5的字段来连接?但这里无法直接连接t5字段,因为子查询中t5字段是分组字段,但t1本身没有这些字段。 ``` 但是,由于t5字段是通过连接t5得到的,我们能在子查询外部用这些字段连接。所以,我们可以将子查询中的t1.id也查出来,然后在外层用t1.id去过滤。 修改子查询,取出t1.id: ```sql SELECT t5.categoryName, t5.`NAME`, t5.model, t1.management_dept_id, MAX(t1.create_time) AS max_create_time, SUBSTRING_INDEX(GROUP_CONCAT(t1.id ORDER BY t1.create_time DESC), ',', 1) AS latest_id FROM asset_escrow_ledger t1 ... -- 连接和变 GROUP BY t5.categoryName, t5.`NAME`, t5.model, t1.management_dept_id ``` 然后,外层查询中,将t1的件改为t1.id = latest.latest_id。 但是,GROUP_CONCAT有长度限制,如果分组内记录很多,可能被截断。所以,我们可以使用内连接: ```sql SELECT t1.id, t1.create_time FROM asset_escrow_ledger t1 INNER JOIN ( SELECT t5.categoryName, t5.`NAME`, t5.model, t1.management_dept_id, MAX(t1.create_time) AS max_create_time FROM asset_escrow_ledger t1 ... -- 连接t5和件 GROUP BY t5.categoryName, t5.`NAME`, t5.model, t1.management_dept_id ) AS latest ON t1.management_dept_id = latest.management_dept_id AND t1.create_time = latest.max_create_time -- 但是,同一个管理部门在同一时间可能有多个记录?所以最好用id ``` 为了避免同一时间有多个记录,我们可以用id来确保唯一。但这里我们假设create_time和management_dept_id等组合能唯一确定最新记录?一定,所以最好用id。 因此,我们使用窗口函数(ROW_NUMBER)来获取每个分组内的最新记录id(MySQL 8.0+): ```sql WITH latest_ledger AS ( SELECT t1.id, ROW_NUMBER() OVER ( PARTITION BY t5.categoryName, t5.`NAME`, t5.model, t1.management_dept_id ORDER BY t1.create_time DESC ) AS rn FROM asset_escrow_ledger t1 LEFT JOIN ... -- 连接t5,和原查询一样 WHERE inventory_id = 7 AND ledger_id IN (...) ) SELECT ... FROM asset_escrow_ledger t1 JOIN latest_ledger ON t1.id = latest_ledger.id AND latest_ledger.rn=1 LEFT JOIN ... -- 其他连接变 ``` 这样,我们只取每个分组内的最新一记录,然后直接查询这一记录,就需要GROUP BY了。 但是,原查询中还有t2(asset_escrow_company_inventory_ledger)的连接,它是通过t1.id=t2.ledger_id连接的,所以每t1记录对应一t2记录(假设是一对一),所以这样取最新记录后,自然就得到对应的t2记录。 因此,最终修改: 由于原查询中GROUP BY是为了将同一个分组的多记录合成一,而我们现在改为只取最新的一记录,所以再需要GROUP BY,且SELECT列中的所有列都是直接来自一记录,会出现非聚合列的问题。 所以,我们去掉GROUP BY子句,改为使用窗口函数过滤出每个分组的最新记录。 修改后的查询(MySQL 8.0+): ```sql WITH latest_ledger AS ( SELECT t1.id, ROW_NUMBER() OVER ( PARTITION BY t5.categoryName, t5.`NAME`, t5.model, t1.management_dept_id ORDER BY t1.create_time DESC -- 按入库时间降序,取第一 ) AS rn FROM asset_escrow_ledger t1 LEFT JOIN asset_escrow_company_inventory_ledger t2 ON t1.id = t2.ledger_id LEFT JOIN ( SELECT t1.id, t1.NAME, t1.model, t1.unit, t1.category_id, t1.manufacturer_id, t2.NAME AS childCategoryName, t3.NAME AS categoryName, t4.NAME AS parentCategoryName FROM `asset_standard_asset` t1 LEFT JOIN asset_category t2 ON t1.category_id = t2.id LEFT JOIN asset_category t3 ON t2.parent_id = t3.id LEFT JOIN asset_category t4 ON t3.parent_id = t4.id WHERE t1.data_type = 'escrow' ) t5 ON t1.standard_asset_id = t5.id WHERE inventory_id = 7 AND ledger_id IN (...) ) SELECT t5.categoryName, t5.`NAME`, t5.model, t1.standard_asset_id, t1.management_dept_id, t1.user_dept_id, t1.user_id, t1.provider_flag, ROUND(t1.price, 2) AS price, t1.position_id, t2.account_num AS num, t1.num AS amount, t2.remarks, t1.purchase_date, t2.first_inventory_num AS inventory_num, t2.second_inventory_num AS secondInventoryNum, (t2.account_num - t2.first_inventory_num) AS firstInventoryWinAndLossNum, (t2.account_num - t2.second_inventory_num) AS secondInventoryWinAndLossNum, ROUND(((t2.account_num - t2.first_inventory_num) * t1.price), 2) AS firstInventoryAmount, ROUND(((t2.account_num - t2.second_inventory_num) * t1.price), 2) AS secondInventoryAmount FROM asset_escrow_ledger t1 LEFT JOIN asset_escrow_company_inventory_ledger t2 ON t1.id = t2.ledger_id LEFT JOIN ( SELECT t1.id, t1.NAME, t1.model, t1.unit, t1.category_id, t1.manufacturer_id, t2.NAME AS childCategoryName, t3.NAME AS categoryName, t4.NAME AS parentCategoryName FROM `asset_standard_asset` t1 LEFT JOIN asset_category t2 ON t1.category_id = t2.id LEFT JOIN asset_category t3 ON t2.parent_id = t3.id LEFT JOIN asset_category t4 ON t3.parent_id = t4.id WHERE t1.data_type = 'escrow' ) t5 ON t1.standard_asset_id = t5.id INNER JOIN latest_ledger ON t1.id = latest_ledger.id AND latest_ledger.rn=1 WHERE inventory_id = 7 AND ledger_id IN (...); ``` 注意:这里我们假设t1有create_time字段示入库时间,如果没有,请替换为实际的字段名。 如果MySQL版本低于8.0,则使用子查询模拟: ```sql SELECT ... -- 原SELECT列 FROM asset_escrow_ledger t1 ... -- 其他连接 INNER JOIN ( SELECT t5.categoryName, t5.`NAME`, t5.model, t1.management_dept_id, MAX(t1.create_time) AS max_create_time FROM asset_escrow_ledger t1 ... -- 连接t5和件 WHERE inventory_id = 7 AND ledger_id IN (...) GROUP BY t5.categoryName, t5.`NAME`, t5.model, t1.management_dept_id ) AS latest_group ON latest_group.management_dept_id = t1.management_dept_id AND latest_group.categoryName = t5.categoryName AND latest_group.NAME = t5.NAME AND latest_group.model = t5.model AND t1.create_time = latest_group.max_create_time WHERE inventory_id = 7 AND ledger_id IN (...); ``` 但是,如果同一个分组内有两记录具有相同的create_time,则会出现多行。为了避免这种情况,可以使用id来确保唯一。 由于时间关系,我们提供8.0版本的CTE写法,低版本可以使用临时或子查询,但较为复杂。 请根据实际情况调整。 §§相关问题§§ 1. 如果同一个分组内最新入库时间有多记录,如何确保只取其中一? 2. 在MySQL 5.7中如何实现ROW_NUMBER() OVER(PARTITION BY ...)的相同功能? 3. 当资产托管台账(t1)数据量非常大时,如何优化这个查询的性能? 4. 如何避免GROUP_CONCAT()函数在获取最新记录ID时的字符串截断问题? 5. 如果原查询中t1没有记录入库时间字段,应该如何处理?</think>### 解决强制显示非聚合列取最新价格的方法 您的需求是:在保持 `GROUP BY t5.categoryName, t5.NAME, t5.model, t1.management_dept_id` 分组的同时,**强制显示所有非聚合列**,且 **price 必须取分组内最新入库时间(假设字段为 `create_time`)对应的值**。以下是完整解决方案: #### ✅ 最终优化方案(MySQL 8.0+ 使用窗口函数) ```sql SELECT * FROM ( SELECT t5.categoryName, t5.`NAME`, t5.model, t1.standard_asset_id, t1.management_dept_id, t1.user_dept_id, t1.user_id, t1.provider_flag, -- 取分组内最新入库时间的price FIRST_VALUE(t1.price) OVER ( PARTITION BY t5.categoryName, t5.`NAME`, t5.model, t1.management_dept_id ORDER BY t1.create_time DESC ) AS latest_price, t1.position_id, t2.account_num AS num, t1.num AS amount, t2.remarks, t1.purchase_date, t2.first_inventory_num AS inventory_num, t2.second_inventory_num AS secondInventoryNum, (t2.account_num - t2.first_inventory_num) AS firstInventoryWinAndLossNum, (t2.account_num - t2.second_inventory_num) AS secondInventoryWinAndLossNum, ROUND(((t2.account_num - t2.first_inventory_num) * FIRST_VALUE(t1.price) OVER w) , 2) AS firstInventoryAmount, ROUND(((t2.account_num - t2.second_inventory_num) * FIRST_VALUE(t1.price) OVER w) , 2) AS secondInventoryAmount, -- 为去重添加行号 ROW_NUMBER() OVER ( PARTITION BY t5.categoryName, t5.`NAME`, t5.model, t1.management_dept_id ORDER BY t1.create_time DESC ) AS rn FROM asset_escrow_ledger t1 LEFT JOIN asset_escrow_company_inventory_ledger t2 ON t1.id = t2.ledger_id LEFT JOIN ( SELECT t1.id, t1.NAME, t1.model, t1.unit, t1.category_id, t1.manufacturer_id, t2.NAME AS childCategoryName, t3.NAME AS categoryName, t4.NAME AS parentCategoryName FROM `asset_standard_asset` t1 LEFT JOIN asset_category t2 ON t1.category_id = t2.id LEFT JOIN asset_category t3 ON t2.parent_id = t3.id LEFT JOIN asset_category t4 ON t3.parent_id = t4.id WHERE t1.data_type = 'escrow' ) t5 ON t1.standard_asset_id = t5.id WHERE inventory_id = 7 AND ledger_id IN (26580, 26596, ...) -- 您的ID列 WINDOW w AS ( PARTITION BY t5.categoryName, t5.`NAME`, t5.model, t1.management_dept_id ORDER BY t1.create_time DESC ) ) AS sub WHERE rn = 1; -- 只取分组内最新记录 ``` #### ⚠️ 关键修改说明 1. **价格取最新逻辑** 使用 `FIRST_VALUE(t1.price) OVER (... ORDER BY create_time DESC)` 确保取分组内最新入库时间的价格 2. **非聚合列处理** 通过 `ROW_NUMBER() + WHERE rn=1` 实现: - 保留所有原始列(丢失任何字段) - 确保每组只返回一记录 - 所有列均来自最新入库的那记录 3. **计算字段同步** 盈亏金额计算统一使用最新价格: ```sql ROUND((... * FIRST_VALUE(t1.price) OVER w), 2) ``` 4. **MySQL 5.7兼容方案** 若版本低于8.0,改用子查询实现: ```sql SELECT t5.categoryName, t5.NAME, ..., -- 取最新价格 (SELECT t1_inner.price FROM asset_escrow_ledger t1_inner WHERE t1_inner.standard_asset_id = t1.standard_asset_id AND t1_inner.management_dept_id = t1.management_dept_id ORDER BY t1_inner.create_time DESC LIMIT 1) AS latest_price ... ``` #### ✨ 优势对比 | 方案 | 数据一致性 | 性能 | 代码复杂度 | |--------------------|------------|--------|------------| | 原始GROUP BY | ❌ 列值可能一致 | ✅ 快 | 简单 | | ANY_VALUE() | ❌ 随机取值 | ✅ 快 | 简单 | | **本方案(窗口函数)** | ✅ 全部一致 | ⚠️ 中等 | 中等 | | 子查询方案 | ✅ 全部一致 | ❌ 慢 | 复杂 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值