We are under construction!
You may experience downtime, errors or visual oddities, but we anticipate all will be resolved by the end of the day.
You may experience downtime, errors or visual oddities, but we anticipate all will be resolved by the end of the day.
SQL: cross and delight of the IT revolution. If you've been in the game for more than a few years, chances are that you got your hands dirty with database queries once or thrice.
OneStream itself keeps data in a SQL database, and as much as we try to surface it through more human representations, sometimes the only way forward is to bust out a SELECT. The good news is that OneStream makes it pretty easy to do it; the bad news is that you're probably doing it wrong.
For simple scenarios, it's often enough to just create a Dashboard Data Adapter of type SQL, write your query, and connect it to grids or charts. Doesn't get easier than that!
You can even leverage adapters from a Business Rule, with a BRApi call:
' Whether the Adapter is in the Dashboard page under the System tab
Dim isSystemLevel As Boolean = False
' Post-7.3 reference to a Workspace
Dim workspaceID As Guid = BRApi.Dashboards.Workspaces.GetWorkspaceIDFromName(si, _
isSystemLevel, "MyWorkspace")
' Adapter Name
Dim adapterName As String = "MySQLAdapter"
' Result Table Name can be an empty string
Dim resultsTableName As String = "MyOutputTable"
' If your adapter uses Dashboard Parameters in the query,
' you can provide values for them in this object.
Dim customSubstVars As New Dictionary(Of String, String)
' customSubstVars.add("MyDashboardParameterName", "Some value")
Dim resultDataSet As DataSet = BRApi.Dashboards.Process.GetAdoDataSetForAdapter(si, _
isSystemLevel, workspaceID, adapterName, resultsTableName, customSubstVars)
' Tables are in a DataSet, which can be returned as it is.
' If you really want to return a single table, use .Copy:
' Return resultDataSet.Tables(0).Copy()
Return resultDataSet
However, there are situations in which you just don't want to keep a Data Adapter around;
and if you're only ever going to use it through rules, it's not the best-performing solution anyway, in terms of execution time.
For pure Rules, the best approach is to work directly with ADO objects. OneStream provides a number of utility methods to do it, on top of standard .Net functionality. We've seen some of them in a previous post, but for raw queries you might be familiar with this kind of approach:
This is faster, but it's an obsolete way to write SQL:
It's 2023, we have databases on mobile phones; we can do better.
Query Parameters have been available in SQLServer for a very long time. OneStream allows you to leverage them very easily:
Dim scenario As String = "Actual"
Dim entity As String = "South Houston"
Using dbConn As DbConnInfoApp = BRApi.Database.CreateApplicationDbConnInfo(si)
Dim myQuery As New Text.StringBuilder()
' In our query we just define placeholders, starting with @;
' note how there are no quotes anywhere.
myQuery.Append("SELECT * FROM vStageSourceAndTargetDataWithAttributes")
myQuery.Append(" WHERE Sn = @scenarioParam AND EtT = @entityParam")
' Then we construct a list of objects that provide values for those placeholders ...
Dim parameters As New List(Of DbParamInfo)
parameters.Add(New DbParamInfo("scenarioParam", scenario))
parameters.Add(New DbParamInfo("entityParam", entity))
' ... and pass that list to ExecuteSQL.
Dim result As DataTable = BRApi.Database.ExecuteSql( _
dbconn, myQuery.ToString, parameters, False)
Return result
End Using
This code is slightly more verbose, but:
"Why would we worry about user's Culture?", some of you might object after reading that last item. The reality is that such settings will influence rule execution, as mentioned in a previous blog. Trying to concatenate numbers into a raw SQL query would likely, sooner or later, incur into issues with decimal and thousand separators during String conversions, which vary by Culture.
By using query parameters we don't need to convert numbers into Strings, so this query will always work properly regardless of Culture:
' use Decimals everywhere, it's what OneStream cells also contain in .CellAmount
Dim someAmount as Decimal = 123.456
Dim myQuery As New Text.StringBuilder()
myQuery.Append("SELECT * FROM vStageSourceAndTargetDataWithAttributes")
myQuery.Append(" WHERE RawAmount < @myThreshold")
Dim parameters As New List(Of DbParamInfo)
parameters.Add(New DbParamInfo("myThreshold", someAmount))
Dim result As DataTable = BRApi.Database.ExecuteSql(dbconn, myQuery.ToString, parameters, False)
Similar considerations can be made for dates and times.
BONUS! Query parameters allow SQLServer to detect similar queries much more easily, resulting in large speedups on repeated calls! Why not be a hero and rewrite some old, performance-critical queries with parameters, and boasvt about your benchmark with peers and bosses...? (You can send me 10% of your payrise - I take credit cards.)
So now you know how to run SQL queries with OneStream in a way that is safe, modern, and efficient. What are you going to build with it? Amaze us with your parameterized creations in the Forums, for a chance to get some swag!
Blogs from Experts on OneStream
Concerning the last comment, I would use a different way to execute the SP :
Consider here that "p_airport_1", "p_airport_2" and "p_passengers" are string parameters.
Using dbConn As DbConnInfo = BRApi.Database.CreateApplicationDbConnInfo(si)
Dim SqlQuery As String = "EXEC SP_API_CLIMATIQ_TRAVEL_FLIGHT @p_airport_1=" _
& p_airport_1 & ", @p_airport_2= " & p_airport_2 & ", @p_passengers= " & p_passengers
Dim dt As DataTable = BRApi.Database.ExecuteSqlUsingReader(dbConn, sqlQuery, True)
End Using
Then I will execute the SP, but also get the output of the SP in a datatable.
Good topic, I don't see this done enough in the wild. I agree that its much more readable. I also feel a lot more comfortable when I'm implementing more secure code (re: sql injection) even in a low risk environment such as OS.
To go one step further on the readability front, I don't entirely understand why people use StringBuilder in these situations and I'd love to be shown the light if there is some. I only understand the benefits of StringBuilder when compared to making many concatenations to a string (e.g. via a loop).
I find this code much more readable. I also like that I can copy the SQL for testing without having to remove x.appends.= and quotes all over the place
' use Decimals everywhere, its what OneStream cells also contain in .CellAmount
Dim someAmount as Decimal = 123.456
Dim myQuery As String
myQuery = "SELECT * FROM vStageSourceAndTargetDataWithAttributes
WHERE RawAmount < @myThreshold"
Dim parameters As New List(Of DbParamInfo)
parameters.Add(New DbParamInfo("myThreshold", someAmount))
Dim result As DataTable = BRApi.Database.ExecuteSql(dbconn, myQuery, parameters, False)
Feel free to send swag 😁
I'm referring to an output parameter of the DB Stored procedure.
So in the example above you are only sending parameters into the stored procedure, but stored procedures can return parameters back out. How can I pull the OUTPUT parameter and use it in my business rule?
Dim SqlQuery As String = "EXEC SP_API_CLIMATIQ_TRAVEL_FLIGHT @param1_IN=param1Val, @param2_IN=param2Val, @param3_OUT"
StringBuilder is generally meant for any string concatenation, not just in a loop - because it doesn't create new objects in memory with each concatenation, it should reduce memory-waste. It also works well for situations where whitespace in the String is significant, but you don't want to break code indentation with a multiline raw String.
I agree that it's unnecessary in this post, but I included it to raise awareness. Good comment!
Can we use the above demonstrated Parameters to execute a Stored Procedure that expects Parameters?
When I tried, I get an error message that "Procedure or function 'sp_BDGT_NEROffering' expects parameter '@BudgetDate', which was not supplied. I tried with null parameter and a date as well.
Appreciate any comments or pointers to troubleshoot.
That worked. Thanks very much!
Using the code sample from above
Using dbConn As DbConnInfo = BRApi.Database.CreateApplicationDbConnInfo(si)
Dim SqlQuery As String = "EXEC SP_API_CLIMATIQ_TRAVEL_FLIGHT @p_airport_1=" _
& p_airport_1 & ", @p_airport_2= " & p_airport_2 & ", @p_passengers= " & p_passengers
Dim dt As DataTable = BRApi.Database.ExecuteSqlUsingReader(dbConn, sqlQuery, True)
End Using
Is there a way to reference and use an OUTPUT parameter from a database stored procedure, in a business rule?
I haven't been in a situation where I need to get a param back from the stored parameter 😞
But I will look for a way to do it !