..
sql=0
sql($i(sql))="select *"
sql($i(sql))=",%external(DOB)||' '||%external(DOBTime) DOBString"
sql($i(sql))=",TO_CHAR(DOB||','||DOBTime,'MM/DD/YYYY HH24:MI:SS') DOBString2"
sql($i(sql))="from dc.test"
st.%ExecDirect(.st,.sql).%Display()
..

Result:

USER>##class(dc.test).Test()

ID
(INTEGER)
DOB
(DATE)
DOBTime
(TIME)
DOBString
(VARCHAR)
DOBString2
(VARCHAR)
1 01.01.2020 23:59:59 01.01.2020 23:59:59 01/01/2020 23:59:59
2 31.12.2022 10:10:10 31.12.2022 10:10:10 12/31/2022 10:10:10

I like @Yaron Munz version better

Class dc.test Abstract ]
{

ClassMethod Test()
{
  v=0,
    v($i(v))=$lb("2021-11-27","2022-08-04"),
    v($i(v))=$lb("2020-02-28","2023-02-27"),
    v($i(v))=$lb("2020-02-28","2023-03-01"),
    v($i(v))=$lb("2017-03-01","2020-02-28")
    
  i=1:1:$o(v(""),-1) {
    d1 $li(v(i),1),
      d2 $li(v(i),2),
      age1 $e(d2,1,4)-$e(d1,1,4)-($e(d1,6,10)]$e(d2,6,10)),
      age2 $number($zabs($zdh(d2,3)-$zdh(d1,3))/365.25,0),
      age3 $zdh(d2,3)-$zdh(d1,3)\365.25
      
    d1," ",d2," (age1 = ",age1,", age2 = ",age2,", age3 = ",age3,")",!
  }
}

}

Result:

USER>##class(dc.test).Test()
2021-11-27 2022-08-04 (age1 = 0, age2 = 1, age3 = 0)
2020-02-28 2023-02-27 (age1 = 2, age2 = 3, age3 = 2)
2020-02-28 2023-03-01 (age1 = 3, age2 = 3, age3 = 3)
2017-03-01 2020-02-28 (age1 = 2, age2 = 3, age3 = 2)

Instead of two separate fields, it would be easier to use the %TimeStamp (or %PosixTime) type, where the date and time are paired at once.
For this type of data, you can make your own indexes for different parts and/or combinations of them: a separate date, a separate time, a separate year, a separate year and month, etc.
You can also use these separate parts in the query (in SELECT and WHERE).

