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

从Excel迁移到Power BI后SOAP连接器Power Query性能骤降求助

Power BI中SOAP API查询性能暴跌50倍的问题

我正在搭建首个Power BI配置,用自定义Power Query M脚本通过SOAP API获取数据。为方便反复测试,先在熟悉的Excel中实现:小查询耗时约1秒,大查询需2-3分钟,2个超大查询加载需20分钟(因仅需每年更新几次,这个耗时可接受)。

但切换到Power BI Desktop后出现问题:最初将Excel文件导入Power BI时,性能表现和Excel中相近;但把Power Query脚本直接复制到Power BI Desktop后,性能暴跌50倍——简单查询现在需要1-2分钟,超大查询加载1小时仍未完成,只能取消。

请问这是什么原因?


相关SOAP查询代码说明

以下是对象查询的SOAP代码(这类查询返回500-4000条记录,取决于对象类型)。我创建了1个递归函数getTEobjects(因为API单次SOAP调用最多返回1000条对象记录),以及2个配置表(TEobjectFields和TEobjectRelations,用于定义每种对象类型需请求的字段和关联关系)。全局变量test用于切换测试与生产环境。

(objectType as text,  optional loop as number, optional maxLoop as number, optional result as table) =>
let
 
    loop = if loop is null then 0 else loop,

    produrl = "https://_here_goes_the_PROD_url_",
    testurl = "https://_here_goes_the_TEST_url_",
    baseurl = if test then testurl else produrl,

    username="*****",
    password = "####",
    certificateKey="abracadabra",
    
    //define api call info
    apiEndPoint = "findObjectsExtended",
    apiReplyLimit = 1000,
    apiReplyCount = "totalnumberofobjects",

    //first register and get registrationKey
    SOAPEnvelope_begin = 
    "<soapenv:Envelope xmlns:soapenv=#(0022)http://schemas.xmlsoap.org/soap/envelope/#(0022) xmlns:ver=#(0022)http://www.timeedit.se/timeedit3/version3#(0022)>
    <soapenv:Header/>
    <soapenv:Body>
    ",
    SOAPEnvelope_end = 
    "</soapenv:Body>
    </soapenv:Envelope>",

    SOAPEnvelope_register = 
    "<ver:register>
        <!--Optional:-->
        <ver:certificate>" & certificateKey & "</ver:certificate>
    </ver:register>
    ",

    options_register = [
        #"Content-Type" = "text/xml;charset=UTF-8"
    ],

    Source_register = Web.Contents(
        baseurl,
        [
            RelativePath = "register" ,
            Content = Text.ToBinary(SOAPEnvelope_begin & SOAPEnvelope_register & SOAPEnvelope_end)
        ]
    ),

    applicationKeyTable = Xml.Tables(Source_register){0}[Table]{0}[Table]{0}[Table]{0}[Table]{0}[Table],
    applicationKey = applicationKeyTable{0}[applicationkey],

    //Now do the actual API call
    SOAPEnvelope_login =  "
    <ver:login>
        <username>" & username & "</username>
        <password>" & password & "</password>
        <applicationkey>" & applicationKey & "</applicationkey>
    </ver:login>
    ",

    SOAPEnvelope_objects = "    
         <!--Optional:-->
         <ver:type>" & objectType & "</ver:type>" &
"          <ver:returnfields>
" &  List.Accumulate(Table.ToList(Table.SelectColumns(TEobjectFields,objectType)),"",(state,current)=>let fld=if Text.Length(current)=0 then "" else "<field>" & current & "</field>" in state & fld) &
"          </ver:returnfields>
          <ver:relatedobjecttypes>
"  & List.Accumulate(Table.ToList(Table.SelectColumns(TEobjectRelations,objectType)),"",(state,current)=>let fld=if Text.Length(current)=0 then "" else "<type>" & current & "</type>" in state & fld) &
"          </ver:relatedobjecttypes>
    <ver:beginindex>" & Number.ToText(loop * apiReplyLimit ) & "</ver:beginindex>
    <ver:numberofobjects>" & Number.ToText(apiReplyLimit) & "</ver:numberofobjects>
    ",

    options_objects = options_register,

    cnt= Text.ToBinary(SOAPEnvelope_begin & "<ver:" & apiEndPoint & ">" & SOAPEnvelope_login & SOAPEnvelope_objects & "</ver:" & apiEndPoint & ">"  & SOAPEnvelope_end),
    
    Source_Objects = Xml.Tables(Web.Contents(baseurl, [RelativePath=apiEndPoint, Content=cnt])),
    //find the real result table bu navigating down 5 levels
    findObjectsTbl = Source_Objects{0}[Table]{0}[Table]{0}[Table]{0}[Table]{0}[Table],

    //extract the total number of records
    objectsCnt = Table.TransformColumnTypes(findObjectsTbl,{{apiReplyCount, Int64.Type}}),
    totalnumberofobjects = objectsCnt[totalnumberofobjects]{0},
    //calc maxLoop: if null then first call, so initialise based on qty received. Max replies per SOAP call=apiReplyLimit
    maxLoop = if maxLoop is null then  Number.RoundUp(totalnumberofobjects/apiReplyLimit) else maxLoop,
    
        //extract list with objects 
        objectsRaw = findObjectsTbl{0}[objects]{0}[object],
        objectsLijst = objectsRaw,
    
        result = if loop = 0 then objectsLijst else Table.Combine({result,objectsLijst}),
    
        output = 
            if (loop+1)<maxLoop then 
                @getTEobjects(objectType,loop+1,maxLoop, result)
            else
                result,

    //extract the requested fields from the 'fields' sub-table in the fields attribute
    b2 = List.Accumulate(Table.ToList(Table.SelectColumns(TEobjectFields,objectType)),output,(state,fieldName)=>
        let  out = if Text.Length(fieldName)>0 
            then 
                Table.AddColumn(if Table.HasColumns(state,{fieldName}) then Table.RemoveColumns( state,{fieldName}) else state,fieldName,each extractValueFromTEFields(fieldName,[fields]))
            else
                state
        in out),
    
    //test: expand relations. Not implemented so far since ertain 'related' fields are empty resulting in error
    b3 = if Table.HasColumns(b2,{"related"}) 
        then Table.ExpandTableColumn(b2, "related", {"object"}, {"related.object"})
        else b2

in
    b2
    //b3

查询调用示例

以下调用在Excel中耗时约1秒,在Power BI Desktop中需约1分钟:

let
    Bron = getTEobjects("course")
in
    Bron

注:大查询和超大查询采用相同架构,但调用其他SOAP接口,数据量为1万至40万条记录。


内容的提问来源于stack exchange,提问作者Christof De Backere

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 06:17:04