go to post Vitaliy Serdtsev · Jul 1, 2022 See RuntimeMode: s rs=##class(%ResultSet).%New("%DynamicQuery:SQL") s rs.RuntimeMode=1 ;or 2
go to post Vitaliy Serdtsev · Jun 30, 2022 parameter INDEXNULLMARKER; Override this parameter value to specify what value should be used as a null marker when a property of the type is used in a subscript of an index map. The default null marker used is -1E14, if none is specfied for the datatype. However %Library.PosixTime and %Library.BigInt datatypes could have values that collate before -1E14, and this means null values would not sort before all non-NULL values. For beauty, I would also use the value of this parameter, for example: Class dc.test Extends %Persistent { Property idp As dc.test; Property idpC As %Integer(INDEXNULLMARKER = "$c(0)") [ Calculated, Private, Required, SqlComputeCode = {s {*}=$s({idp}="":$c(0),1:{idp})}, SqlComputed ]; Property Name As %String [ Required ]; Index iUnq On (idpC, Name) [ Unique ]; ClassMethod Test() { d ..%KillExtent() &sql(insert into dc.test(Name,idp)values('n1',1)) w SQLCODE,! ;0 &sql(insert into dc.test(Name,idp)values('n2',1)) w SQLCODE,! ;0 &sql(insert into dc.test(Name,idp)values('n2',1)) w SQLCODE,!! ;-119 &sql(insert into dc.test(Name,idp)values('n1',null)) w SQLCODE,! ;0 &sql(insert into dc.test(Name,idp)values('n2',null)) w SQLCODE,! ;0 &sql(insert into dc.test(Name,idp)values('n2',null)) w SQLCODE,!! ;-119 zw ^dc.testD,^dc.testI } }Output: USER>d ##class(dc.test).Test() 0 0 -119 0 0 -119 ^dc.testD=4 ^dc.testD(1)=$lb("",1,"n1") ^dc.testD(2)=$lb("",1,"n2") ^dc.testD(3)=$lb("","","n1") ^dc.testD(4)=$lb("","","n2") ^dc.testI("iUnq",1," N1",1)="" ^dc.testI("iUnq",1," N2",2)="" ^dc.testI("iUnq",$c(0)," N1",3)="" ^dc.testI("iUnq",$c(0)," N2",4)=""Or Property idpC As %Integer [ Calculated, Private, Required, SqlComputeCode = {s {*}=$s({idp}="":$$$NULLSubscriptMarker,1:{idp})}, SqlComputed ];Output: USER>d ##class(dc.test).Test() 0 0 -119 0 0 -119 ^dc.testD=4 ^dc.testD(1)=$lb("",1,"n1") ^dc.testD(2)=$lb("",1,"n2") ^dc.testD(3)=$lb("","","n1") ^dc.testD(4)=$lb("","","n2") ^dc.testI("iUnq",-100000000000000," N1",3)="" ^dc.testI("iUnq",-100000000000000," N2",4)="" ^dc.testI("iUnq",1," N1",1)="" ^dc.testI("iUnq",1," N2",2)=""
go to post Vitaliy Serdtsev · Jun 30, 2022 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() { s 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{ s tmp=..%New() $$$ThrowOnError(tmp.%JSONImport(json,"AthenaAppointment")) $$$ThrowOnError($system.OBJ.Dump(tmp)) }catch(ex){ #dim ex As %Exception.AbstractException w ex.DisplayString() } } }Output: USER>d ##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?
go to post Vitaliy Serdtsev · Jun 30, 2022 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
go to post Vitaliy Serdtsev · Jun 30, 2022 This can be done much easier, for example: Include %occUtility Class dc.test Extends %Persistent [ ClassType = persistent, ProcedureBlock, SqlTableName = demo ] { Property Foo As %String [ SqlFieldName = FooBar ]; Property Bar As %Boolean; ClassMethod Test() { ; d ##class(dc.test).Test() d ..%KillExtent() &sql(insert into dc.demo(FooBar,Bar)values('f1',0)) &sql(insert into dc.demo(FooBar,Bar)values('f2',1)) &sql(insert into dc.demo(FooBar,Bar)values('f3',null)) s tablename=$$$comClassKeyGet(..%ClassName(1),$$$cCLASSsqlqualifiednameQ) s rs=##class(%ResultSet).%New("%DynamicQuery:SQL") d rs.Prepare("select * from "_tablename) d rs.Execute() while rs.Next() { w ! f i=1:1:rs.GetColumnCount() w rs.GetColumnHeader(i)," = ",$$quote(rs.GetData(i))," " } d rs.%Close() } }Output: USER>d ##class(dc.test).Test() ID = 1 Bar = 0 FooBar = "f1" ID = 2 Bar = 1 FooBar = "f2" ID = 3 Bar = "" FooBar = "f3"
go to post Vitaliy Serdtsev · Jun 28, 2022 Caché 5.0.21: Class DC.DemoPropertyQuery Extends %Persistent [ ClassType = persistent, ProcedureBlock ] { Property Foo As %String; Property Bar As %Boolean; ClassMethod Benchmark() { set Job=##CLASS(%SYSTEM.Process).%OpenId($job) set start = $zhorolog set startGlobalRefs = Job.GlobalReferences set startLines = Job.LinesExecuted for i=1:1:1000 { kill properties do ..GetPropertiesAsQuickly(.properties) } set endLines = Job.LinesExecuted set endGlobalRefs = Job.GlobalReferences write "Elapsed time (1000x): ",($zhorolog-start)," seconds; ",(endGlobalRefs-startGlobalRefs)," global references; ",(endLines-startLines)," routine lines",! do ..Print(.properties) write ! } ClassMethod Print(ByRef properties) { set key = "" for { set key = $order(properties(key),1,data) quit:key="" set $listbuild(type,origin) = data write !,"property: ",key,"; type: ",type,"; origin: ",origin } } ClassMethod GetPropertiesAsQuickly(Output properties) { // Getting properties via macro-wrapped direct global references is harder to read, // but is the fastest way to do it. set key = "" set class = ..%ClassName(1) for { set key = $$$comMemberNext(class,$$$cCLASSproperty,key) quit:key="" set type = $$$comMemberKeyGet(class,$$$cCLASSproperty,key,$$$cPROPtype) set origin = $$$comMemberKeyGet(class,$$$cCLASSproperty,key,$$$cPROPorigin) set properties(key) = $listbuild(type,origin) } } }Result: USER>do ##class(DC.DemoPropertyQuery).Benchmark() Elapsed time (1000x): .018047 seconds; 25003 global references; 40000 routine lines property: %Concurrency; type: %Library.CacheString; origin: %Library.Persistent property: %IsInSave; type: %Library.CacheString; origin: %Library.Persistent property: Bar; type: %Library.Boolean; origin: DC.DemoPropertyQuery property: Foo; type: %Library.String; origin: DC.DemoPropertyQuery
go to post Vitaliy Serdtsev · Jun 23, 2022 See $SYSTEM.SQL.SetServerInitCode() (there are differences for IRIS) Simple example: Class dc.test { ClassMethod Test() { s programname=$zcvt(##class(%SYS.ProcessQuery).%OpenId($j).ClientExecutableName,"L") i 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.
go to post Vitaliy Serdtsev · Jun 23, 2022 See Parent-Child Relationships and Storage Demonstration: 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() { d ..%KillExtent() d ##class(child).%KillExtent() &sql(insert into dc.parent(name) values('parent1')) &sql(insert into dc.parent(name) values('parent2')) &sql(insert into dc.child(name,parent) values('child11',1)) &sql(insert into dc.child(name,parent) values('child12',1)) &sql(insert into dc.child(name,parent) values('child21',2)) &sql(insert into dc.child(name,parent) values('child22',2)) zw ^dc.parentD,^dc.childD } }USER>d ##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) 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>d ##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) 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>d ##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.
go to post Vitaliy Serdtsev · Jun 23, 2022 See MultiValue Basic | Caché Alternative Exists for SOUNDEX() Workaround: Class dc.test [ Abstract ] { ClassMethod Test() { w ..SOUNDEX("M"),! ;or &sql(select SOUNDEX('McD') into :r) w r,! } ClassMethod SOUNDEX(s) As %String [ Language = mvbasic, SqlName = SOUNDEX, SqlProc ] { RETURN SOUNDEX(s) } } Result: USER>d ##class(dc.test).Test() M000 M230
go to post Vitaliy Serdtsev · May 17, 2022 Now on evaluation.intersystems.com for Windows/MacOSX/Container allows to download the version of IRIS 2020.1CE, and for the rest - IRIS 2021.2CE. What happened and when will the problem with downloading the latest version disappear once and for all?
go to post Vitaliy Serdtsev · May 13, 2022 It gives an incorrect result for some data, for example: Detector("abbz", "abzz") Detector("aaz", "azz") Detector("azz", "aaz")
go to post Vitaliy Serdtsev · May 13, 2022 Without loops: size = 82 (likely the code can be further reduced) ClassMethod Detector( a As %String, b As %String) As %Boolean { s a=$zu(28,a,6),b=$zu(28,b,6) q $l(a)=$l(b)&(a'=$tr(a,b,a_b))&(b'=$tr(b,a,b_a)) ;or s a=$zu(28,a,6),b=$zu(28,b,6) q $l(a)=$l(b)&(a'=$tr(a,b,a_b))&(a'=$tr(a,a_b,b)) }
go to post Vitaliy Serdtsev · May 12, 2022 I follow a simple rule: first I change the data, and only then I put the constraints in the code. Not the other way around.
go to post Vitaliy Serdtsev · May 12, 2022 This is the case when don't know whether to stop or continue.
go to post Vitaliy Serdtsev · May 12, 2022 Excellent work. You have come up with an additional optimization different from mine. Your code can be improved to 66. size = 68 ClassMethod Detector( a As %String, b As %String) As %Boolean { f i=65:1:91{q:$$r(a)'=$$r(b)} q i=91 r(x)q $l($$$UPPER(x),$c(i)) } size = 67 ClassMethod Detector( a As %String, b As %String) As %Boolean { f i=0:1:91{q:$$r(a)'=$$r(b)} q i=91 r(x)q $l($zu(28,x,6),$c(i)) } $$$ALPHAUP(x) == $zu(28,x,6) size = 66 ClassMethod Detector( a As %String, b As %String) As %Boolean { f i=90:-1:0{q:$$r(a)'=$$r(b)} q 'i r(x)q $l($zu(28,x,6),$c(i)) }
go to post Vitaliy Serdtsev · May 11, 2022 Ok. size = 190 ClassMethod Compress(s As %String) As %String { a s a=$p(s,",",$i(i)),d=$p(s,",",i+1)-a f c=1:1{q:d*c+a'=$p(s,",",i+c)} s q=$zabs(d),v=$s(c>2&d:"-"_(c-1*d+a)_$s(q=1:"",1:"/"_q),c>1&'d:"*"_c,1:0) s:v'=0 $p(s,",",i,i+c-1)=a_v q:a="" s g a } size = 189 ClassMethod Compress(s As %String) As %String { f i=1:1:2e6{s a=$p(s,",",i),d=$p(s,",",i+1)-a f c=1:1{q:d*c+a'=$p(s,",",i+c)} s q=$zabs(d),v=$s(c>2&d:"-"_(c-1*d+a)_$s(q=1:"",1:"/"_q),c>1&'d:"*"_c,1:0) s:v'=0 $p(s,",",i,i+c-1)=a_v} q s }