select datediff('yy',DOB,current_dateAge from yourtable
Class dc.test Extends (%RegisteredObject%JSON.Adaptor)
{

Parameter %JSONENABLED = 1;

Property AppointmentID As %String(%JSONFIELDNAME "AppointmentID"%JSONINCLUDE "inout");
Property AppointmentType As %String(%JSONINCLUDE "inout");
Property AppointmentTypeID As %String(%JSONINCLUDE "inout");
Property Date As %String(%JSONINCLUDE "inout");
Property DepartmentID As %String(%JSONINCLUDE "inout");
Property Duration As %Integer(%JSONINCLUDE "inout");
Property PatientAppointmentTypeName As %String(%JSONINCLUDE "inout");
Property LocalProviderID As %String(%JSONINCLUDE "inout");
Property ProviderID As %String(%JSONINCLUDE "inout");
Property StartTime As %String(%JSONINCLUDE "inout");
Property Reason As %String(%JSONINCLUDE "inout");

XData AthenaAppointment
{
<Mapping xmlns="http://www.intersystems.com/jsonmapping">
  <Property Name="AppointmentID" FieldName="appointmentid" />
  <Property Name="AppointmentType" FieldName="appointmenttype" />
  <Property Name="AppointmentTypeID" FieldName="appointmenttypeid" />
  <Property Name="Date" FieldName="date" />
  <Property Name="DepartmentID" FieldName="departmentid" />
  <Property Name="Duration" FieldName="duration" />
  <Property Name="PatientAppointmentTypeName" FieldName="patientappointmenttypename" />
  <Property Name="LocalProviderID" FieldName="localproviderid" />
  <Property Name="ProviderID" FieldName="providerid" />
  <Property Name="StartTime" FieldName="starttime" />
  <Property Name="Reason" FieldName="reasonid" />
</Mapping>
}

/// d ##class(dc.test).Test()
ClassMethod Test()
{
  json="{""date"":""06/27/2022"",""appointmentid"":""1214525"",""departmentid"":""195"",""localproviderid"":""187"",""appointmenttype"":""NEW PATIENT 45"",""providerid"":""187"",""starttime"":""14:00"",""duration"":45,""appointmenttypeid"":""1188"",""reasonid"":""-1"",""patientappointmenttypename"":""New Patient""}"
  
  try{

    tmp=..%New()
    
    $$$ThrowOnError(tmp.%JSONImport(json,"AthenaAppointment"))

    $$$ThrowOnError($system.OBJ.Dump(tmp))
  
  }catch(ex){
    #dim ex As %Exception.AbstractException
    ex.DisplayString()
  }
}

}

Output:

USER>##class(dc.test).Test()
+----------------- general information ---------------
|      oref value: 3
|      class name: dc.test
| reference count: 1
+----------------- attribute values ------------------
|      AppointmentID = 1214525
|    AppointmentType = "NEW PATIENT 45"
|  AppointmentTypeID = 1188
|               Date = "06/27/2022"
|       DepartmentID = 195
|           Duration = 45
|    LocalProviderID = 187
|PatientAppointmentTypeName = "New Patient"
|         ProviderID = 187
|             Reason = -1
|          StartTime = "14:00"
+-----------------------------------------------------

PS: pay special attention to the reason field: is it a string or an array of strings?

The new version of IRIS 2022.2 has a new feature Columnar Storage, about which the documentation says the following:

Choosing a storage layout is not an exact science. You might need to experiment with multiple layouts and run multiple query tests to find the optimal one.

Therefore, you are unlikely to find an exact answer to your question.

Usually, the more efficient the query is and there are "correct" indexes, the smaller the GREF and, accordingly, the shorter the execution time. But this is influenced by many factors, not just the above: see InterSystems SQL Optimization Guide

See $SYSTEM.SQL.SetServerInitCode() (there are differences for IRIS)

Simple example:

Class dc.test
{

ClassMethod Test()
{
  
  programname=$zcvt(##class(%SYS.ProcessQuery).%OpenId($j).ClientExecutableName,"L")

  programname="blablabla.exe" {

    ;useful work

    s $EC="ERROR"
    ;or
    s $ROLES="r1"
  }
}

}

USER>d $SYSTEM.SQL.SetServerInitCode("d ##class(dc.test).Test()")

Now, when connecting from a specific program via ODBC/JDBC to namespace "USER", an error will occur. You can configure something another.

See Parent-Child Relationships and Storage

Demonstration:

  1. Class dc.child Extends %Persistent
    {
    Property name;
    Property parent As dc.parent;
    // Relationship parent As dc.parent [ Cardinality = parent, Inverse = child ];
    }
    
    Class dc.parent Extends %Persistent
    {
    Property name;
    // Relationship child As dc.child [ Cardinality = children, Inverse = parent ];
    ClassMethod Test()
    {
      ..%KillExtent()
      ##class(child).%KillExtent()
      
      &sql(insert into dc.parent(namevalues('parent1'))
      &sql(insert into dc.parent(namevalues('parent2'))
    
      &sql(insert into dc.child(name,parentvalues('child11',1))
      &sql(insert into dc.child(name,parentvalues('child12',1))
      &sql(insert into dc.child(name,parentvalues('child21',2))
      &sql(insert into dc.child(name,parentvalues('child22',2))
      
      zw ^dc.parentD,^dc.childD
    }
    }
    USER>##class(dc.parent).Test()
    ^dc.parentD=2
    ^dc.parentD(1)=$lb("","parent1")
    ^dc.parentD(2)=$lb("","parent2")
    ^dc.childD=4
    ^dc.childD(1)=$lb("","child11",1)
    ^dc.childD(2)=$lb("","child12",1)
    ^dc.childD(3)=$lb("","child21",2)
    ^dc.childD(4)=$lb("","child22",2)
  2. Important: do not touch the existing Storages in both classes!!!
    Class dc.child Extends %Persistent
    {
    Property name;
    //Property parent As dc.parent;
    Relationship parent As dc.parent Cardinality = parent, Inverse = child ];
    Storage Default
    {
    ...
    }
    }
    
    Class dc.parent Extends %Persistent
    {
    Property name;
    Relationship child As dc.child Cardinality = children, Inverse = parent ];
    ClassMethod Test()
    {
     ...
    }
    Storage Default
    {
    ...
    }
    }
    USER>##class(dc.parent).Test()
    ^dc.parentD=2
    ^dc.parentD(1)=$lb("","parent1")
    ^dc.parentD(2)=$lb("","parent2")
    ^dc.childD=4
    ^dc.childD(1,1)=$lb("","child11",1)
    ^dc.childD(1,2)=$lb("","child12",1)
    ^dc.childD(2,3)=$lb("","child21",2)
    ^dc.childD(2,4)=$lb("","child22",2)
  3. Important: now remove Storage from dc.child class and recompile both classes. Note that now the Storage of the dc.child class has changed.
    USER>##class(dc.parent).Test()
    ^dc.parentD=2
    ^dc.parentD(1)=$lb("","parent1")
    ^dc.parentD(1,"child",1)=$lb("","child11")
    ^dc.parentD(1,"child",2)=$lb("","child12")
    ^dc.parentD(2)=$lb("","parent2")
    ^dc.parentD(2,"child",3)=$lb("","child21")
    ^dc.parentD(2,"child",4)=$lb("","child22")
    ^dc.childD=4
    ^dc.childD(1,1)=$lb("","child11",1)
    ^dc.childD(1,2)=$lb("","child12",1)
    ^dc.childD(2,3)=$lb("","child21",2)
    ^dc.childD(2,4)=$lb("","child22",2)

    Now the data in ^dc.childD from the previous test/step is hanging in the air and cannot be accessed via SQL

Based on the above, the answer to your question will depend on what and how exactly you changed in your classes.

PS: for simplicity, I would advise you to create a clone of your dc.child class (without Relationship) and already take the "disappeared" data from it. After linking the tables (possibly with subsequent copying of data from the old Storage to the new one), the clone with the data can be deleted.

See MultiValue Basic | Caché Alternative Exists for SOUNDEX()

Workaround:

Class dc.test Abstract ]
{

ClassMethod Test()
{
  
  ..SOUNDEX("M"),!

  ;or

  &sql(select SOUNDEX('McD'into :r)
  r,!
}

ClassMethod SOUNDEX(sAs %String Language = mvbasic, SqlName SOUNDEXSqlProc ]
{
 RETURN SOUNDEX(s)
}

}

Result:

USER>##class(dc.test).Test()
M000
M230

See %ZEN.Auxiliary.jsonSQLProvider:%WriteJSONStreamFromSQL()

 
Simple example

The contents of the file test.json (UTF8):

{
"children":[
{"ID":1,"Date":"04.05.2022","bool":0,"int":30,"str":"Hello Caché"}
,{"ID":2,"Date":"31.12.1840","bool":1,"int":303,"str":"ăîşţâ"}
,{"ID":3,"Date":"","bool":"","int":"","str":""}
]
}