go to post Vitaliy Serdtsev · Nov 24, 2020 Creating Class Documentation PS: Once upon a time (when was Caché) there was a wonderful DOCBOOK database that had the methods you needed. Here's my old (2012 sic!) article on this topic: Extending the built-in documentationRU->EN Here you can read a little more: The DocBook Application DocBookwiki
go to post Vitaliy Serdtsev · Nov 9, 2020 There is another way. If you want any empty strings to always be treated as null/"" instead of "/$c(0), then there is an documented setting (within the scope of namespace), namely: ^%SYS("sql","sys","namespace",<YOUR_NAMESPACE>,"empty string") Here is a small example: Class dc.test Extends %Persistent { Property i As %Integer; Property str As %String; /// do ##class(dc.test).Test() ClassMethod Test() { do ..%KillExtent() try{ do $system.SQL.Purge() set ^%SYS("sql","sys","namespace",$namespace,"empty string")=$c(0) ; '' -> $c(0) do ##class(%SQL.Statement).%ExecDirect(,"insert into dc.test(i,str) values(1,'')") ; null -> "" do ##class(%SQL.Statement).%ExecDirect(,"insert into dc.test(i,str) values(2,null)") do $system.SQL.Purge() set ^%SYS("sql","sys","namespace",$namespace,"empty string")="" ; '' -> "" do ##class(%SQL.Statement).%ExecDirect(,"insert into dc.test(i,str) values(11,'')") ; null -> "" do ##class(%SQL.Statement).%ExecDirect(,"insert into dc.test(i,str) values(22,null)") zwrite ^dc.testD kill ^%SYS("sql","sys","namespace",$namespace,"empty string") } catch(ex){ write ex.DisplayString(),! } } } USER>do ##class(dc.test).Test() ^dc.testD=4 ^dc.testD(1)=$lb("",1,$c(0)) ^dc.testD(2)=$lb("",2,"") ^dc.testD(3)=$lb("",11,"") ^dc.testD(4)=$lb("",22,"")
go to post Vitaliy Serdtsev · Nov 9, 2020 Another option just for the fun: Include %SYS.PTools.Stats Class dc.test [ Abstract ] { ClassMethod setValue(args...) As %Status { quit:args<2 $$$ERROR($$$DataMissing) $$$convertArrayToList(args,list) quit:$listlength(list)'=args $$$ERROR($$$RequiredArgumentMissing) set $list(list,*,*)="", var=##class(%Utility).FormatString(list), $extract(var,1,3)=$name(%sessionData), ##; or $name(%session.Data) @var=args(args) quit $$$OK } /// do ##class(dc.test).Test() ClassMethod Test() { new %sessionData try{ do $system.OBJ.DisplayError(..setValue("key1", "val1")), $system.OBJ.DisplayError(..setValue("key1", "key2", "key3", "key4", "val2")), $system.OBJ.DisplayError(..setValue("key1", "key2", , "key4", "val3")), $system.OBJ.DisplayError(..setValue()) write ! zwrite %sessionData } catch(ex){ write ex.DisplayString(),! } } }
go to post Vitaliy Serdtsev · Oct 8, 2020 Try it with between 1 and 5 on the %VID. Just as slow. I ran your code with new index. count(prop1=2) = 2629833 %VID BETWEEN 1 AND 5 .000293 secs %VID BETWEEN 2629829 AND 2629833 3.63472 secs I can't create an index on every possible filter and order by combination. Waiting for @Kyle.Baxter response.
go to post Vitaliy Serdtsev · Oct 8, 2020 Try adding a new index and don't forget make rebuild index/tunetable/recompile class Index idx On (prop1, prop3) [ Type = bitmap ]; Here yet need the help of @Kyle.Baxter. PS: by the way, check select count(*) from mp.test where prop1=2 to insert the correct values in the code %VID BETWEEN 3000000 AND 30000005 Because of this, is obtained 0 Rows(s) Affected
go to post Vitaliy Serdtsev · Oct 7, 2020 I don't think I quite understand you. That is, you need to: do SELECT * FROM <table> WHERE <difficult condition> ORDER BY <some field> DESC then select a certain data page from the resulting result select * from (step1) where %VID between N1 and N2 If not, provide a ready-made example with data similar to the example by @MARK PONGONIS
go to post Vitaliy Serdtsev · Oct 7, 2020 Source code Class mp.test Extends %Persistent { Property prop1; Property prop2; Property prop3; Index idx3 On (prop3, prop1, prop2) [ Type = bitmap ]; ClassMethod Fill(total = 5000000) { d ..%KillExtent() f i=1:1:total s ^mp.testD(i)=$lb("","name","prop2",$r(30)) s ^mp.testD=total d ..%BuildIndices() } ClassMethod Query() { s N=5 &sql(SELECT count(*) into :count FROM mp.test WHERE prop1='name' AND prop2='prop2' AND prop3>='1' AND prop3<='30') w "count=",count,!! s sql=##class(%SQL.Statement).%New() d sql.%Prepare("SELECT %ID,prop3 FROM mp.test WHERE %ID IN ("_ " SELECT * FROM ("_ " SELECT TOP ALL %ID FROM mp.test WHERE prop1='name' AND prop2='prop2' AND prop3>='1' AND prop3<='30' ORDER BY prop3 DESC"_ " ) WHERE %VID BETWEEN ? AND ?"_ ") ORDER BY prop3 DESC") w "first 5 [1:5]",! s rset=sql.%Execute(1,5) s time=$zh d rset.%Display() w !,$zh-time," secs",!! w $$$FormatText("last 5 [%1:%2]",count-N+1,count),! s rset=sql.%Execute(count-N+1,count) s time=$zh d rset.%Display() w !,$zh-time," secs" } } USER>d ##class(mp.test).Fill(5000000) USER>d ##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?
go to post Vitaliy Serdtsev · Sep 11, 2020 There is at least one case where this encoding style is acceptable - it is a generator method. Just look at the code generated by &sql() or CSP, for example. No wonder there are two versions for javascript libraries: for the developer and for production (processed by the minimizer).
go to post Vitaliy Serdtsev · Sep 8, 2020 And what if the answer is given, but the author either does not want to accept the answer at all or does not make a more elegant decision? https://community.intersystems.com/post/there-way-defermine-column-jsono... https://community.intersystems.com/post/creating-json-objects-objectscri...
go to post Vitaliy Serdtsev · Aug 5, 2020 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.
go to post Vitaliy Serdtsev · Aug 5, 2020 Entity–attribute–value model is usually used for this purpose. I have already written about this at the time: SQL index for array property elements.
go to post Vitaliy Serdtsev · Jul 9, 2020 select *, case when a is JSON then 1 else 0 end aa, case when b is JSON then 1 else 0 end bb, case when c is JSON then 1 else 0 end cc from (SELECT 1 a, JSON_OBJECT('id':1) b, '{"id":1}' c)
go to post Vitaliy Serdtsev · Jun 10, 2020 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}
go to post Vitaliy Serdtsev · May 28, 2020 Google the JDBC Driver for Excel. Here is a good option: HXTT Excel Pure Java JDBC (1.2, 2.0, 3.0, 4.0, 4.1, 4.2, 4.3) Drivers for MS Excel (XLS, XLSX)
go to post Vitaliy Serdtsev · May 22, 2020 Added several more types: Class dc.test [ Abstract ] { ClassMethod ValType(ByRef var) As %String { s a(-1)="undefined",a(0)="empty",a(1)="string",a(2)="integer",a(3)="float",a(4)="double",a(5)="object",a(6)="list",a(7)="array",a(8)="bitstring" s r=$s('$d(var):-1, $isobject(var):5, $d(var)>9:7, var="":0, $lv(var):6, $l(var)>254:1, 1:$tr($a($lb(var),2),1245678,1122334)) i r=1 { s s=##class(%Utility).FormatString(var) s:($f(s,"$c(")=4)||($f(s,"$zwc(")=6) r=8 } q a(r) } /// d ##class(dc.test).test() ClassMethod test() { s old=$system.Process.Undefined(2) try{ w ##class(%Utility).FormatString(undef)," => ",..ValType(.undef),! f i="","1",1,1.1,$double(1.1),##class(%ListOfDataTypes).%New(),$lb(),$factor(1),$c(7) { w ##class(%Utility).FormatString(i)," => ",..ValType(.i),! } s (i,i(1))="" w ##class(%Utility).FormatString(i)," => ",..ValType(.i),! }catch{ } d $system.Process.Undefined(old) } } Result: USER>d ##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
go to post Vitaliy Serdtsev · May 22, 2020 InterSystems IRIS Community Edition Limitations Also see %SYSTEM.License, e.g. method MaxConnections
go to post Vitaliy Serdtsev · May 20, 2020 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