Forum Discussion

hjaf's avatar
hjaf
Advocate I
6 years ago
Solved

SharePoint list query alternative or optimization

Hello everyone!

 

Because sharepoint list queries quickly comes too slow to work with in Power BI, I have experimented with the query that sharepoint generates for excel. This is vastly more effective in Excel compared to getting the same information using the sharepoint query in Power BI. I think the excel export query is basically a query of the list view, where all the lookup values are flattened.  I can't seem to find the way to recreate this query in Power BI, It looks like its using a OLE DB method, but not sure how to continue from there, and some posts suggest that this is not supported by PowerBI.
Connectionstring for excel: "Provider=Microsoft.Office.List.OLEDB.2.0;Data Source="";ApplicationName=Excel;Version=12.0.0.0"
Anyone have experience dealing with this?

 

I think the reason for queries to sharepoint-resources become extremely slow, is due to all the lookup and choice columns, to get the value of the column I have to expand them. An alternative to exploring these view queries is optimizing the queries, suggestions are most welcome! 😄

40 Replies

    • jaleman's avatar
      jaleman
      Frequent Visitor

      Thank you, your video helped me. After I moved to version 2 everything was faster.

      • CmdrKeene's avatar
        CmdrKeene
        Helper IV

        If you think version 2 is fast, wait until you try the "good" method using the REST API.  I have a list with more than 150,000 items, and it refreshes in about 8 seconds.

         

    • Freddy_Paredes's avatar
      Freddy_Paredes
      Regular Visitor

      first of all you are a crack, My query is the following, I am applying your code and I have a very large sp list.... and it returns me 150000 elements but they are duplicates please could you help me

       

      let
      sitename ="<your site>", // if a subsite use "Site/SubSite"
      listname = "BigList",
      baseurl = "https://<your SharePoint URL>/sites/" & sitename & "/_api/web/lists/GetByTitle('" & listname & "')/",
      itemcount = Json.Document(Web.Contents(baseurl&"ItemCount", [Headers=[Accept="application/json"]]))[value],
      skiplist = List.Numbers(0, Number.RoundUp(itemcount/5000), 5000),
      #"Converted to Table" = Table.FromList(skiplist, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
      #"Renamed Columns" = Table.RenameColumns(#"Converted to Table",{{"Column1", "Skip"}}),
      #"Changed Type" = Table.TransformColumnTypes(#"Renamed Columns",{{"Skip", type text}}),
      fieldselect = "&$top=5000", // all fields with no expansion
      //fieldselect = "&$top=5000&$select = Id,Title,Person,Date", // list desired fields (no expansion)
      //fieldselect = "&$top=5000&$select=Id,Title,Choice,LookupColumn/Title,LookupColumn/Project,LookupColumn/ProjectStatus,Date,Person/LastName,Person/FirstName,Person/EMail&$expand=LookupColumn,Person",
      Custom1 = Table.AddColumn(#"Changed Type", "Items", each Json.Document(Web.Contents(baseurl& "/items?$skipToken=Paged=TRUE%26p_ID=" & [Skip] & fieldselect, [Headers=[Accept="application/json"]]))),
      #"Expanded Items" = Table.ExpandRecordColumn(Custom1, "Items", {"value"}, {"value"}),
      #"Expanded value" = Table.ExpandListColumn(#"Expanded Items", "value")
      in
      #"Expanded value"

      • CmdrKeene's avatar
        CmdrKeene
        Helper IV

        Add a "Remove Duplicates" step to the end of your query, using the list  item ID as the field to de-duplicate.

  • mahoneypat's avatar
    mahoneypat
    Microsoft Employee

    Sharepoint List data sources can be slow.  Fortunately, there is a much faster way.  Try a blank query with this formula as your Source.

    = Json.Document(Web.Contents("https://<YourTenantName>.sharepoint.com/sites/<YourSiteName>/_api/web/lists/GetByTitle('<YourListName>')/items?$top=5000", [Headers=[Accept="application/json"]]))

     

    Replace all the parts in < >, including the < >.  You will get a JSON response.

     

    There is another version that does pagination if your list is >5000 items.  Please let me know if you need that one.

     

    If this solution works for you, please mark it as the solution.  Kudos are appreciated too.  Please let me know if not.

    Regards,

    Pat

     

     

     

    • hjaf's avatar
      hjaf
      Advocate I

      mahoneypat Awesome!
      yes, I most definately need to use pagination 🙂 near 20k items in the lists 🙂 

      • mahoneypat's avatar
        mahoneypat
        Microsoft Employee

        Replace after the ? with the following

         

        ?$skipToken=Paged=TRUE%26p_ID=30&$top=5000", [Headers=[Accept="application/json"]]))

         

        I would make a list with = {0, 5000, 10000, 15000, 20000}  or something more dynamic for when the list gets bigger.  Convert that to a table and add a custom column that concatenates the list value in place of the 30 in red text above.  Then expand the table to get all your data.

         

        Sharepoint Lists can be slow.  This approach has saved much refresh time.

         

        If this works for you, please mark it as solution.  Kudos are appreciated too.  Please let me know if not.

        Regards,

        Pat

         

  • Hi mahoneypat ... I am using your approach to get the items using REST API and it works really fast. But I have published the file to the power bi service and I cannot set the schedule refresh because I have this error: "You can't schedule refresh for this dataset because the following data sources currently don't support refresh".

     

    Do you know how to overcome this problem?

     

    Thanks

    • CmdrKeene's avatar
      CmdrKeene
      Helper IV

      Someone in this forum said to look up a solution here: https://blog.crossjoin.co.uk/2016/08/16/using-the-relativepath-and-query-options-with-web-contents-in-power-query-and-power-bi-m-code/

       

      I did so, and after reading a number of posts on the subject I was able to get it working in PowerBI online service.  I won't bore you with a lot of details, here's just a simple modified M Code you need to use instead.

       

      What this does is uses a static URL for the first parameter of the Web.Contents() function, then uses a little known (apparently) 2nd parameter to actually pass the rest of the URL (relative path) and query/value pairs into the query string.

       

      You should be able to just modify the bold parts of this and be ready to go.  I modified this from the original on the blog post and it even has the original commented-out bits. You can remove those if you want. 

       

      I actually hard-coded most of the paths instead of using baseurl or other variables. I probably could have still used them but I wanted immediate success 🙂

       

      This has been an aboslute lifesaver to me. Now my list with 120K list items only takes 15 seconds to refresh.

       

       

      let
      baseurl = "https://TENANT.sharepoint.com/sites/SITE/_api/web/lists/GetByTitle('LIST')/",
      itemcount = Json.Document(Web.Contents("https://TENANT.sharepoint.com/sites/SITE/_api/web/lists/GetByTitle('LIST')/"&"ItemCount", [Headers=[Accept="application/json"]]))[value],
      skiplist = List.Numbers(0, Number.RoundUp(itemcount/5000), 5000),
      #"Converted to Table" = Table.FromList(skiplist, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
      #"Renamed Columns" = Table.RenameColumns(#"Converted to Table",{{"Column1", "Skip"}}),
      #"Changed Type" = Table.TransformColumnTypes(#"Renamed Columns",{{"Skip", type text}}),
      fieldselect = "&$top=5000", // all fields with no expansion
      //fieldselect = "&$top=5000&$select = Id,Title,Person,Date", // list desired fields (no expansion)
      //fieldselect = "&$top=5000&$select=Id,Title,Choice,LookupColumn/Title,LookupColumn/Project,LookupColumn/ProjectStatus,Date,Person/LastName,Person/FirstName,Person/EMail&$expand=LookupColumn,Person",
      //Custom1 = Table.AddColumn(#"Changed Type", "Items", each Json.Document(Web.Contents("baseurl" & "/items?$skipToken=Paged=TRUE%26p_ID=" & [Skip] & fieldselect, [Headers=[Accept="application/json"]]))),

      Custom1 = Table.AddColumn(#"Changed Type", "Items", each Json.Document(Web.Contents(
      "https://TENANT.sharepoint.com/sites/SITE/_api/web/lists/GetByTitle('LIST')/",
      [
      RelativePath="/items?$skipToken=Paged=TRUE%26p_ID=" & [Skip] & fieldselect,
      Headers=[Accept="application/json"]
      ]
      )
      )
      ),
      #"Expanded Items" = Table.ExpandRecordColumn(Custom1, "Items", {"value"}, {"value"}),
      #"Expanded value" = Table.ExpandListColumn(#"Expanded Items", "value")
      in
      #"Expanded value"

       

       

      • mahoneypat's avatar
        mahoneypat
        Microsoft Employee

        Glad you found that approach.  I was about to post a link to same article.

        Pat

         

    • Saide's avatar
      Saide
      Frequent Visitor

      This fixed it for me. It was not refreshing as PBI App was recognising it as dynamid data source. Replaced Custom1 step with this: Custom1 = Table.AddColumn(#"Changed Type", "Items", each Json.Document(Web.Contents(baseurl, [RelativePath = "/items?$skipToken=Paged=TRUE%26p_ID=" & [Skip] & fieldselect, Headers=[Accept="application/json"]]))), 

      It works all right for me

  • I'm loving this approach to use the paged response to get 5000-item chunks of list items quickly, but I'm wondering if anyone has gotten filters working with that in the same query? 

    I realize I can retrieve all items and then filter in the query editor, but I'd love to get a pre-filtered output from sharepoint.

    I tried adding $filter=Modified ge datetime'2021-05-08T09:59:32Z' to my URL string and at first I thought it was working, but then I realized my final results were showing a lot of duplication (the same items appearing many times). I think maybe the combination of filtering and pagination is causing the issue, but I'm not sure.

     

  • Hi,

     

    The problem I am having is our datasets have a column called FieldValuesAsText, which seems to contain nested records. Unfortunately, this is the data that I need to get to. 

     

    I believe this is a re-ocurring theme. Whilst all of the columns are available to be expanded with the sharepoint connectors, this does not appear to be the case with the api / json method.