Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How to check that my query is empty ? or not empty ? 4

Status
Not open for further replies.

A_Radiolog_inFoxPro

IS-IT--Management
Mar 29, 2020
41
CY
Dear All ,

I am having an issue with some high-level queries,

Code:
 Select * from *sometable* INTO ARRAY aMyArray WHERE arguments

I need to be able to know if this query is empty so that I won't go down the line to check if my array even exists ( because since the query is empty the array won't be created at all.)

Thanks

The Doc
 
Steve said:
With your code, I got a RECCOUNT() of 4.
Okay, if you use VFP8 or lower it has no optimizaton of this sepcifi LIKE case with %. Also might depend on whether you have SET ANSI ON or OFF, but trry this instead:

Code:
Clear
Open Database _samples+"tastrade\data\tastrade.dbc"
Select * from customer where customer_id = 'ALFKI' into cursor alfki
? _tally
? Reccount()
Scan
   ? customer_id
endscan

If that's still returning reccount() of 1 I'd be interested what's going on with your tastrade database. It has an index on customer_Öid that makes that query fully optimizable which is why the result cursor becomes a filter cursor, and then reccount() will be the reccount of the customer.dbf not the _tally of the query.

I'm just posting this for future readers, Steve, I know I convinced you, but whoever is gettnig the idea _tally is unreliable would perhaps - wrongly - jump on the wagon to use reccount(). You can also rely on reccount when you use the NOFILTER or READWRITE keyword for queries, especially since the result cursor also is the selected workarea directly after the query.

And you can fail with anything you use, if you use _tally not directly after a query but after doing any further command listed in the hackers guide that also sets _tally, you have that new count, not the one related to the query. If you use reccount() on a filter cursor or after you select another workarea, reccount() itself won't give you the resultcount, reccount('resultcursoraliasname'), will. But as also mentioned once you addd records to the result cursor that obviously also doesn't reflect the query result count.

And all of that is obviously okay, because that's what reccount() should deliver, if the cursor has more records, it should givce that, and if you use reccount() to reflect that, not only directly afte the query, then reccount() is the right choice.

There's no war between what to avoid or use, just be cautious of the special cases, if you're like someone not knowing how things work and not eager to lookup and take for granted that VARTYPE reflects whether a result hs rows or not, then you don't become aqware that this sepcific VARTYPE checks the existence or nonexiystance of the array. And when you make sure the array doesn't exist before the query runs, you can use that VARTPE check. But if you want to learn something for universal usage, then _tally is the best as it does nother depend on whether a result is a cursor, an array, a new DBGF file or ahtever else I forgot could be a possible result. And you can slo use _tally after updates and such things where knowing the recccount() wouldn't tell you how many rows are changed.

So that makes _tally a solution to keep in mind when you like a general solution. I personally prefer thinking about each individual case. Some would say this makes them slow, I say it keeps me alert and once you know more and more this also isn't the time consuming task you think it is. I'd never just rely on a ruleset, but if you're that kind of programmer or hobbyist, then the general rule I'd guarantee works universalle is using _tally, even though it has gotchas others than maybe under some circumstances being wrong.

I just see people using code they don't understand and rely on it, which often makes it non universal and transferring it to a similar but not equal case then fails on not knowing the details. I can only compare this to copying a paragraph of chinese. If I only know the whole translation of it, I'd still not be able to guess what each single glyph or word means. That's what I see is the state of many developers, even though the programming languages somewhat still lean on english words.

So I'm just after this so rigorously, as I don't want someones "vocabulary" being spoiled ba something that bears no direct relationship to what was asked. The question is how to determine whether a query has results, not whether an array exists, and _tally=0 is the case of no result. vartype('cursorname')='U' would also be true, if the reccount() of it is >0.

Now you may accuse me of thinking too bad about the ability of people to understand things, but my experience here is many things sink in very slow for some members, here. It's not phenomenon limitd to one or two, only.

Chriss
 
And one more "last word" on why the filter cursor case is not just an exotic exception. When your goal is to have optimizable queries you define the necessay indexes that make a query fully optimizable and the result then is a filter cursor instead of a "real" cursor and you therefore could fall for that trap more often than you believe.

Chriss
 
There are a couple of cases where _TALLY can be risky. The most significant is if you're using timers and the timer code might run a query. If the timing is just right (or, I guess, just wrong), you can run a query and before your next command (the one that checks _TALLY) runs, the timer fires and runs its code and changes _TALLY.

The other, less likely, case is when you have event binding. If a bound event fires between the end of your query and your test of _TALLY, you can have trouble.

Tamar
 
I mentioned the risk of the next thing done that influences _tally. I can also think of further cases, but that's just confirming my overall mindset to never just write more rules into your golden book of rules and always think what fits best in a case.

I think, though, a timer event in VFP has this peculiar behavior to not occur between any two lines of code execution. Let's see with a very tight timer:
Code:
_vfp.AutoYield =.f.

Create Cursor cursor1 (num int)
Insert into cursor1 values (1)

