Defining Stored Procedures

 
Source code
Result:
select * from dc.numbers(107)
n
1
2
...
105
106
107

Through %ZEN.proxyObject is unlikely to work, since the q parameter cannot be disabled in this case

q - output numeric values unquoted even when they come from a non-numeric property

Use your own class, for example:

Class dc.test Extends %RegisteredObject
{

Property articlenumber As %String;

}
object ##class(dc.test).%New()
object.articlenumber "15049950"

##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject(.json,object,,,,"aelotw")

Output:

{
  "articlenumber":"15049950"
}

Conclusion: the quote will be removed from the documentation since it's no longer true.

Then besides this, in the documentation for %STARTSWITH need to add the note DEPRECATED and the recommendation "use LIKE 'XXX%'"

I also did an analysis for Caché 2018.1

Class del.t Extends %Persistent
{

Index ip On p;

Property As %VarString;

/// d ##class(del.t).Fill()
ClassMethod Fill(1000000)
{
  DISABLE^%NOJRN
  ^del.tD,^del.tI

  i=1:1:^del.tD(i)=$lb("","test"_i)
  ^del.tD=N
  ENABLE^%NOJRN

  ..%BuildIndices(,,,$$$NO)
  d $system.SQL.TuneTable($classname(),$$$YES)
  d $system.OBJ.Compile($classname(),"cu-d")
}
}

Although the plans are exactly the same in SMP the results of the metrics differ:

select count(*from del.where like 'test7%'
Row count: 1 Performance: 0.291 seconds 333340 global references 2000537 lines executed

select count(*from del.where %startswith 'test7'
Row count: 1 Performance: 0.215 seconds 333340 global references 1889349 lines executed

For the next two queries, the INT code matches:

&sql(select * from del.where like 'test7%')
&sql(select * from del.where %startswith 'test7')

But for these - is already different, so the metrics in SMP are different:

&sql(select * from del.where like :a)
&sql(select * from del.where %startswith :a)

In IRIS 2020.1, the work with embedded queries was changed, but I can't check it.

If you have old version of Caché, you can use %ZEN.Auxiliary.jsonProvider or %ZEN.Auxiliary.altJSONProvider, which have a bunch of useful methods, for example:
%ArrayToJSON
%WriteJSONFromArray
%WriteJSONStreamFromArray
etc.

Here are two small examples:

set array=##class(%ListOfDataTypes).%New()
for i=1:1:4000000 array.Insert("item"_i)
   
write "count = ",array.Count(),!
   
do ##class(%ZEN.Auxiliary.altJSONProvider).%WriteJSONStreamFromObject(.stream,array,,,1,"aeloq")
   
; here you can save stream to a file or send it to the client
set meta=$lb("nameA","nameB","nameC")
   
for i=1:1:4000000 set data(i)=$lb("itemA"_i,"itemB"_i,"itemC"_i)
   
do ##class(%ZEN.Auxiliary.altJSONProvider).%ArrayToJSON(.meta,.data)

To Dmitry's words I will add a few links from the documentation:

In your case, be the $LISTTOSTRING function is useful, for example:

USER>set first=$lb("words","more","words")
USER>write first
words-morewords
USER>write $listtostring(first)
words,more,words
USER>write $listtostring(first,"^")
words^more^words
USER>write $listtostring(first,"")
wordsmorewords

I think that the result of "dogcatfish" in the book was due to copying/pasting, which caused the service characters to be lost.

By the way, here on the forum it’s also not so easy to insert this gibberish ;)

 
And more food for thought:

Hi Peter.

Ok.

"Time for If (i=2): ",time(2,"end")-time(2,"start")," seconds",!,
  "Time for ElseIf #1 (i=3): ",time(3,"end")-time(3,"start")," seconds",!,
  "Time for ElseIf #2 (i=1): ",time(1,"end")-time(1,"start")," seconds",!,
  "Time for Else (i=4): ",time(4,"end")-time(4,"start")," seconds",!!

1e6
Time for If (i=2): .030974 seconds
Time for ElseIf #1 (i=3): .045126 seconds
Time for ElseIf #2 (i=1): .07144 seconds
Time for Else (i=4): .087353 seconds

1e9
Time for If (i=2): 28.59286 seconds
Time for ElseIf #1 (i=3): 43.044261 seconds
Time for ElseIf #2 (i=1): 82.277535 seconds
Time for Else (i=4): 69.212718 seconds