%VID is a Caché internal workaround for subqueries to hide the fact that ROWNUM or ROW_NUMBER wasn't implemented
https://cedocs.intersystems.com/latest/csp/docbook/Doc.View.cls?KEY=RSQL_C189621 

You can't mix it with external database access with internal features
instead, use the RowNumber implementation of your external DB (they vary by product)
https://www.w3schools.com/sql/sql_top.asp

https://docs.microsoft.com/en-us/sql/t-sql/functions/row-number-transact-sql?view=sql-server-ver15

You just can win. The workload moves to the external DB and you transfer fewer records

I just tested a variable parameter list in SQL. It works: (simplified example)

Class User.SQLvar
{
ClassMethod MyLIST(var...) As %String [ SqlName = MyLIST, SqlProc ]
{ set result="^"
 for i=1:1:var set result=result_var(i)_"^"
  quit result }
}

with SQL:

 CACHE>do $system.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
[SQL]CACHE>>select MyList(1,2,3)
3.      select MyList(1,2,3)
Expression_1
^1^2^3^
 
1 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0538s/33292/149491/0ms
          execute time(s)/globals/cmds/disk: 0.0002s/0/428/0ms
                          cached query class: %sqlcq.CACHE.cls1
---------------------------------------------------------------------------
[SQL]CACHE>>select MyList(1,2,3,4,5,6)
Expression_1
^1^2^3^4^5^6^
 
1 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0545s/33591/158533/4ms
          execute time(s)/globals/cmds/disk: 0.0002s/0/440/0ms
                          cached query class: %sqlcq.CACHE.cls2
---------------------------------------------------------------------------
[SQL]CACHE>>

so you can generate your JSON  straght as ClassMethd

With 2013.* it is not built-in,  but you can mimic it:

every Classmethod can be projected as SQLprocedure.  

ClassMethod MyJSON(par1 As %String, par2 As %String As %String(MAXLEN==")  SqlName My_JSONSqlProc ]
{  

    set result = ""   /* now you assemble your JSON  in this string */
   
quit result 
}

I'm not sure if you can pass a variable list of arguments from SQL.
But this is the basic mechanic behind it

Based on the changed question you may consider this solution with just 1 Common Class:

If you look to the  Storage definition you see something similar:

<SQLMap name="Map1">
<Data name="....
</Data>
<Global>^tabA</Global>

Now you change the <Global> node to

<Global>@($s($d(%GLOBAL):%GLOBAL,1:"^tabA"))@</Global>

this means if there is no variable %GLOBAL you see ^tabA  but if you set %GLOBAL you see ^tabB 

to set the variable also directly from SQL  you may add this ClassMethod as SQLprocedure

ClassMethod SetGlobal(global As %String = "tabA") As %Integer [ SqlName = SetGlobal, SqlProc ]
{  set %GLOBAL=global  quit $$$OK }

now see the effect. first the test data:

 CACHEUSER>zw ^tabA
^tabA(1)="A=Brendan Bannon^55192^SQL Manager"
^tabA(2)="A=Nicole Aaron^63375^Support Specialist"
^tabA(3)="A=Kyle Baxter^61155^Senior Support Specialist"
^tabA(4)="A=Prasad Kari^58471^Support Specialist"
^tabA(5)="A=Clive Morgan^57982^Senior Support Specialist"
 
CACHEUSER>zw ^tabB
^tabB(1)="B=Brendan Bannon^55192^SQL Manager"
^tabB(2)="B=Nicole Aaron^63375^Support Specialist"
^tabB(3)="B=Kyle Baxter^61155^Senior Support Specialist"
^tabB(4)="B=Prasad Kari^58471^Support Specialist"
^tabB(5)="B=Clive Morgan^57982^Senior Support Specialist"

and now the SQL query

 CACHEUSER>zw   ; no variables

CACHEUSER>do $system.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
[SQL]CACHEUSER>>select * from Tbl  -- tableA
 
ID      HireDate        Name    Sub2    Title
1       55192   A=Brendan Bannon        1       SQL Manager
2       63375   A=Nicole Aaron  2       Support Specialist
3       61155   A=Kyle Baxter   3       Senior Support Specialist
4       58471   A=Prasad Kari   4       Support Specialist
5       57982   A=Clive Morgan  5       Senior Support Specialist
 
5 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0007s/20/955/0ms
          execute time(s)/globals/cmds/disk: 0.0004s/6/1324/0ms
                          cached query class: %sqlcq.CACHEUSER.cls6
---------------------------------------------------------------------------
[SQL]CACHEUSER>>COS set %GLOBAL="^tabB"  ; going for Global ^tabB

[SQL]CACHEUSER>>select * from Tbl -- tableB

 
ID      HireDate        Name    Sub2    Title
1       55192   B=Brendan Bannon        1       SQL Manager
2       63375   B=Nicole Aaron  2       Support Specialist
3       61155   B=Kyle Baxter   3       Senior Support Specialist
4       58471   B=Prasad Kari   4       Support Specialist
5       57982   B=Clive Morgan  5       Senior Support Specialist
 
5 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0005s/20/955/0ms
          execute time(s)/globals/cmds/disk: 0.0003s/6/1324/0ms
                          cached query class: %sqlcq.CACHEUSER.cls6
---------------------------------------------------------------------------

 next with pure SQL

 CACHEUSER>zw   ; no variables
CACHEUSER>do $system.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------

[SQL]CACHEUSER>>select * from Tbl where SetGlobal('^tabB')=1
 
ID      HireDate        Name    Sub2    Title
1       55192   B=Brendan Bannon        1       SQL Manager
2       63375   B=Nicole Aaron  2       Support Specialist
3       61155   B=Kyle Baxter   3       Senior Support Specialist
4       58471   B=Prasad Kari   4       Support Specialist
5       57982   B=Clive Morgan  5       Senior Support Specialist
 
5 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0008s/27/3394/0ms
          execute time(s)/globals/cmds/disk: 0.0003s/6/1350/0ms
                          cached query class: %sqlcq.CACHEUSER.cls10
---------------------------------------------------------------------------
[SQL]CACHEUSER>>

The SQL procedure SetGlobal is independent of the rows:
It will be processed before the row selecting.  So we set our local variable %GLOBAL there.
As it is part of the WHERE clause we have to check the return value.
So it works with any SLQ Query tool. eg. Mgmt Portal.

Setting the variable works of course also for object access.  

If I understand you correct you expect something  like

INSERT INTO new.MESSAGE (SELECT * from  EnsLib.HL7.Message  where id = ? )

this may work for rather modest designed relational tables but I doubt it will work for some complex structure as EnsLib.HL7.Message .

Subclassing is not the way to solve your problem for 2 reasons:

  • A subclass has always to match the .IsA() class type of its parent    (e.g. Employee as subclass of Person. Employee is also always a Person)
  • SubClasses share the storage with its parent. There are dirty tricks to avoid this but I assume you do not intend to change  EnsLib.HL7.Message.

There are ways to do something like this if you fiddle around in the storage structures. 
BUT: for reasons of transparency, documentation, and maintainability
I'd strongly recommend to use DTL. 


Fiddling around with COS in Ensemble is a rather tricky exercise.
Based on practical experience I can confirm that your successors will never forget you.