Create Cursor cursor2 (num int)
Insert into cursor2 values (1)
Insert into cursor2 values (2)

Clear
oTimer = CreateObject("myTimer")

lastTally = 1
nNow = Seconds()
Do while m.lastTally = 1 And Seconds()-nNow < 10
   Select * from cursor1 into cursor cursor3
   lastTally = _Tally
EndDo
? oTimer.nCount
Doevents Force
? oTimer.nCount
oTimer = .null.

Define Class myTimer as Timer
   Interval = 1
   nCount = 0
   Enabled = .T.
   
   Procedure Timer() 
      Select * from cursor2 into cursor cursor3
      This.nCount = This.nCount + 1
   EndProc
Enddefine

It's only a test, it doesn't prove anything, but I always see the first count display as 0, also when you switch autoyield to .T., and the while loop always runs for the full 10 seconds and has no case of an unexepcted _tally (or lastTally).

In both cases the FORCE clause of the DOEVENTS in the aftermath code will let VFP wait until at least 1 event occured, which I didn't just add to show the timer works, but also to verify, if it can only ever happen in the idle state of VFP, which I think is the case.

With autoyield .F. DOEVENTS FORCE will let exactly 1 timer event happen, with autoyield =.T. I get higher counts, which seems to point out queued up events now get done.

So overall I'd say the timer isn't having a chance to interfere between two lines of code, even with autoyield=.t., unless the line of code is DOEVENTS or even DOEVENTS FORCE.

Feel free to try with many other settings, lso with a longer timer interval. I'd never feel a danger of a timer messing up _tally, I'd be more afraid of complex queries and subqueries getting VFP confused, or using a function call within a query. Or the most blatant error of doing anything the hacker guide lists also influencing _tally before evaluating _tally. So one thing that makes it pretty safe would be storing _tally into something you then check.

Chriss
 
The reason I know that timers can mess with _TALLY is because I ran into it in a real application. It was a very complex application, involving a VFP EXE that instantiated and communicated with a VFP COM object (which, in turn, talked to a C# COM object which, in turn, talked to a piece of hardware). We had a timer that fired pretty regularly to let us check a communication queue, and we absolutely ran into issues with code from the timer overwriting _TALLY.

Tamar
 
Okay, I can accept that many processes and runtimes involved you somehow can get there.

You still have no choice to use RECCOUNT() over _TALLY when it comes to the number of updates an UPDATE-SQL made, unless you use an UPDATE-SQL without WHERE condition or a REPLACE ALL with no FOR condition to change all records.

I assume you were actually using VFP timer classes. Weren't you also involved in finding some bugs in timers and suggesting to use some helper libraries like cpptimer.fll, if it comes to more accurate or reliable timer usage? In regard of actually running after every interval unless they would run reentrant, for example.

On the negative side, these ttimers would not be involved into the model of the VFP callstack and VFP code execution that I still think only allows timer events to occur when VFP enters the idle mode. It's just impossible to prove without having the source code of VFPs main runtime code for the execution of VFP object code.

In some cases COM servers will use copies of the vfp runtime, either the normal 9r or the limited 9t runtime, so in case COM objects, esepcially VFP COM objects are involved and are in processs, the shared access to _TALLY could mes things up.

Anyway, okay. Since even storing _TALLY to somewhere under your codes full control right after each query then may risk to already store away a wrong _TALLY it becomes useless. I'd still take a bet this has to at least involve an in process COM server that even has the same public _TALLY system variable as a shared resource.

As far as I understand the separation of resources done by the appartment model multithreded DLLs, you would have separate _TALLY variables depending on which appartment your object runs in. So maybe an error also is relying on _TALLY in one object being the same as in another. I'll think about that and may come up with an example investigating that.

I think about that paragraph of Rick Strahls article about multithreading:

Rick Strahl said:
Visual FoxPro DLL – MTDLL or otherwise - do not qualify as thread safe. FoxPro has tons of shared state internally and if that shared state were to be accessed simultaneously from multiple threads – BOOOM! So FoxPro is not a true multi-threaded component.

It's not so much about his wording of BOOM, but more about the topic of shared states, which is not very specific here, but may include things like the states of settings or values of system variables, I think. It's still quite an academic case of getting two different _TALLYs.

I mean if a timer that should run every millisecond does not even run once, there is no chance it's timer query could override _TALLY, so my example sucks in that aspect, it only demonstrates that a timer hardly can even run and thereby cause that damage, if you're in the normal world of a single VFP process and runtime running singlethreaded. I think you could find out whether timer events only can happen from idle states, if you analyzed tons of coverage logging and see whether timer events only follow on lines having DOEVENTS, READ EVENTS or code starting modal form, maybe also Sleep API Calls, more likely only SleepEx or similar APII calls which could put VFP into an idle state, too. That would be what I wanted to check next.

Chriss
 
It wasn't me on that timer stuff, though I've seen the advice.

For what I was going in that app, using RECCOUNT() solved the problem because it was always queries we needed to know about. So I never dug any deeper.

Tamar
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top