Besides the functionality and speed that you mentioned already,
there come other factors to my mind. Just a few from the hip:

  • Portability: Moving a Caché/IRIS DB from one system to the other is as easy as a file copy. And this goes across most actual operating platforms on local HW or in the cloud, from Win to *X and back
  • Flexibility: Splitting or extending your DB if you run out of disk space has no impact on the applications. Vertical or horizontally scaling is a standard feature
  • Connectivity: especially with IRIS you have a huge range of adapters and interfaces available that allow access not only by SQL but also by Objects without any need to separate the data accessed from each other.
  • Openness: You need a private SQL Function or SQL Procedure because Standards don't fit?   No problem: Just add your self modeled ClassMethod and you have it done for both SQL and Object access.  

As I mentioned already in a different place: The only limit is your own imagination.  

 

The scope and the dimension of possibilities are much wider with IRIS than it was with Caché.
Very generally speaking: There is nothing in Caché that you can't do with IRIS.
The only thing you might miss eventually, are some ancient compatibility hooks back to the previous millennium. wink
Also, support of some outdated operating system versions is gone

In general YES.
I did it myself with preIRIS  Caché.
Data and Tables and Views and Triggers are not demanding.
Migrating Stored Procedures could take a little bit more effort.
But the ability to transform Stored Procedures to Class Methods should allow also to solve tricky situations.

(I'm personally proud that over more than a decade I never lost a benchmark against Oracle)) 

Hi Jeff;

all you have to do is to set the Stream location to a  Global in *TEMP Database.
Example:
 

Ok ... so this actually works:

ClassMethod GetHL7Msg(pId As %String) As %Stream.GlobalCharacter [ SqlName = GetMsg, SqlProc ]
{
    Set tHl7 = ##class(EnsLib.HL7.Message).%OpenId(pId,,.tSC)
    Throw:$$$ISERR(tSC) ..GetErr(-400, "HL7 Message with ID "_pId_" Not Found.")
    Set tMsg = ##class(%Stream.GlobalCharacter).%New()
           SET tMsg.%Location="^mtemp.JEFF"
    Set tSC = tHl7.OutputToLibraryStream(.tMsg)
    Do tHl7.%Close()
    If tSC Set tSC=tMsg.%Save()
    Throw:$$$ISERR(tSC) ..GetErr(-400, $system.Status.GetErrorText(tSC))
    Return tMsg."%%OID"
}

>>>>>

yes

StreamPrefetch looks related but it is totally related to the .NET end (googled SQL prefetch in .NET), 
while <INVALID OREF> is clearly an issue @Ensemble end.
I'm not too deep in the external ODBC  world but I assume that collecting the resultset and
transmitting it to .NET  [ %SYS.SQLStreamSRV ] could happen asynchronously in different processes.
So I doubted %Stream.TempCharacter which is bound to a process private Global  vs. a standard Global visible also to other processes. And only the final %Save nails it down.

I wonder if you experience the same problem if you run your query from
terminal prompt: do $system.SQL.Shell()  
This can signal if the ODBC connection plays a role at all.

%SYS.SQLStreamSRV.obj  is a deployed routine / class so we can't look into its details.
<INVALID OREF> expects a (stream) Object, but it doesn't exist.
So an additional check might be required.  Or you force some content for testing.
 

ClassMethod GetHL7Msg(pId As %String) As %Stream.TmpCharacter [ SqlName = GetMsg, SqlProc ]
{
    Set tHl7 = ##class(EnsLib.HL7.Message).%OpenId(pId,,.tSC)
    Throw:$$$ISERR(tSC) ..GetErr(-400, "HL7 Message with ID "_pId_" Not Found.")
    Set tMsg = ##class(%Stream.GlobalCharacter).%New()
    Set tSC = tHl7.OutputToLibraryStream(.tMsg)

     ;;  Throw:$$$ISERR(tSC) ..GetErr(-400, "HL7 Message with ID "_pId_" failed.") 
         IF 'tMsg.Size DO tMsg.Write("*** no message for ID:"-pId_" ***")
         SET tSC=tMsg.%Save()

    Do tHl7.%Close()
    Return tMsg
}

So you should have always a Stream Object