OAX and DAX
OAX, Open Analytical Expressions, is the language of OpenBI’s own engine. It takes DAX as its reference, so Power BI and Analysis Services models and their measures work in OpenBI as they are, and it adds functions DAX doesn’t have. This page is for analysts and IT.
What OAX is
DAX is the language of Microsoft’s Power BI and Analysis Services, run by Microsoft’s engine. OpenBI has an engine of its own, written in C, and its language is OAX. OAX uses DAX as its reference: the same syntax, the same functions under the same names, the same filter and row contexts. OpenBI’s conformance tests check its answers against Power BI Desktop’s, case by case, with Power BI Desktop as the benchmark.
On top of DAX’s functions, OAX has functions of OpenBI’s own: survival analysis, predictions and forecasts, optimisation, quantitative finance, networks, statistics as tables, images and more. OAX functions lists every function, with OpenBI’s own marked OpenBI.
What works as it is
- Models from Power BI and Analysis Services
- Imported from an open Power BI Desktop, a
.pbip,.pbitor.bimfile, a TMDL folder or an Excel data model: their measures, calculated columns, calculation groups and roles’ row filters run in OAX unchanged. See Import from Power BI. - Power BI projects kept in Power BI
- A
.pbipyou keep working in Power BI Desktop stays Power BI’s: a measure written in OpenBI works in both, as long as it uses DAX’s functions only. OpenBI’s own functions are for OpenBI projects. - Live connections
- On a model that lives on Analysis Services, Power BI or Fabric, the server’s own engine answers each query, in its DAX, and your own measures go with the queries OpenBI sends it. Most of OpenBI’s own functions work there too: the server computes the rows they read, and OpenBI runs the function on them. See Live connections.
- Older servers
- A server of an older version lacks newer DAX: SQL Server 2016 Analysis Services has no
TREATAS,INor table constructor ({ }). OpenBI’s report pages notice the first refusal and ask such a server the older way. - Measures and queries
- Measures, calculated columns and row filters are OAX, and so are queries, written as DAX writes them:
EVALUATE,DEFINEwithMEASURE,VAR,COLUMNandTABLE,ORDER BYandSTART AT. The OAX view runs them (see OAX queries and performance), and agents run them withdax_query. - Dates and times
- A date-time is held as Power BI Desktop holds it, to 1/300 of a second: a value loaded from a source, and one an expression of constants works out, so date-time arithmetic, filters on columns with fractions of a second and
FORMATgive Desktop’s answers, and the studio’s grids show the fractions.NOW,TODAY,UTCNOWandUTCTODAYread the clock once for the whole query;UTCNOWandUTCTODAYin UTC. - Excel
- Analyze in Excel connects Excel’s PivotTables to OpenBI’s engine as to Analysis Services, in the studio and on OpenBI Server. Excel asks in MDX, and OpenBI answers with OAX queries over the model, so its measures work in Excel as in Power BI. See Excel in the studio and Excel connections on the server.
What OAX adds
OpenBI Studio 0.3.74 has 69 functions of OpenBI’s own. A measure that uses one works in OpenBI only: Power BI and Analysis Services don’t have them. By family, with a few of each:
| Family | Some of its functions |
|---|---|
| Survival analysis | SURVIVAL.CURVE, SURVIVAL.TEST, SURVIVAL.COX, SURVIVAL.COX.PREDICT: how long until an event, as R’s survival package computes it |
| Predictions and forecasts | PREDICT: the scores, or the forecast, of a prediction trained in the studio (see Prediction) |
| Operations research | OPTIMIZE, OPTIMIZE.VALUE, ROUTES, SCHEDULE: plans solved again for a what-if, vehicle routes and schedules |
| Quantitative finance | QUANT.BOND.PRICE, QUANT.OPTION.PRICE, QUANT.CURVE, VALUATION, MONTECARLO: bonds, options, curves and simulations with QuantLib |
| Networks | NETWORK.NODES, NETWORK.COMMUNITIES, NETWORK.PATH, NETWORK.CYCLES: the network the rows in view make |
| Many variables at once | PCA.COMPONENTS, KMEANS, UMAP: principal components, clusters and maps of the rows |
| Statistics as tables | DECOMPOSE, HISTOGRAM, BOXPLOT, TRENDLINE, CORREL: a series’ trend and season, distributions, trend lines with their bands |
| Images and colours | IMAGECOLORS, IMAGECOLOR, COLORNAME: the colours of a picture, and a colour’s name |
Where the studio still says DAX
OpenBI Studio says OAX wherever it names OpenBI’s language: the OAX view and OAX query on the ribbon, Format OAX on the Measure tools tab, the formula bar, notebooks’ OAX steps, filters’ OAX conditions and a visual’s Show OAX. It says DAX where it means Microsoft’s: a live server’s own measures and queries, the Analysis Services source, and SQLBI’s DAX Studio and DAX Formatter. The tools agents use keep their names, dax_query, dax_show, dax_compare, dax_benchmark and dax_format, because agents know DAX; they all take OAX, DAX’s functions and OpenBI’s alike. Where these pages name a label or a tool, they use its real name.
Known differences
Where Power BI Desktop and an older Analysis Services answer differently, OpenBI answers as Power BI Desktop. The differences its tests record, on a model OpenBI’s engine runs (on a live connection the server answers, with its own behaviour):
| What | OpenBI | Power BI and Analysis Services |
|---|---|---|
| Fixed decimal (Currency) arithmetic | Each row’s result rounded to four decimals, as DAX’s rules say: Power BI Desktop’s answer when its formula engine computes it. | Desktop’s storage engine may round otherwise, depending on the query, so a total can differ in its last decimals. Sum first and then multiply, or CONVERT to DOUBLE, and both agree. |
| Sums of decimal numbers | A sum that comes to zero is zero. | Analysis Services can leave a tiny residue, such as 2-39, so a test for zero can differ. |
Two-digit years in DATE, and the year 100 | As Power BI Desktop: DATE(49, 1, 1) is in 2049, DATE(100, 1, 1) in the year 100. | SQL Server 2016 Analysis Services adds 1900 to every year below 1900. |
APPROXIMATEDISTINCTCOUNT | The exact distinct count. | Power BI refuses it on an imported model: it estimates, for DirectQuery sources. |
HASH | A 64-bit hash with the same properties. | Power BI’s algorithm isn’t published, so its numbers differ. |
EVALUATEANDLOG | Returns its value; writes no log. | Power BI Desktop also logs the value, for tracing. |
SAMPLEAXISWITHLOCALMINMAX, SAMPLECARTESIANPOINTSBYCOVER | The whole table up to the size asked; past it, OpenBI’s own bins. | Power BI Desktop’s bins past the size aren’t published, so its samples may differ. |
USERNAME, USEROBJECTID | The same text as USERPRINCIPALNAME: the user the query runs as (on OpenBI Server, the person’s e-mail). | Each its own form of the user’s identity. |
| Date-times worked out from constants | As Power BI Desktop: held to 1/300 of a second, so (DATE(2026, 11, 2) + TIME(9, 55, 26)) - (DATE(2026, 11, 2) + TIME(8, 55, 26)) is exactly 1/24, and 24 times it is 1. | SQL Server 2016 Analysis Services doesn’t round them, and answers 0.99999999994. |
Differences kept on purpose
A few queries Power BI Desktop refuses, or answers in an order it doesn’t say, have a well-defined answer. OpenBI gives it, on purpose:
| What | OpenBI | Power BI Desktop |
|---|---|---|
TOPNSKIP with a skip | Sorts the rows and skips as many as asked, over any table and by any number of order columns. | Skips rows only over a model table ordered by one column; over a variable’s rows or VALUES, or ordered by two columns, it refuses the query. |
LOOKUP’s INFERRED | Read as EXPLICIT, the default. | INFERRED also clears the filters of the columns the visual sorts and groups the search columns by. In every visual shape tried, Power BI Desktop answered the same both ways. |
CONCATENATEX without an order | A column’s values from the smallest, a table’s rows as they were loaded. | The order its storage keeps them in, which follows the data only in part and can’t be told from the model. Give CONCATENATEX an order, its fourth argument, whenever the order matters: then both agree. |
A TRUE/FALSE filter in SUMMARIZECOLUMNS | An OAX convenience, read as CALCULATE reads one: SUMMARIZECOLUMNS ( Products[Category], Products[Product] = "Pils", "Liters", [Liters sold] ) filters with FILTER ( ALL ( Products[Product] ), Products[Product] = "Pils" ). On a live connection, OpenBI sends the server that FILTER form. | Refuses a TRUE/FALSE expression as a filter of SUMMARIZECOLUMNS. In a measure or query that must run in Power BI too, write the FILTER ( ALL ( … ), … ) form yourself. |
PATH over a parent that is no row’s id | The parent is taken as the top, and the path begins with it: x|d for a row d whose parent x is no row’s id. The model loads, with a warning that names the column and its first such rows; on OpenBI Server, the refresh’s history says so too. | Refuses such a model: its refresh fails until the parent is put right. |
Refused, as in Power BI
Where Power BI Desktop refuses a query because its answer would contradict the function’s own definition, OpenBI refuses it too, with an error that says what to change:
KEEPFILTERSonly where Power BI Desktop takes it: inCALCULATE’s andCALCULATETABLE’s filters, and around the table an iterator goes through, such asSUMX ( KEEPFILTERS ( ALL ( Products[Product] ) ), [Liters sold] ). Used as a table on its own, inEVALUATE, a variable orCOUNTROWS, it is an error.NONVISUALon a column theSUMMARIZECOLUMNSdoesn’t group by is an error: such a filter would change the values, whichNONVISUALsays it doesn’t.USERELATIONSHIPonly amongCALCULATE’s andCALCULATETABLE’s filters, as itself: inside another function it is an error.
A report whose visual sends such a query shows that visual’s error instead of a number. Each function’s page shows its refusals as examples.