Source code

USER>##class(mp.test).Fill(5000000)

USER>##class(mp.test).Query()
count=3833346

first 5 [1:5]
ID prop3
3 3
4 3
24 3
30 3
97 3

5 Rows(s) Affected
.000328 secs

last 5 [3833342:3833346]
ID prop3
4999798 1
4999817 1
4999836 1
4999866 1
4999947 1

5 Rows(s) Affected
2.884304 secs

PS: for those who put a minus. May I ask why?

My EAV implementation is the same as your Approach 3, so it will work fine even with fully filled 4.000.000 attributes.
Since the string has a limit of 3,641,144, approaches with serial and %List are dropped.

All other things being equal, everything depends on the specific technical task: speed, support for Objects/SQL, the ability to name each attribute, the number of attributes, and so on.

Here is a ready-made example (works even in last Caché):

Class dc.test Extends %Persistent
{

Property title As %VarString;

Property author As %VarString;

/// do ##class(dc.test).test()
ClassMethod test()
{
  &sql(truncate table dc.test)
  
  &sql(insert into dc.test(title,author)
    select 'For Whom the Bell Tolls','Hemmingway' union
    select 'The Invisible Man','Ellison' union
    select 'Tender is the Night','Fitzgerald')
  
  set provider=##class(%ZEN.Auxiliary.altJSONSQLProvider).%New(),
      provider.sql="select title,author from dc.test",
      provider.arrayName="items",
      provider.maxRows = 0,
      provider.%Format "tw"
  
  do provider.%WriteJSONStreamFromSQL(.stream,,,,,provider)
  
  set json={}.%FromJSON(stream),
      json.results=json.items.%Size()

  write json.%ToJSON()
}

}

Result:

USER>do ##class(dc.test).test()
{"items":[{"title":"For Whom the Bell Tolls","author":"Hemmingway"},{"title":"The Invisible Man","author":"Ellison"},{"title":"Tender is the Night","author":"Fitzgerald"}],"results":3}
 
Added several more types:

Result:

USER>##class(dc.test).test()
"" => undefined
"" => empty
1 => string
1 => integer
1.1 => float
$double(1.1000000000000000888) => double
"1@%Library.ListOfDataTypes" => object
$lb() => list
$zwc(128,4)_$c(1,0,0,0)/*$bit(1)*/ => bitstring
$c(7) => bitstring
"" => array

Extending the reply of @Robert Cemper

  • The following query compiles without errors, even though Studio highlights the error
    &sql(select :fieldname into :fieldvar from :tablename)

    Everything is fine in Caché: the error occurs at the compilation-time.

    In order for an error to occur in IRIS Studio too you need to use the flag /compileembedded=1

  • It is strange that the documentation mentions field name, but there is no mention of table name:
    A host variable cannot be used to pass a field name or keyword into an SQL statement. proof

In MUMPS, not everything is so simple, so specify technical task ;)

Example:

  1. USER>set v1=1,v2="1" zzdump v1,v2
     
    0000: 31                                                      1
    0000: 31                                                      1
  2. what to do with dates:
    30000 is a number or date 20.02.1923?
    or
    "20.02.1923" - is it a date or a string?
  3. what about numbers: 0 is %TinyInt, %SmallInt, or %Integer?
  4. what about boolean: 1 is %Boolean or %Integer?

In addition, you mix MUMPS (variable) and InterSystems ObjectScript (%Library.XXX): see Variable Types

Thank you for asking.
It seems that no one has tried my code in all this time, or it's just that no one is interested in it.

There are two ways to solve the issue:

  1. configure Undefined=2. I have historically set this value. This can be done programmatically or via SMP
  2. fix the code:
    ClassMethod NewQuery1Fetch(
      ByRef qHandle As %Binary,
      ByRef Row As %List,
      ByRef AtEnd As %Integer 0As %Status PlaceAfter = NewQuery1Execute ]
    {
      if qHandle>1 {
        set Row=qHandle($increment(qHandle,-1))
      else {
        set AtEnd=1
      }
    
      quit $$$OK
    }
 
Example

Result:

USER>##class(dc.test).Test("test"_$c(68))
gTNg0UMkvQ3o+ehJkvr6lA==

USER>##class(dc.test).Test("test"_$c(768))
R8UuZkjDVZidYckYMTpnVg==

USER>##class(dc.test).Test("测试")
lsYxFAQgNtiXHyaeGTWJ0A==