CSQRY from the .Net API
Introduction
Generally in these blog posts I talk about how to access the Sage 300 ERP data through our various APIs that go through our Business Logic Views. This is the only way to update the data in a supported manner, but often for reporting needs something more is hoped for. We store our data in standard databases which are accessible via various APIs like Entity Framework, ADO.Net or ODBC. The downside is that you need a connection string to establish a connection, which means you need a SQL Server login and password. Often for Sage 300 add-in solutions there can be a lot of resistance from local IT departments on providing these. Basically anywhere these are given out is a possible security risk.
In this article I’ll show how to execute a custom SQL Query through a special Business Logic View to get at data for reporting purposes. This means you can use the full power of SQL Server to extract the exact data you want. This article uses our .Net API which I have blogged on quite a bit with an introductory article here.
The project for the sample program is located in both zip, and folder structure on Google Drive here. It’s the csqrydemo one.
CSQRY
CSQRY is the View CS0120. It basically takes an arbitrary SQL Query as the parameter to its Browse method and then returns the records via calls to Fetch. It also returns the attendant meta-data for the result set using the regular View meta-data APIs (which I blogged on here).
The problem with this is that the meta-data is read and utilized by the .Net API when you first open the View. At this point it build the fields list and various other structures and then you use these in-memory versions afterwards without the .Net API inquiring of the meta-data again from the Views. So when you open CSQRY there is no results set and so it returns that there are zero fields in the View. Then when you execute the Browse statement, it then populates all its meta-data based on the results set returned form the database server, but the .Net API still thinks there are zero fields.
It turns out, that we added a “hack” to the .Net API for situations like this. You can get the .Net API to refresh all the meta-data from the View by calling the terribly intuitive:
view.InternalSet(256);
You might then wonder, what other magic numbers can I pass to the InternalSet method? It turns out this is the only value that InternalSet acts on. But I guess in the future if we need any other sort of “hacks” this would provide a method to put them. Still a more easily understood RefreshMetaData() method would have been helpful.
Sample Program
For the sample program, let’s get the top 6 customers by billing from the AROBL table. Basically summing all the invoices, credit notes and debit notes for a given fiscal year. This is a nested query to get the data, then get the top six by sum. This is the sort of query you might issue to provide, perhaps a dashboard type KPI display.
Here is the routine from the sample program to refresh the list control by issuing the query and displaying the results:
private void refreshList() {
// Issue the SQL Query
// TXTTRXTYPE = (1, 2, 3), Document Type (Invoice, Debit Note, Credit Note)
csQry.Browse(@"select top 6 c.IDCUST, c.NAMECUST, sum(c.AMTINVCHC) as 'INVOICED' from (select a.IDCUST, b.NAMECUST, a.AMTINVCHC from AROBL a, ARCUS b where a.IDCUST = b.IDCUST and a.TRXTYPETXT in (1, 2, 3) and a.FISCYR = 2020) c group by c.IDCUST, c.NAMECUST order by INVOICED desc", true);
// Refresh the View's MetaData csQry.InternalSet(256);
listView1.Clear(); listView1.View = System.Windows.Forms.View.Details; listView1.Columns.Add("Cust ID", 100, HorizontalAlignment.Left); listView1.Columns.Add("Customer Name", 200, HorizontalAlignment.Left); listView1.Columns.Add("Amount", 100, HorizontalAlignment.Right);
while (true == csQry.Fetch(false)) { var row = new ListViewItem(new string[]
{ csQry.Fields[0].Value.ToString(), csQry.Fields[1].Value.ToString(), csQry.Fields[2].Value.ToString() }); listView1.Items.Add(row); } }
For a bit more information on the structure of the A/R tables used, check out this blog post.
Performance
It may appear that SQL Server can execute this query pretty quickly, for instance in SAMINC in less than a second. But this is still quite an expensive query. Look at the execution plan below:
It might be hard to read but the query needs to do index scans on both AROBL and ARCUS along with a couple of sorts. So we might expect that although this looks good on sample data, it will take quite a bit more time on larger real customer data sets.
Data Types (2016/02/09)
If you return calculated values then we don’t know the data type and might return something unexpected. You can control this by providing some hints in your query to tell us what data type you want. Something like:
SELECT SUM(AMTINVCHC) /* ACCPAC TYPE 1 FT_BCD 10 3 */ …
The format of the comment is /* ACCPAC TYPE fieldnumber type length precision */ where the precision is only needed for type FT_BCD. The type can be FT_BCD, FT_BTYE, FT_DATE, FT_LONG, FT_TIME, FT_BOOL, FT_CHAR, FT_INT, FT_REAL.
As another example:
SELECT UPPER(SHPTOSTTE) /* ACCPAC TYPE 1 FT_CHAR 30 */ as PROV from ARIBH
Summary
CSQRY gives a fairly easy to use way to execute general SQL statements through the Sage 300 API. This way you don’t need additional database credentials and you don’t need to use an alternate API like ODBC. At the same time beware that doing general SQL statements isn’t a solution to all performance problems.
Written by smist08
February 7, 2015 at 11:31 pm
Posted in sage 300, Software Development Lifecycle
Tagged with .net api, 300, accpac, Business Logic View, csqry, ERP, meta-data, query performance, Sage, sage 300, sage 300 erp, sage erp, sql, sql query, SQL Server
12 Responses
Subscribe to comments with RSS.
Leave a comment Cancel reply
This site uses Akismet to reduce spam. Learn how your comment data is processed.
[…] Introduction Generally in these blog posts I talk about how to access the Sage 300 ERP data through our various APIs that go through our Business Logic Views. This is the only way to update the dat… […]
CSQRY from the .Net API | Sage 300 ERP | Scoop...
February 7, 2015 at 11:51 pm
Reblogged this on Dinesh Ram Kali..
dineshramitc
February 10, 2015 at 4:44 am
[…] Last week, we looked a bit at using the Sage 300 ERP .Net API to do a general SQL Query which could be used to optimize calculating a KPI. In this case you could construct a SQL statement to do exactly what you need and optimize it nicely in SQL Management Studio. In some cases this will be much faster than the Sage 300 Views, in some cases it won’t be if the business logic already does this. […]
On Calculating Dashboards | Stephen Smith's Blog
February 14, 2015 at 7:25 pm
[…] on, I did blog on some of the technical aspects that went into how these were implemented using CSQRY and then on caching and calculating these. For this on premise release, we aren’t implementing […]
The Sage 300 2016 KPIs | Stephen Smith's Blog
September 6, 2015 at 11:06 am
Hi Mr, Smith,
Can you advise whether it is possible to get the my user lists with corresponding email?.
Please advise.
Thanks,
Lyn
Lyn
September 10, 2015 at 10:50 am
Not using CSQRY, since the User records aren’t stored in the database (because they are needed to sign on to the database).
smist08
September 10, 2015 at 1:35 pm
You could use the AS0003 view to get this information via a macro, export the Users or use the Users report.
smist08
September 11, 2015 at 5:12 am
[…] Introduction Generally in these blog posts I talk about how to access the Sage 300 ERP data through our various APIs that go through our Business Logic Views. This is the only way to update the data in a supported manner, but often for reporting needs something more is hoped for. We store our data… […]
CSQRY from the .Net API | Sage 300 | Scoop.it
February 10, 2016 at 3:55 pm
[…] could do a CSQRY call and select based on the audit stamps that are newer than our last clock tick (sync time). […]
Synchronizing Data with Sage 300 | Stephen Smith's Blog
April 18, 2016 at 10:09 pm
Hi Stephen, I am in a bit of a jam trying to use the CS0120 superview to execute a SQL transaction
If I use a transaction that has two statements in it, only the first statement executes
In the example below only the UPDATE statement executes and I am not able to retrieve the result
csUpdateW.Browse “BEGIN TRANSACTION; ” & _
” UPDATE OWID SET LASTVALUE = LASTVALUE + 1; ” & _
” SELECT LASTVALUE FROM OWID; ” & _
“COMMIT TRANSACTION;”, True
csUpdateW.InternalSet 256
Do While csUpdateW.Fetch
resultval = csUpdateW.Fields(0).Value
Loop
csUpdateW.Close
I am trying to use the superview instead of an ADO connection for various reasons.
Any suggestions are appreciated, thank you very much.
Ilona
April 11, 2018 at 8:56 pm
Generally Sage doesn’t like you going behind the regular Views to do updates since this bypasses the regular validation and integrity checks. The intent of this View was to provide a way to do sophisticated data queries. Not sure how its handling your case. You might want to try running SQLTrace to see what is really going to SQL Server.
smist08
April 11, 2018 at 11:23 pm
Thanks Stephen.
After additional testing it turns out that both select statement and update statement execute but the Fetch reads the results of only the first statement. So if I put the select statement first and update second I get what need.
I usually use the view for complicated queries used in reporting but in this case I was asked by a third party to use the particular transaction to update a field and since the rest of the projects uses the view I wanted to use the view for the update as well. Thanks again.
Ilona
April 13, 2018 at 1:52 pm