如何用GROUP BY子句将表A记录插入主从表?解决Msg 8120报错
这个问题我碰到过好多次,核心是SQL Server对GROUP BY的严格规则和你想要去重插入父记录的需求之间的矛盾,咱们一步步拆解解决:
首先得明确Msg 8120错误的本质:SQL Server默认要求SELECT列表里的非聚合列必须出现在GROUP BY子句中(除非修改数据库兼容级别或ANSI设置,不推荐这么做,会破坏标准SQL一致性)。但你不想把所有列都加进GROUP BY,因为那样会因某些列的细微差异生成冗余父记录——这完全合理,父表本该只存每组(ReturnBatchID + From_Id)唯一的一条记录。
下面分两种场景给你针对性方案:
场景1:同一组内的其他列值完全一致
如果每个ReturnBatchID + From_Id组合对应的FromTo_TypeId、To_Id、ReturnDate等列的值完全相同,那么可以用**聚合函数(MAX/MIN)**包裹这些列。同一组内值相同的情况下,MAX/MIN不会改变结果,但能满足SQL Server的GROUP BY要求:
INSERT INTO tbl_ItemReturnMaster (FromTo_Type_Id, From_Id, To_Id, ReturnedDate, StatusId, UserId, Reason, DateUpdated, PharmaReturnID) SELECT MAX(FromTo_TypeId) AS FromTo_TypeId, From_Id, MAX(To_Id) AS To_Id, MAX(ReturnDate) AS ReturnedDate, MAX(StatusId) AS StatusId, MAX(UserId) AS UserId, MAX(Reason) AS Reason, MAX(DateUpdated) AS DateUpdated, MAX(PharmaReturnID) AS PharmaReturnID FROM tbl_InternalItemReturns GROUP BY ReturnBatchID, From_Id
先验证数据一致性
执行上述SQL前,建议先确认同一组内的列确实无差异,避免聚合后取到非预期值:
SELECT ReturnBatchID, From_Id, COUNT(DISTINCT FromTo_TypeId) AS TypeIdDiffCount, COUNT(DISTINCT To_Id) AS ToIdDiffCount, COUNT(DISTINCT ReturnDate) AS ReturnDateDiffCount FROM tbl_InternalItemReturns GROUP BY ReturnBatchID, From_Id HAVING COUNT(DISTINCT FromTo_TypeId) > 1 OR COUNT(DISTINCT To_Id) > 1 OR COUNT(DISTINCT ReturnDate) > 1;
如果这个查询返回结果,说明同一组内存在不同值,就得用场景2的方案。
场景2:同一组内存在不同值,需选择特定记录
如果同一ReturnBatchID + From_Id组内有不同记录(比如不同的DateUpdated),但你只想插入其中一条(比如最新的那条),那么**窗口函数ROW_NUMBER()**是更稳妥的选择:
WITH RankedReturns AS ( SELECT FromTo_TypeId, From_Id, To_Id, ReturnDate, StatusId, UserId, Reason, DateUpdated, PharmaReturnID, -- 按ReturnBatchID+From_Id分组,给每组记录按DateUpdated倒序排名 ROW_NUMBER() OVER (PARTITION BY ReturnBatchID, From_Id ORDER BY DateUpdated DESC) AS rn FROM tbl_InternalItemReturns ) INSERT INTO tbl_ItemReturnMaster (FromTo_Type_Id, From_Id, To_Id, ReturnedDate, StatusId, UserId, Reason, DateUpdated, PharmaReturnID) SELECT FromTo_TypeId, From_Id, To_Id, ReturnDate, StatusId, UserId, Reason, DateUpdated, PharmaReturnID FROM RankedReturns WHERE rn = 1; -- 只取每组排名第一的记录(最新的)
你可以根据需求修改ORDER BY字段,比如想要最早的记录就改成ORDER BY DateUpdated ASC。
后续插入详情表的建议
插入父表后,你可以通过父表的主键(假设tbl_ItemReturnMaster有自增主键ItemReturnMasterID)和原表的ReturnBatchID、From_Id关联,把对应子记录插入详情表:
INSERT INTO tbl_ItemReturnDetails (ItemReturnMasterID, /* 其他详情列 */) SELECT m.ItemReturnMasterID, i./* 原表中属于详情的列,比如ItemId, Quantity等 */ FROM tbl_InternalItemReturns i JOIN tbl_ItemReturnMaster m ON i.ReturnBatchID = m.ReturnBatchID AND i.From_Id = m.From_Id;
内容的提问来源于stack exchange,提问作者Kamran Khan

