Blog Post

Fabric platform Community Blog
3 MIN READ

One button, fifty rows: bulk write-back from Power BI to Fabric SQL using a UDF | Part-2

FarhanJeelani's avatar
FarhanJeelani
Icon for Super User rankSuper User
1 month ago

Multi-select writeback with Translytical Task Flows, User Data Functions and a column-level audit trail.

5. The report page

Enable Translytical task flows and Text slicer under Preview features in Desktop first, or the Data function action type will not appear on the button at all.

ElementSetup
Hotel slicerHotelCode, multi-select on. This is what drives single vs bulk.
Code type slicerCodeType, single select on.
Major group inputText slicer, no field bound. It's an input box, not a filter.
Minor group inputText slicer, no field bound.
Table visualHotelCodeSetup, showing current state.
Audit visualvw_HotelCodeSetupHistory, sorted by ChangedDateUtc descending.
Apply buttonAction → Data function.

Set both text slicers to Edit interactions → None against every other visual. Otherwise typing "Rooms Division" into the input box filters the report down to nothing and the user thinks the data vanished.

Button parameter mapping:

Function parameterBind to
hotelCodesConditional value → [Selected Hotel Codes]
codeTypeConditional value → [Selected Code Type]
majorGroupMajor group text slicer
minorGroupMinor group text slicer

Turn on Refresh the report automatically and Auto clear on the button, and bind the button text to [Apply Button Label].

6. Things I learned the hard way

Test as a non-author. Report access and function execute permission are separate grants in Fabric. If they are out of sync, the user gets an opaque error, and the people most likely to hit it are the least equipped to interpret it. Grant your writeback audience Execute on the User Data Functions item explicitly, then log in as one of them and click the button.

Deployment pipelines do not rebind the button. The button stores a hard reference to a specific workspace, function set, and function. Promote dev to test to prod and it is still pointing at dev, even when an identically named function exists in the target workspace. It will happily write dev data from your prod report. Repoint it manually after every deployment, and put that on the release checklist.

Decide your insert policy deliberately. @AllowInsert = 1 means editing a code type for a hotel that doesn't currently have one creates the row. That is convenient, and it is also a way for typos to become master data. Pass 0 if setup rows should only ever originate from your master data process.

The analytics endpoint thing. Worth repeating because it presents as "the button does nothing", which sends you looking in entirely the wrong place.

7. Audit queries that earn their keep

-- Everything one button click did SELECT * FROM audit.vw_HotelCodeSetupHistory WHERE BatchId = '<batch-guid>' ORDER BY HotelCode; -- Full history for one hotel and code type SELECT ChangedDateUtc, ChangedBy, ActionType, ChangeDescription FROM audit.vw_HotelCodeSetupHistory WHERE HotelCode = 'HTL001' AND CodeType = 'FBFOOD' ORDER BY ChangedDateUtc DESC; -- Who is making bulk changes SELECT ChangedBy, COUNT(DISTINCT BatchId) AS Batches, COUNT(*) AS ColumnChanges FROM audit.HotelCodeSetupAudit WHERE ChangedDateUtc >= DATEADD(DAY, -30, SYSUTCDATETIME()) GROUP BY ChangedBy ORDER BY ColumnChanges DESC;

The view builds a readable line per change:

CONCAT(a.ColumnName, ': ', ISNULL(a.OldValue, '(blank)'), ' -> ', ISNULL(a.NewValue, '(blank)')) AS ChangeDescription

So the audit visual on the report reads "MajorGroup: Food & Bev -> Food & Beverage" rather than making the user mentally diff two columns.

What's next

Because the audit stores old and new values per column and groups them by BatchId, rollback is a replay of OldValue for a given batch. That is the obvious third function to build once the main flow has been stable for a while. I have deliberately not built it yet, on the grounds that an undo button written in week one tends to be the thing that needs undoing.

Wrapping up

The pattern generalises well beyond hotel codes. Any reference or mapping table that business users maintain, and that currently lives in a spreadsheet somebody emails around, is a candidate. The parts worth copying are the ones that are not obvious from the tutorials:

  • Surrogate PK plus a unique constraint on the real business key, so bulk editing is possible at all.
  • Audit inside the procedure rather than in a trigger, so the identity is the report user.
  • COALESCE in the update so blank means "leave alone", not "erase".
  • ISFILTERED in the DAX so an empty slicer can't rewrite everything.
  • A hard cap on selection size in the function.

Happy to answer questions in the comments. If you have built something similar and solved the deployment pipeline rebinding problem more elegantly than "remember to do it manually", I would genuinely like to hear it.

PBIX File & full scripts: 01_schema_and_procs.sql and 02_user_data_function.py are attached below.

Updated 1 month ago
Version 2.0
No CommentsBe the first to comment