How to work with record-variables (version 3)?

Administrator
Member, Moderator, Administrator Posts: 2,506
How to work with record-variables?
http://www.mibuso.com/howtoinfo.asp?FileID=22
Discuss this How To here.
http://www.mibuso.com/howtoinfo.asp?FileID=22
Discuss this How To here.
0
Comments
-
Thanks Kriki!
A real eye opener. =D> =D>
Never realised that the following code will do two select statements:TheTable.GET(...); TheTable."Some Field" := 'Some Value'; TheTable.MODIFY(FALSE);
And the following only one!TheTable.LOCKTABLE; TheTable.GET(...); TheTable."Some Field" := 'Some Value'; TheTable.MODIFY(FALSE);
I will keep it in mind from now on!Reijer Molenaar
Object Manager0 -
SQL Server works on sets of records and not record by record.
NAV on the other hand works record by record.
SQL must emulate the record by record approach of the native DB. How can SQL do that : by using cursors.
The problem with cursors is that they are very heavy for SQL because SQL has to maintain them while working on it. So if you can avoid it (by using FINDFIRST,FINDLAST,ISEMPTY) you should do it.
If you ask pure SQL specialists about cursors, they will say to you NEVER to use them because they are very bad for performance (and that is true), but NAV doesn't have any other possibility.Regards,Alain Krikilion
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!0 -
Thank you, kriki!0
-
Great post Kriki.
I’ll start by saying I also learned something reading your entry. I didn’t know that a CLEAR(Rec) also respected InitValue properties. And that an array of temporary records only resulted in ONE temporary table. However; I have a FEW comment ;-)
I think you need to mention that your description of most of the different functions assumes that SQL is used. A lot of the explanations are not true for Native, but I guess you are only working with SQL by now :-)
You should also explain that when you say a function returns several records, it actually means that the SQL server returns them to the NAV client, which then caches them and hope you will ask for the next one without changing filters or key or locking first.
I found a few small mistakes in your list: (To the best of my knowledge - at least)
“FIND('>') / FIND('<'): This bases itself on the values in the primary key to get the next or previous record, it ALSO considering the filters. Better use NEXT or NEXT(-1).”
Should say “…values in the current key…”
“ISEMPTY: This is THE statement to use if you want to check if there is at least one record in the (filtered) table.”
Please note, that Native DB uses the current key, so please set a proper key first to be DB independent.
“WRONG: This will give you 1 record at a time. \IF TheTable.FIND('-') THEN\REPEAT\ ...\UNTIL TheTable.NEXT = 0;”
I’m not sure, but I don’t’ think so. At least it used to it returns less records than the FINDSET command, but the number of records used to be variable. I guess it depended on recordssize, but it was typically around 25 records. I’m quite sure it used to say this in the Client Monitor as well, but for some reason it doesn’t. Using the profiler it seems it is doing a TOP 1.
“CORRECT: This will give you the first N records in one go, and after that 1 by 1.\IF TheTable.FINDSET THEN\REPEAT\ ...\UNTIL TheTable.NEXT = 0;”
Again: No – I don’t think so. I tried doing a FIND(‘-‘) followed by 100 times NEXT. This resulted in a SELECT (without TOP or FAST) and four times a FETCH API_CURSOR. This is almost as I expected, but I don’t know how we get to see the actual SQL statements it executes.
“First and for all some general advice for writing to the DB\...”
You forgot the most important one: “Never ever do a COMMIT unless all data is consistent and it is acceptable that only the queued changes are committed, since an error (logical or physical) can appear half a millisecond later and thereby stopping the remaining of the code.”
“CORRECT in case most of the time you need to change the record to avoid a second SELECT in SQL..”
I guess you forgot the “IF to be changed THEN BEGIN” in the code below.
“Method 2:\// now loop the temptable. This code retrieves the record again (with EXCLUSIVE-LOCK!) and then changes it. If the record was changed between the first read and now, it will NOT generate an error on the MODIFY because you will have received the latest version.”
I really don’t like this one. You risk the conditions from the first loop isn’t fulfilled anymore and thereby making inconsistent data. You should at least repeat the “IF (Record has to be changed) THEN BEGIN” again.
“But in C/AL we can't use SQL-statements (or we must use ADO), so there is another way. I advice to always use this way to do some summing in C/AL.”
You have an confusing indention under your “IF NOT tmpGLEntry.SETCURRENTKEY("Gen. Prod. Posting Group") THEN ;” All the lines below should be 4 chars to the left :-)
But besides these issues I think it is a very good walkthrough which I’ll show my co-workes as well. Keep up the good work. =D>Regards
Peter0 -
Thanks for your remarks.
I'll put them in the new version.
Under here I put my answers to your remarks.
I think you need to mention that your description of most of the different functions assumes that SQL is used. A lot of the explanations are not true for Native, but I guess you are only working with SQL by now :-)
=> CORRECT. It is specially for SQL. I wrote that in the title in the beginning but later I changed the title to make it somewhat shorter and forgot to put it in the text. I now put it in the text.
You should also explain that when you say a function returns several records, it actually means that the SQL server returns them to the NAV client, which then caches them and hope you will ask for the next one without changing filters or key or locking first.
=> CORRECT. In some places I didn't specifically write that
I found a few small mistakes in your list: (To the best of my knowledge - at least)
“FIND('>') / FIND('<'): This bases itself on the values in the primary key to get the next or previous record, it ALSO considering the filters. Better use NEXT or NEXT(-1).”
Should say “…values in the current key…”
=> I made that more clear
“ISEMPTY: This is THE statement to use if you want to check if there is at least one record in the (filtered) table.”
Please note, that Native DB uses the current key, so please set a proper key first to be DB independent.
=> CORRECT but The How To is specially for SQL.
“WRONG: This will give you 1 record at a time. \IF TheTable.FIND('-') THEN\REPEAT\ ...\UNTIL TheTable.NEXT = 0;”
I’m not sure, but I don’t’ think so. At least it used to it returns less records than the FINDSET command, but the number of records used to be variable. I guess it depended on recordssize, but it was typically around 25 records. I’m quite sure it used to say this in the Client Monitor as well, but for some reason it doesn’t. Using the profiler it seems it is doing a TOP 1.
=> I think it does, otherwise what is the reason of the existence of the FINDSET command (and why did they change the recordset from 500 to 50 in NAV2009?
“CORRECT: This will give you the first N records in one go, and after that 1 by 1.\IF TheTable.FINDSET THEN\REPEAT\ ...\UNTIL TheTable.NEXT = 0;”
Again: No – I don’t think so. I tried doing a FIND(‘-‘) followed by 100 times NEXT. This resulted in a SELECT (without TOP or FAST) and four times a FETCH API_CURSOR. This is almost as I expected, but I don’t know how we get to see the actual SQL statements it executes.
=> The actual SQL statement is "FETCH API_CURSOR". NAV opened a cursor at the beginning and now it is fetching the records 1 by 1. This means that NAV each time sends a command to fetch the next record and then SQL sends 1 record. With FINDSET, SQL sends immediately 500 (or 50) records on FINDSET command and after those record, it goes 1 by 1.
“First and for all some general advice for writing to the DB\...”
You forgot the most important one: “Never ever do a COMMIT unless all data is consistent and it is acceptable that only the queued changes are committed, since an error (logical or physical) can appear half a millisecond later and thereby stopping the remaining of the code.”
=> I found that sooooooo obvious I didn't write it..... But you are right!
“CORRECT in case most of the time you need to change the record to avoid a second SELECT in SQL..”
I guess you forgot the “IF to be changed THEN BEGIN” in the code below.
=> OOOOPPPPPPPPPPSSSSSS
“Method 2:\// now loop the temptable. This code retrieves the record again (with EXCLUSIVE-LOCK!) and then changes it. If the record was changed between the first read and now, it will NOT generate an error on the MODIFY because you will have received the latest version.”
I really don’t like this one. You risk the conditions from the first loop isn’t fulfilled anymore and thereby making inconsistent data. You should at least repeat the “IF (Record has to be changed) THEN BEGIN” again.
=> TRUE, Depending on the need it might be necessary to add the test again or not at all.
“But in C/AL we can't use SQL-statements (or we must use ADO), so there is another way. I advice to always use this way to do some summing in C/AL.”
You have an confusing indention under your “IF NOT tmpGLEntry.SETCURRENTKEY("Gen. Prod. Posting Group") THEN ;” All the lines below should be 4 chars to the left :-)
=> Actually, the indentation is correct. The idea is that I try to use a key if it exists. If it doesn't exist, I try the second key. If that second key doesn't exist, never mind about keys. Just use the primary key.
I could also rewrite that part as follows, but I think the original is better:
tmpGLEntry.reset;
// try to get a good key for the filters
CASE TRUE OF
tmpGLEntry.SETCURRENTKEY("Gen. Bus. Posting Group"): BEGIN END;
tmpGLEntry.SETCURRENTKEY("Gen. Prod. Posting Group"): BEGIN END;
END;
// I filter on the records for which I want to group the records
tmpGLEntry.setrange("Gen. Bus. Posting Group",recGLEntry."Gen. Bus. Posting Group");Regards,Alain Krikilion
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!0 -
Thanks for the updates, but I still have a few comments:
>No. of records returned by FIND(‘-‘)
You might be right, but I will still claim that it used to do a FAST 25 in some older versions. :-)kriki wrote:=> The actual SQL statement is "FETCH API_CURSOR". NAV opened a cursor at the beginning and now it is fetching the records 1 by 1. This means that NAV each time sends a command to fetch the next record and then SQL sends 1 record. With FINDSET, SQL sends immediately 500 (or 50) records on FINDSET command and after those record, it goes 1 by 1.kriki wrote:Actually, the indentation is correct. The idea is that I try to use a key if it exists. If it doesn't exist, I try the second key. If that second key doesn't exist, never mind about keys. Just use the primary key.
…I could also rewrite that part as follows, but I think the original is better:
The code you made is fine, but the indention confused me a bit. If I only look at the indention (and ignore ; and BEGIN END) it actually only does something if none of the keys exists.
PS: My tests were done with NAV5SP1-Upd1.Regards
Peter0 -
Dear all,
Which will give the better performance ? Please explain if possible.IF TheTable.FINDFIRST THEN BEGIN ....................; ....................; END;
ORIF TheTable.FIND('-') THEN BEGIN ....................; ....................; END;
Thanks with regards!Now or Never0 -
Hi navuser1,
Based on Kriki's post, I think it depends on the conditions.
FINDFIRST: Use it when you only need ONE record.
FIND('-'): Use it when you need the ENTIRE records, instead of only one. Hence, you MUST use it when using with loop.
If you want only check whether there are any records, prefer to use the best practice:IF NOT TheTable.ISEMPTY THEN BEGIN ....................; ....................; END;
Dear all, please CMIIW :oops:Regards,
Andwian0 -
Andri Arie Wianto wrote:Hi navuser1,
Based on Kriki's post, I think it depends on the conditions.
FINDFIRST: Use it when you only need ONE record.
FIND('-'): Use it when you need the ENTIRE records, instead of only one. Hence, you MUST use it when using with loop.
If you want only check whether there are any records, prefer to use the best practice:IF NOT TheTable.ISEMPTY THEN BEGIN ....................; ....................; END;
Dear all, please CMIIW :oops:
Dear Andri
FINDFIRST,FIND('-'),ISEMPTY are clear to me. But I have seen the code(given below) in the base database in many places. I think that the function FIND('-') is the best for that particular cases. Please CMIIW.IF TheTable.FINDFIRST THEN BEGIN ....................; ....................; END;
Now or Never0 -
navuser1 wrote:FINDFIRST,FIND('-'),ISEMPTY are clear to me. But I have seen the code(given below) in the base database in many places. I think that the function FIND('-') is the best for that particular cases. Please CMIIW.
IF TheTable.FINDFIRST THEN BEGIN ....................; ....................; END;
Check the section called You want ONY the first/last record (if it exists), but NEVER more.Regards
Peter0 -
Andri Arie Wianto wrote:FIND('-'): Use it when you need the ENTIRE records, instead of only one. Hence, you MUST use it when using with loop.Regards
Peter0 -
pdj wrote:Andri Arie Wianto wrote:FIND('-'): Use it when you need the ENTIRE records, instead of only one. Hence, you MUST use it when using with loop.0
-
Hi Kriki,
I have a question about GET vs. FINDFIRST.GET: This command is the best to use if you want to search the record using its primary key. You can also filter on the primary key and use FINDFIRST. The SELECT-statement sent to SQL is the same. But the GET requires less coding and is easier to read. No RESET-SETCURRENTKEYSETRANGE are needed. The GET does NOT EVEN consider them.
Can you confirm this?
Best Regards,Reijer Molenaar
Object Manager0 -
DenSter wrote:The way I read it is "you must only use it when you need to loop through records", as opposed to needing only one record.
I don't understand why anyone would use FIND('-') in these situations after having read Krikis description.Regards
Peter0 -
reijermolenaar wrote:I have read somewhere that the the GET method only locks 1 record and the FINDFIRST method also locks the record before and the record after the fetched record.
Mark Brummel has made a few posts about locking in NAV right here.Regards
Peter0 -
pdj wrote:That's also how I read it. My question is regarding the usage of FIND('-') instead of FINDSET.0
-
DenSter wrote:You weren't asking a question about FINDSET vs FIND('-')DenSter wrote:Andri was replying to a message by Navuser1, who was comparing FINDFIRST and FIND('-'),
Andri answered him correctly (FINDFIRST), but also mentioned that FIND('-') should be used when looping.
Navuser then quoted all of Andris answer and concluded he should use FIND('-') when needing to find just one record??
This frustraded me, and I tried to direct both of them to the sections in Krikis article describing where they each went wrong.DenSter wrote:Whether to use FIND('-') or FINDSET was not being discussed.
I also agree that a lot of the confusion might be related to translation issues, mine included. We don't all speak nor write English flawlessly. :oops:Regards
Peter0 -
And meanwhile, everyone means exactly the same
I would LOVE to sit down with this group for beers
0 -
Oh yeah! I'm sure we could handle this in the popular beer summit style 8)Regards
Peter0 -
How to work with record-variables (version 2)?
http://www.mibuso.com/howtoinfo.asp?FileID=22
Discuss this How To here.0 -
pdj wrote:>No. of records returned by FIND(‘-‘)
You might be right, but I will still claim that it used to do a FAST 25 in some older versions. :-)kriki wrote:=> The actual SQL statement is "FETCH API_CURSOR". NAV opened a cursor at the beginning and now it is fetching the records 1 by 1. This means that NAV each time sends a command to fetch the next record and then SQL sends 1 record. With FINDSET, SQL sends immediately 500 (or 50) records on FINDSET command and after those record, it goes 1 by 1.pdj wrote:No, the indention it IS wrong. Otherwise the ”END” before the “UNTIL recGLEntry.NEXT = 0;” would be only 2 chars different and not 6 as now.
The code you made is fine, but the indention confused me a bit. If I only look at the indention (and ignore ; and BEGIN END) it actually only does something if none of the keys exists.Regards,Alain Krikilion
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!0 -
navuser1 wrote:Dear all,
Which will give the better performance ? Please explain if possible.IF TheTable.FINDFIRST THEN BEGIN ....................; ....................; END;
ORIF TheTable.FIND('-') THEN BEGIN ....................; ....................; END;
Thanks with regards!
To summarize all other posts (and my how-to) in 1 line for your question:
If you want 1 record (if it exists), use FINDFIRST. FINDfirst does NOT create a cursor in SQL. FIND('-') creates a cursor in SQL. Cursors ARE SLOW.Regards,Alain Krikilion
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!0 -
kriki wrote:You're right. In my document it is correct (I checked my document). Now I noticed that while converting it in HTML, something went wrong.No support using PM or e-mail - Please use this forum. BC TechDays 2024: 13 & 14 June 2024, Antwerp (Belgium)0
-
pdj wrote:I also agree that a lot of the confusion might be related to translation issues, mine included. We don't all speak nor write English flawlessly. :oops:Regards,Alain Krikilion
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!0 -
reijermolenaar wrote:Hi Kriki,
I have a question about GET vs. FINDFIRST.GET: This command is the best to use if you want to search the record using its primary key. You can also filter on the primary key and use FINDFIRST. The SELECT-statement sent to SQL is the same. But the GET requires less coding and is easier to read. No RESET-SETCURRENTKEYSETRANGE are needed. The GET does NOT EVEN consider them.
Can you confirm this?
Best Regards,
I did some tests with GET,FINDFIRST,FIND('=') and locking and I noticed ALL 3 behave the same way! There is no difference in locking between them. But remember that I used them to get exactly 1 record using the primary key.
Like Mark explains in his blogs, it is possible to lock a range of records, but it depends on the filters.Regards,Alain Krikilion
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!0 -
Myth busted!
They probably changed the locking meganism in the newer versions of NAV.
Good to hear.
Thanks!Reijer Molenaar
Object Manager0 -
Instead of saying WRONG or CORRECT, why not use EFFICIENT and INEFFICIENT?
Wrong is such a strong word for cases where the code needs to be as it is.Confessions of a Dynamics NAV Consultant = my blog
AP Commerce, Inc. = where I work
Getting Started with Dynamics NAV 2013 Application Development = my book
Implementing Microsoft Dynamics NAV - 3rd Edition = my 2nd book0 -
Alex Chow wrote:Instead of saying WRONG or CORRECT, why not use EFFICIENT and INEFFICIENT?
Wrong is such a strong word for cases where the code needs to be as it is.
I have had an example some years ago: someone used always SETFILTER also when he could use SETRANGE (Native DB). The person remembered that one was more efficient than the other. But that person remembered (wrongly) that SETFILTER was more efficient then SETRANGE. And in reality it is SETRANGE that can be more efficient but never the other way around.Regards,Alain Krikilion
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!0
Categories
- All Categories
- 73 General
- 73 Announcements
- 66.6K Microsoft Dynamics NAV
- 18.7K NAV Three Tier
- 38.4K NAV/Navision Classic Client
- 3.6K Navision Attain
- 2.4K Navision Financials
- 116 Navision DOS
- 851 Navision e-Commerce
- 1K NAV Tips & Tricks
- 772 NAV Dutch speaking only
- 617 NAV Courses, Exams & Certification
- 2K Microsoft Dynamics-Other
- 1.5K Dynamics AX
- 320 Dynamics CRM
- 111 Dynamics GP
- 10 Dynamics SL
- 1.5K Other
- 990 SQL General
- 383 SQL Performance
- 34 SQL Tips & Tricks
- 35 Design Patterns (General & Best Practices)
- 1 Architectural Patterns
- 10 Design Patterns
- 5 Implementation Patterns
- 53 3rd Party Products, Services & Events
- 1.6K General
- 1.1K General Chat
- 1.6K Website
- 83 Testing
- 1.2K Download section
- 23 How Tos section
- 252 Feedback
- 12 NAV TechDays 2013 Sessions
- 13 NAV TechDays 2012 Sessions