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

跨服务器SQL查询结果不一致问题求助

问题描述

执行一条跨服务器SQL查询时,每次返回的行数几乎都不一致,但未出现任何报错。已尝试在查询涉及的所有服务器上执行该语句,也在一台关联了这两台服务器的无关第四台服务器上测试过。单独运行每个CTE(公共表表达式)时结果始终一致,仅查询的最后部分会出现结果不一致的情况。

查询语句

with customerOrderMatches as (
  select
 SAPOX.docentry
,SAPO.cardcode as 'O CC'
,SAPCMS.CardCode+'-'+SAPCMS.Address as 'OrigShipToID'
,SAPCMB.CardCode+'-'+SAPCMB.Address as 'OrigCustID'
from [Server1].[BowDB].[dbo].ORDR SAPO
join [Server1].[BowDB].[dbo].RDR12 SAPOX
on SAPO.docentry=SAPOX.docentry
left outer join [Server1].[BowDB].[dbo].CRD1 SAPCMS --ship to match
on SAPCMS.cardcode=SAPO.cardcode
and SAPCMS.Address=SAPO.Shiptocode
and SAPCMS.AdresType='S'
and SAPCMS.Street=SAPOX.StreetS
left outer join [Server1].[BowDB].[dbo].CRD1 SAPCMB --bill to match
on SAPCMB.cardcode=SAPO.cardcode
and SAPCMB.Address=SAPO.PayToCode
and SAPCMB.AdresType='B'
and SAPCMB.Street=SAPOX.StreetB
where 
SAPO.cardcode NOT IN (
'1001', '1002', '1003'
)
AND SAPO.canceled = 'N'
),

customerRank as (
SELECT 
    rtrim(C.custid) AS 'custid'
    ,COUNT(SLShipper.ShipperID) as 'totalShippers'
    ,Row_number() OVER (ORDER BY COUNT(SLShipper.ShipperID) DESC) AS 'customerRank'
FROM [MLSQL12].[SLapplication15].dbo.customer C
left outer join [MLSQL12].[SLapplication15].dbo.SOShipHeader SLShipper 
    on SLShipper.custid=C.custid
GROUP BY C.CustID
),

customerShipToRank as (
SELECT 
rtrim(SOA.CustID) AS 'custid'
,rtrim(SOA.ShiptoID) as 'shiptoid'
,COUNT(SLShipper.ShipperID) as 'totalShippers'
,cast(Row_number() OVER(Partition by SOA.custid ORDER BY COUNT(SLShipper.ShipperID) DESC) as int) AS 'ShipToRank'
,customerRank
FROM [MLSQL12].[SLapplication15].dbo.soaddress SOA
left outer join [MLSQL12].[SLapplication15].dbo.SOShipHeader SLShipper
on SLShipper.CustID=SOA.CustId
and SLShipper.ShiptoID=SOA.shiptoid
join customerRank CR
on CR.custid=SOA.CustID
GROUP BY 
SOA.CustID
,SOA.ShiptoID
,customerRank
),

combinedData as (
select
COM.Docentry
,CXR.*
,CSTR.*
from customerOrderMatches COM
join MLSQL15.HistoricalData.Hist.CustomerXRef CXR
on CXR.OrigShipToID=COM.OrigShipToID collate SQL_Latin1_General_CP850_CI_AS
and CXR.OrigCustID=COM.OrigCustID collate SQL_Latin1_General_CP850_CI_AS
left outer join customerShipToRank CSTR
on CSTR.shiptoid =CXR.BKShiptoId 
and CSTR.custid =CXR.BKCustId 
)

select 
*
from combinedData CD
where CONCAT(customerRank,ShipToRank) in (
select MIN(CONCAT(customerRank,ShipToRank))
from combinedData
group by docentry)
order by docentry

补充信息

  • 已知查询存在可优化的低效点,但这不应该导致结果不一致。
  • 涉及的数据库包括SAP数据库、Microsoft Dynamics SL数据库,以及自研的并购数据专用数据库。

更新(2022年12月12日)

查询返回的其中一列是订单表的主键DocEntry,6次执行结果如下:

查询执行次数总行数最小DocEntry最大DocEntry
114509931412
214509931412
3545510531408
4544810831411
514509931412
6518110531411

问题原因分析及解决方案

1. ROW_NUMBER()排序键重复导致的不确定性

在customerRank和customerShipToRank两个CTE中,ROW_NUMBER()仅按COUNT(SLShipper.ShipperID)排序。如果多个客户(或客户收货地址)的totalShippers数值相同,SQL Server无法确定这些行的排序顺序,每次执行可能生成不同的customerRank或ShipToRank值。这种不确定性会传递到后续逻辑,导致最终筛选结果波动。

2. 字符串拼接的最小值比较逻辑缺陷

最终筛选使用CONCAT(customerRank,ShipToRank)取最小值,这种字符串比较存在逻辑问题:

  • 例如customerRank=10+ShipToRank=2拼接为102,customerRank=2+ShipToRank=10拼接为210,字符串比较会认为102更小,但这可能不符合实际业务的数值组合逻辑。
  • 结合ROW_NUMBER()的不稳定排序,拼接后的最小值会随机变化,直接导致筛选结果不一致。

3. 跨服务器查询的执行计划波动

跨服务器查询时,SQL Server可能选择不同的执行计划(比如数据拉取顺序、连接方式变化),如果执行计划影响到ROW_NUMBER()的计算顺序,会进一步加剧结果的不一致性。


修复方案

方案1:为ROW_NUMBER()添加稳定排序键

在ROW_NUMBER()的ORDER BY子句中加入唯一标识列,确保排序结果固定:

  • 修改customerRank中的排序逻辑:
    Row_number() OVER (ORDER BY COUNT(SLShipper.ShipperID) DESC, C.CustID) AS 'customerRank'
    
  • 修改customerShipToRank中的排序逻辑:
    cast(Row_number() OVER(Partition by SOA.custid ORDER BY COUNT(SLShipper.ShipperID) DESC, SOA.ShiptoID) as int) AS 'ShipToRank'
    

添加唯一列(如CustID、ShiptoID)后,即使totalShippers相同,排序顺序也不会随机变化,ROW_NUMBER()生成的排名值将稳定一致。

方案2:修正最小值比较逻辑

避免字符串拼接,改用数值组合判断最小值,例如通过customerRank * 10000 + ShipToRank(假设ShipToRank不超过4位数)生成数值型组合键:

select 
*
from combinedData CD
where customerRank * 10000 + ShipToRank in (
select MIN(customerRank * 10000 + ShipToRank)
from combinedData
group by docentry)
order by docentry

这种方式能避免字符串比较的逻辑错误,结合方案1的稳定排序,可彻底解决结果不一致问题。

方案3:强制固定执行计划(可选)

如果跨服务器执行计划波动是诱因,可使用查询提示(如OPTION(FORCE ORDER))强制SQL Server使用固定连接顺序,但需先分析执行计划后谨慎使用,避免引入性能问题。

内容的提问来源于stack exchange,提问作者Brendan Lesinski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 19:50:25