Tutorial para Desenvolvedor nível Médio/Senior : Solução de Consulta Geral
Resumo
O que é Query
Queryé um método para encontrar dados que atendem às condições e apresentar os resultados como um conjunto de dados.
Tipo de Query
SQL Query,Usando%SQLQueryeSQL SELECT.Custom Query,Usando a classe%Querye lógica personalizada para gerar resultados.
Observação: antes de falar sobre a solução de Query geral, vamos primeiro entender os fundamentos da Query para compreender os princípios da implementação. Se você já conhece o uso básico da Query, pule esta seção e vá direto para "Desafios".
Fundamentos da Query
Fundamentos do SQL Query
Query QueryPersonByName(name As %String = "") As %SQLQuery(COMPILEMODE = "IMMEDIATE", CONTAINID = 1, ROWSPEC = "id:%Integer:ID,MT_Name:%String:name,age:%String,no:%String", SELECTMODE = "RUNTIME") [ SqlName = QueryPersonByName, SqlProc ]
{
SELECT top 10 ID, MT_Age, MT_Name, MT_No
FROM M_T.Person
WHERE (MT_Name %STARTSWITH :name)
ORDER BY id
}
Descrição:
Query- Declara a palavra-chave do métodoQuery.QueryPersonByName- Declara o métodoQuery.name As %String = ""- Declara a palavra-chave do métodoQuery.%SQLQuery- O tipo daQueryé%SQLQuery.%SQLQueryé uma subclasse de%Querye usa uma forma simples deQueryque permite a gravação de declaraçõesSelect SQLdiretamente no corpo do método.
COMPILEMODE- parâmetro de%SQLQueryque indica o método de compilação.IMMEDIATE- faz a compilação imediata, ao verificar se a declaraçãoSQLatual está correta.DYNAMIC- faz a compilação dinâmica, quando a declaraçãoSQLé compilada no tempo de execução.
CONTAINID- Para definir o número da coluna colocada como o ID retornado.1- Retorna a coluna do ID.0- Não retorna.
SELECTMODE- Indica o método de exibição.RUNTIME- NenhumODBC- Mostra os dados como ODBC.DISPLAY- Mostra os dados como um display.LOGICAL- Mostra os dados de maneira lógica.
ROWSPEC- Fornece o nome da coluna de dados, o tipo de dado e a descrição. Uma lista de nomes de variáveis e tipos de dados separados por aspas e vírgulas. Confira o formato a seguir.id- Indica o nome da coluna de dados.%Integer- Indica o tipo de dado.ID- A descrição dos dados.
SqlProc- Indica que o método pode ser chamado como um procedimento armazenado.SqlName- O nome do procedimento a ser chamado.- Método de chamada não declarado -
call M.Query_QueryPersonByName() - Método de chamada não declarado -
call M.QueryPersonByName()
- Método de chamada não declarado -

- Método de chamada não declarado -
d ##class(%ResultSet).RunQuery(className, queryName, arg...)
USER>d ##class(%ResultSet).RunQuery("M.Query", "QueryPersonByName")
ID:age:name:no:
1:21:yaoxin:314629:
2:29:yx:685381:
3:18:Umansky,Josephine Q.:419268:
4:27:Pape,Ted F.:241661:
5:25:Russell,Howard T.:873214:
6:30:Xenia,Ashley U.:420471:
7:24:Rotterman,Martin O.:578867:
8:18:Drabek,Hannah X.:662167:
9:19:Eno,Mark U.:913628:
11:18:Tsatsulin,Dan Z.:920134:
Fundamentos da Query personalizada
Ao usar uma Query personalizada, geralmente seguimos um modelo fixo. Os seguintes métodos de classe são definidos na mesma classe.
QueryName- Especifica a %Query no tipo de método Query.QueryNameExecute— Esse método principalmente grava a lógica de negócio para buscar os dados e obter o conjunto de dados.QueryNameFetch— Esse método itera o conjunto de dados.QueryNameClose— Esse método exclui dados ou objetos temporários.
**Observação: o exemplo a seguir mostra um modelo de Query personalizada que é um dos casos mais usados, e não um fixo. **
Definição de QueryName
Query QueryPersonByAge(pAge As %String = "", count As %Integer = "10") As %Query(ROWSPEC = "id:%Integer:ID,MT_Name:%String:name,age:%String,no:%String")
{
}
Define o tipo de Query chamado QueryPersonByAge e especificado como %Query. E deixa o corpo da definição da consulta em branco.
Definição de QueryNameExecute
ClassMethod QueryPersonByAgeExecute(ByRef qHandle As %Binary, pAge As %String = "", count As %Integer = "10") As %Status
{
s pid = $i(^IRISTemp) // comment1
s qHandle = $lb(0, pid, 0) // comment2
s index = 1 // comment 3
/* Business Logic comment4 */
s id = ""
for {
s id = $o(^M.T.PersonD(id))
q:(id = "")
q:(id > count)
s data = ^M.T.PersonD(id)
s i = 1
s name = $lg(data, $i(i))
s age = $lg(data, $i(i))
continue:(age < pAge)
s no = $lg(data, $i(i))
d output
}
q $$$OK
output
s ^IRISTemp(pid, index) = $lb(id, age, name, no) // comment 6
s index = index + 1 // comment 7
}
O método QueryNameExecute() fornece toda a lógica de negócio necessária. O nome do método precisa ser QueryNameExecute(), em que QueryName é o nome da Query definida.
其中:
qHandle- Usado para a comunicação com outros métodos que implementam essa consulta.qHandlepode ser de qualquer tipo. O padrão é%Binary.pAge As %String = "", count As %Integer = "10"são os parâmetros de entrada para a Query, que podem ser usados como condições para a lógica de negócio.- Comment 1,
s pid = $i(^IRISTemp)- obtém pid. - Comment 2,
s qHandle = $lb(0, pid, 0)- O primeiro elemento 0 no array indica o início do loop, o segundo elemento pid é usado para obter os dados de^IRISTemp, e o terceiro elemento 0 é usado para percorrer o nó inicial^IRISTemp. - Código de lógica de negócio - é a principal lógica de implementação para buscar o conjunto de dados.
- Comment 3 e comment 7,adiciona nós de índice a
^IRISTemp. - Comment 6,
s ^IRISTemp(pid, index) = $lb(id, name, age, no)- atribuindo valores a^IRISTemppara a travessia subsequente.- Aqui o formato de dados está na forma de
%Library.List, para que o método Fetch não precise converter o tipo. Caso contrário, o método Fetch ainda precisaria converter os dados para o formato de lista interna.
- Aqui o formato de dados está na forma de
Definição de QueryNameFetch
ClassMethod QueryPersonByAgeFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = QueryPersonByAgeExecute ]
{
s end = $li(qHandle, 1) // comment1
s pid = $li(qHandle, 2)
s index = $li(qHandle, 3)
s index = $o(^IRISTemp(pid, index)) // comment2
if index = "" { // comment3
s end = 1
s row = ""
} else {
s row = ^IRISTemp(pid, index)
}
s qHandle = $lb(end, pid, index) // comment4
q $$$OK
}
O método QueryNameFetch() precisa retornar uma única linha de dados em %Library. O nome do método precisa ser QueryNameFetch, em que QueryName é o nome da Query definida.
Em que:
qHandle- Usado para a comunicação com outros métodos que implementam essa consulta. O valor deve ser o definido por Execute.row- indica o valor da linha de dados que é retornada do tipo%Library.Listou a string vazia, se nenhum dado for retornado.end- precisa ser 1 quando a última linha de dados é alcançada. Se 1 não estiver especificado, ele ficará em loop infinito.PlaceAfter- a palavra-chave do métodoPlaceAftercontrola a ordem desse método no código gerado. Isso significa que o métodoQueryPersonByAgeFetché gerado após a geração do métodoQueryPersonByAgeExecute.- Comment 1, linhas 1~3, analisa os valores do array
qHandlepara obterend,pid,index. - Comment 2,
s index = $o(^IRISTemp(pid, index))inicia a travessia de acordo com opideindexanalisado. - Comment 3, a travessia de
^IRISTemp(pid, index), cada linha pertence ao valor da fila, se "index" estiver vazio, precisa ser atribuído a 1 "end". - Comment 4,
s qHandle = $lb(end, pid, index)buscará o end, recopiará o index doqHandlepara a próxima linha de dados se preparar.
Observação: O método Fetch é executado várias vezes e itera o maior número possível de linhas de dados. Os métodos Execute e Close são executados uma vez.
Definição de QueryNameClose
ClassMethod QueryPersonByAgeClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = QueryPersonByAgeExecute ]
{
s pid = $li(qHandle, 2) // comment1
k ^IRISTemp(pid) // comment2
q $$$OK
}
O método QueryNameClose() é encerrado após a conclusão da busca de dados ao remover e limpar os dados ou objetos temporários. O nome do método precisa ser QueryNameClose(), em que QueryName é o nome da Query definida.
qHandle- Usado para a comunicação com outros métodos que implementam essa consulta.- Comment 1,para obter o
pidsalvo porqHandle. - Comment 2,para limpar o
^IRISTempgerado temporariamente.
Chame uma Query personalizada
USER> d ##class(%ResultSet).RunQuery("M.Query", "QueryPersonByAge","20")
ID:name:age:no:
1:yaoxin:21:314629:
2:yx:29:685381:
4:Pape,Ted F.:27:241661:
5:Russell,Howard T.:25:873214:
6:Xenia,Ashley U.:30:420471:
7:Rotterman,Martin O.:24:578867:
- A consulta aqui é para todas as pessoas com mais de 20 anos de idade e com ID menor que 10.
Desafios
Os 2 exemplos básicos acima de Query são provavelmente as duas maneiras mais frequentemente usadas no momento.
No entanto, os desenvolvedores que geralmente escrevem consultas ou relatórios enfrentam vários problemas, como os seguintes:
- Sempre que você escreve uma
Query, definir o cabeçalho da colunaROWSPECé bastante complicado, você consegue especificar o cabeçalho da colunaROWSPECsozinho? - Agora quantos métodos retornam valores
JSON, como converter métodosJSONem Query rapidamente? - É possível escrever uma Query genérica que só precisa escrever a lógica principal de
Execute? - É possível otimizar o modelo atual, por exemplo, substituindo
^IRISTemppor^||IRISTemp?
Há soluções para todas as perguntas acima. Continue lendo a próxima seção do artigo.
Solução
Se você quiser implementar uma Query geral, também precisa conhecer o método de retorno de chamadas QueryNameGetInfo.
ClassMethod Json2QueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
q $$$OK
}
Conforme acima:
colinfo- Esse parâmetro é fundamental para definir a seção do cabeçalho da colunaROWSPEC. Ele contém um elemento de lista para cada coluna declarada emROWSPEC. A forma éname:exttype:caption.name- nome do cabeçalho da coluna.exttype- tipo de dados.caption- descrição.
- O tipo
colinfoprecisa ser%Library.List, e o tipo do cabeçalho de coluna definido também é%Library.List.
Exemplo:
ClassMethod QueryPersonByAgeGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef %qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
s colinfo = $lb($lb("id", "%Integer", "ID"), $lb("age", "%String", ""), $lb("MT_Name", "%String", "name"), $lb("no", "%String", ""))
s parminfo = ""
s idinfo = ""
q $$$OK
}
Descrição: Ordem de execução da Query Execute -> GetInfo -> Fetch(n) -> Close.
As soluções a seguir são descritas separadamente:
- Gerar uma Query dinamicamente a partir de dados ou métodos
Json - Geração dinâmica de Query por uma declaração
Select Sql - Geração dinâmica de Query por Query
- Compatibilidade com a Query legada e geração de colunas Query por meio de parâmetros
- Definição de uma Query genérica, só com a implementação do método Execute
Geração dinâmica de uma Query por dados ou métodos Json
Definindo os métodos Json
- O método
Jsonpode ser definido de maneira arbitrária, este exemplo é apenas para fins de teste. O seguinte método: faça uma consulta na unidade de disco atual do computador para gerar resultadosJson.
ClassMethod QueryDrives(fullyQualified = 1, type = "D")
{
s array = []
s rs = ##class(%ResultSet).%New()
s rs.ClassName = "%File"
s rs.QueryName = "DriveList"
d rs.Execute(fullyQualified)
while (rs.Next()) {
s drive = rs.Get("Drive")
s drive = $zcvt(drive, "U")
s obj = {}
s obj.type = "D"
continue:(type '= "D")
s obj.drive = drive
d array.%Push(obj)
}
q array
}
Execute:
USER> w ##class(M.Query).QueryDrives().%ToJSON()
[{"type":"D","drive":"C:\\"},{"type":"D","drive":"D:\\"},{"type":"D","drive":"E:\\"},{"type":"D","drive":"F:\\"},{"type":"D","drive":"G:\\"}]
Defina QueryName
Query Json2Query(className As %String, methodName As %String, arg...) As %Query
{
}
其中:
className- nome da classe.methodName- nome do métodoJsona ser executado.arg... - parâmetros do método a ser executado.
Defina QueryNameExecute
ClassMethod Json2QueryExecute(ByRef qHandle As %Binary, className As %String, methodName As %String, arg...) As %Status
{
s array = $classmethod(className, methodName, arg...) // comment1
if ('$isobject(array)) { // comment2
s array = [].%FromJSON(array)
}
q:('array.%IsA("%Library.DynamicArray")) $$$ERROR($$$GeneralError, "Not a Array Object") // comment3
q:(array.%Size() = 0) $$$ERROR($$$GeneralError, "No data") // comment4
s qHandle = array // comment5
q $$$OK
}
- Comment 1,usando o mecanismo de reflexão para chamar o método desejado e obter o valor de retorno.
- Comment 2,determina se a string retornada é convertida em objeto Json.
- Comment 3,determina se a string retornada é convertida em objeto Json.
- Comment 4,o comprimento do array
Json0 emite uma mensagem de erro. - Comment 5,obtém o objeto do array.
Defina QueryNameGetInfo
ClassMethod Json2QueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
s colinfo = $lb() // comment1
s count = 1
s obj = qHandle.%GetIterator()
if obj.%GetNext(.key, .value) {
s obj = value.%GetIterator()
while obj.%GetNext(.objKey, .objValue) { // comment2
s $li(colinfo, count) = $lb(objKey)
s count = $i(count)
}
}
s parminfo = "" // comment3
s idinfo = "" // comment4
s qHandle = qHandle.%GetIterator() // comment5
q $$$OK
}
- Comment 1,inicializa o array
colinfo, atribui obj ao objeto iteradorqHandle.%GetIterator(). - Comment 2,itera o objeto
Jsonpara obter Key e atribui um valor acolinfopelo$li. - Comment 3,inicializa
parminfoou o erro é relatado. - Comment 4,inicializa
idinfoou o erro é relatado. - Comment 5,obtém o objeto iterador
Defina QueryNameFetch
ClassMethod Json2QueryFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = Json2QueryExecute ]
{
s iter = qHandle
q:($g(iter) = "") $$$OK
if iter.%GetNext(.key, .value) { // comment1
s row = ""
s obj = value.%GetIterator()
while obj.%GetNext(.objKey, .objValue) { // comment2
if ( $g(row) = "" ) {
s row = $lb(objValue)
} else {
s row = row _ $lb(objValue)
}
}
s end = 0
} else {
s row = ""
s end = 1 // comment3
}
q $$$OK
}
- Comment 1,obtém a linha de dados Json do iterador atual.
- Comment 2,itera o objeto
Jsone concatena o valor com a linha para$lb. - Comment 3,se não houver conjunto de dados, define
endcomo1para indicar o final da travessia.
Defina QueryNameClose
ClassMethod Json2QueryClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = Json2QueryFetch ]
{
s qHandle = "" // comment1
q $$$OK
}
- Comment 1,deixa o objeto
qHandleem branco.
Observação: na verdade, M tem um mecanismo de reciclagem relacionado e o método Close pode ser usado sem a declaração dele.
Chamando o método Json2Query
USER>d ##class(%ResultSet).RunQuery("M.Query","Json2Query","M.Query","QueryDrives","0","D")
type:drive:
D:D::
D:E::
D:F::
D:G::
USER>d ##class(%ResultSet).RunQuery("M.Query","Json2Query","M.Query","QueryDrives","1","D")
type:drive:
D:D:\:
D:E:\:
D:F:\:
D:G:\:
Geração dinâmica de Query por uma declaração Select Sql
Defina QueryName
Query Sql2Query(sql As %String, mode As %String = 1) As %Query
{
}
sql- variável que representa a declaraçãoSQLa ser escrita.mode- mostra o tipo de formato de dados.- 0 - formato lógico
- 1 - formato lógico
- 2 - formato lógico
Defina QueryNameExecute
ClassMethod Sql2QueryExecute(ByRef qHandle As %Binary, sql As %String, mode As %String = 1) As %Status
{
s sqlStatement = ##class(%SQL.Statement).%New()
s sqlStatement.%SelectMode = mode // comment1
s sqlStatus = sqlStatement.%Prepare(.sql) // comment2
q:$$$ISERR(sqlStatus) sqlStatus
s sqlResult = sqlStatement.%Execute()
s stateType = sqlStatement.%Metadata.statementType
q:('stateType = 1 ) $$$ERROR($$$GeneralError, "Not a select statement") // comment3
s qHandle = {}
s qHandle.sqlResult = sqlResult // comment4
s qHandle.sqlStatement = sqlStatement
q $$$OK
}
- Comment 1,define o formato de exibição dos dados de
SQL. - Comment 2,transmite a declaração de
SQLpara obter o objetosqlStatementesqlResult. - Comment 3,transmite a declaração
SQLnão selecionada, emite uma mensagem de erro. - Comment 4,o
qHandletransmitido nos dois objetos sãosqlResultesqlStatement.sqlResulté utilizado para percorrer os dados usados.sqlStatementé usado para obter informações do cabeçalho da coluna.
Defina QueryNameGetInfo
ClassMethod Sql2QueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
s colinfo = $lb()
s sqlStatement = qHandle.sqlStatement // comment1
s count = 1
s column = ""
for {
s column = $o(sqlStatement.%Metadata.columnIndex(column))
q:(column = "")
s data = sqlStatement.%Metadata.columnIndex(column)
s $li(colinfo, count) = $lb($lg(data, 2)) // comment2
s count = $i(count)
}
s parminfo = ""
s idinfo = ""
q $$$OK
}
- Comment 1,obtém o objeto
sqlStatementporqHandle. - Comment 2,dá a lista de
colinfopara a atribuição cíclica das informações de cabeçalho da coluna.
Defina QueryNameFetch
ClassMethod Sql2QueryFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = Sql2QueryExecute ]
{
s sqlStatement = qHandle.sqlStatement // comment1
s sqlResult = qHandle.sqlResult
s colCount = sqlResult.%ResultColumnCount // comment2
if (sqlResult.%Next()) {
for i = 1 : 1 : colCount{
s val = sqlResult.%GetData(i)
if ( $g(row) = "" ) { // comment3
s row = $lb(val)
} else {
s row = row _ $lb(val)
}
}
s end = 0
} else {
s row = ""
s end = 1
}
s qHandle.sqlResult = sqlResult // comment4
q $$$OK
}
- Comment 1,obtém o objeto
sqlStatementesqlResultporqHandle. - Comment 2,obtém o número de colunas, equivale a obter uma linha de dados do número de itens.
- Comment 3,itera os dados para atribuir um valor à linha.
- Comment 4,objeto
qHandle.sqlResult, atribui um valor ao objeto atual do loop.
Defina QueryNameClose
Podemos pular isso.
Observação: na verdade, M tem um mecanismo de reciclagem relacionado, e o método Close não deve ser declarado.
Chame o método Sql2Query
USER>d ##class(%ResultSet).RunQuery("M.Query","Sql2Query","select * from M_T.Person", 1)
id:MT_Age:MT_Name:MT_No:
1:21:yaoxin:314629:
2:29:yx:685381:
3:18:Umansky,Josephine Q.:419268:
4:27:Pape,Ted F.:241661:
5:25:Russell,Howard T.:873214:
6:30:Xenia,Ashley U.:420471:
7:24:Rotterman,Martin O.:578867:
8:18:Drabek,Hannah X.:662167:
9:19:Eno,Mark U.:913628:
...
100:24:Nathanson,Jocelyn A.:147578:
USER>d ##class(%ResultSet).RunQuery("M.Query","Sql2Query","select ID,MT_Name from M_T.Person")
id:MT_Name:
1:yaoxin:
2:yx:
3:Umansky,Josephine Q.:
4:Pape,Ted F.:
5:Russell,Howard T.:
6:Xenia,Ashley U.:
7:Rotterman,Martin O.:
...
100:Nathanson,Jocelyn A.:
USER>d ##class(%ResultSet).RunQuery("M.Query","Sql2Query","select top 10 ID as id from M_T.Person")
id:
1:
2:
3:
4:
5:
6:
7:
8:
9:
11:
Chame o método Sql2Query
Defina QueryName
Query Query2Query(className As %String, queryName As %String, arg...) As %Query
{
}
className- nome da classe.queryName- nome do método Query a ser executado.arg... - parâmetros do método Query a ser executado.
Defina QueryNameExecute
ClassMethod Query2QueryExecute(ByRef qHandle As %Binary, className As %String, queryName As %String, arg...) As %Status
{
s sqlStatement = ##class(%SQL.Statement).%New()
s sqlStatus = sqlStatement.%PrepareClassQuery(className, queryName)
q:$$$ISERR(sqlStatus) sqlStatus
s sqlResult = sqlStatement.%Execute()
s qHandle = {}
s qHandle.sqlResult = sqlResult
s qHandle.sqlStatement = sqlStatement
q $$$OK
}
- Semelhante a
Sql2Query
Defina QueryNameGetInfo
ClassMethod Query2QueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
s colinfo = $lb()
s sqlStatement = qHandle.sqlStatement
s count = 1
s column = ""
for {
s column = $o(sqlStatement.%Metadata.columnIndex(column))
q:(column = "")
s data = sqlStatement.%Metadata.columnIndex(column)
s $li(colinfo, count) = $lb($lg(data, 2))
s count = $i(count)
}
s parminfo = ""
s idinfo = ""
q $$$OK
}
- Semelhante a
Sql2Query
Defina QueryNameFetch
ClassMethod Query2QueryFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = Query2QueryExecute ]
{
s sqlStatement = qHandle.sqlStatement
s sqlResult = qHandle.sqlResult
s colCount = sqlResult.%ResultColumnCount
if (sqlResult.%Next()) {
for i = 1 : 1 : colCount{
s val = sqlResult.%GetData(i)
if ( $g(row) = "" ) {
s row = $lb(val)
} else {
s row = row _ $lb(val)
}
}
s end = 0
} else {
s row = ""
s end = 1
}
s qHandle.sqlResult = sqlResult
q $$$OK
}
- Semelhante a
Sql2Query
Chame Query2Query
USER>d ##class(%ResultSet).RunQuery("M.Query","Query2Query","M.Query","QueryPersonByName")
age:id:MT_Name:no:
1:21:yaoxin:314629:
2:29:yx:685381:
3:18:Umansky,Josephine Q.:419268:
4:27:Pape,Ted F.:241661:
5:25:Russell,Howard T.:873214:
6:30:Xenia,Ashley U.:420471:
7:24:Rotterman,Martin O.:578867:
8:18:Drabek,Hannah X.:662167:
9:19:Eno,Mark U.:913628:
11:18:Tsatsulin,Dan Z.:920134:
Compatibilidade com a Query tradicional e geração de colunas Query por parâmetro
- Compatibilidade com a Query tradicional e geração de colunas Query por parâmetro
- Compatibilidade com a definição de colunas pelo formato de parâmetro sem especificar os parâmetros
ROWSPEC - Otimização para transformar
^IRISTempem^||IRISTemp
Defina M.CommonQuery
Class M.CommonQuery Extends %Query
{
ClassMethod Close(ByRef qHandle As %Binary) As %Status [ CodeMode = generator, PlaceAfter = Execute, ProcedureBlock = 1, ServerOnly = 1 ]
{
s %code($i(%code))= (" s pid = $li(qHandle, 2)")
s %code($i(%code))= (" k ^||GlobalTemp(pid)")
s %code($i(%code))= (" q $$$OK")
q $$$OK
}
ClassMethod Fetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ CodeMode = generator, PlaceAfter = Execute, ProcedureBlock = 1, ServerOnly = 1 ]
{
s %code($i(%code))= (" s end = $li(qHandle, 1)")
s %code($i(%code))= (" s pid = $li(qHandle, 2)")
s %code($i(%code))= (" s ind = $li(qHandle, 3)")
s %code($i(%code))= (" s ind = $o(^||GlobalTemp(pid, ind))")
s %code($i(%code))= (" if (ind = """") { ")
s %code($i(%code))= (" s end = 1")
s %code($i(%code))= (" s row = """"")
s %code($i(%code))= (" } else { ")
s %code($i(%code))= (" s row = ^||GlobalTemp(pid, ind)")
s %code($i(%code))= (" }")
s %code($i(%code))= (" s qHandle = $lb(end, pid, ind)")
s %code($i(%code))= (" q $$$OK")
q $$$OK
}
ClassMethod GetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, ByRef extinfo As %List) As %Status [ CodeMode = generator, ServerOnly = 1 ]
{
s %code($i(%code))= (" s colinfo = $lb()")
s %code($i(%code))= (" s column = $lg(qHandle, 4)")
s %code($i(%code))= (" if ($lv(column)) {")
s %code($i(%code))= (" for i = 1 : 1 : $ll(column) {")
s %code($i(%code))= (" s $li(colinfo, i) = $lb(""Column"" _ i )")
s %code($i(%code))= (" } ")
s %code($i(%code))= (" } else {")
s %code($i(%code))= (" s len = $l(column, "","")")
s %code($i(%code))= (" for i = 1 : 1 : len {")
s %code($i(%code))= (" s $li(colinfo, i) = $lb($p(column, "","", i))")
s %code($i(%code))= (" }")
s %code($i(%code))= (" }")
s %code($i(%code))= (" s parminfo = """"")
s %code($i(%code))= (" s idinfo = """"")
s %code($i(%code))= (" q $$$OK")
q $$$OK
}
}
Defina QueryName
Query CustomColumnQuery(column As %List) As M.CommonQuery
{
}
column- variável que indica a coluna do parâmetro a ser personalizado.M.CommonQuery- tipo de Query personalizada, sem necessidade de escrever métodos GetInfo, Fetch e Close.
Defina QueryNameExecute
QueryNameExecute é compatível com três maneiras de definição dos cabeçalhos de coluna:
- O cabeçalho de coluna é transmitido pelo parâmetro
columne implementado da seguinte maneira.
ClassMethod CustomColumnQueryExecute(ByRef qHandle As %Binary, column As %List) As %Status
{
s pid = $i(^||GlobalTemp)
s qHandle = $lb(0, pid, 0)
s $li(qHandle, 4) = column // Mode 1 This location is required
s ind = 1
s id = ""
for {
s id = $o(^M.T.PersonD(id))
q:(id = "")
s data = ^M.T.PersonD(id)
s i = 1
s name = $lg(data, $i(i))
s age = $lg(data, $i(i))
s no = $lg(data, $i(i))
d output
}
q $$$OK
output
s data = $lb(id, name)
s ^||GlobalTemp(pid, ind)=data
s ind = ind + 1
}
USER> d ##class(%ResultSet).RunQuery("M.Query","CustomColumnQuery","ID,Name")
ID:Name:
1:yaoxin:
2:yx:
3:Umansky,Josephine Q.:
4:Pape,Ted F.:
5:Russell,Howard T.:
- Sem a transmissão do parâmetro
column, os cabeçalhos de coluna são gerados automaticamente com base no número de dados de lista, implementado da seguinte maneira.
ClassMethod CustomColumnQueryExecute(ByRef qHandle As %Binary, column As %String = "") As %Status
{
s pid = $i(^||GlobalTemp)
s qHandle = $lb(0, pid, 0)
s ind = 1
s id = ""
for {
s id = $o(^M.T.PersonD(id))
q:(id = "")
s data = ^M.T.PersonD(id)
s i = 1
s name = $lg(data, $i(i))
s age = $lg(data, $i(i))
s no = $lg(data, $i(i))
s data = $lb(id, name, no)
q:(id > 5)
d output
}
s $li(qHandle, 4) = data // Mode 2 This location is required
q $$$OK
output
s ^||GlobalTemp(pid, ind)=data
s ind = ind + 1
}
USER>d ##class(%ResultSet).RunQuery("M.Query","CustomColumnQuery")
Column1:Column2:Column3:
1:yaoxin:314629:
2:yx:685381:
3:Umansky,Josephine Q.:419268:
4:Pape,Ted F.:241661:
5:Russell,Howard T.:873214:
- 3. Sem a transmissão do parâmetro
column, personalize as informações do cabeçalho da coluna pelo métodoExecute, implementado da seguinte maneira.
ClassMethod CustomColumnQueryExecute0(ByRef qHandle As %Binary, column As %String = "") As %Status
{
s pid = $i(^||GlobalTemp)
s qHandle = $lb(0, pid, 0)
s ind = 1
s id = ""
for {
s id = $o(^M.T.PersonD(id))
q:(id = "")
s data = ^M.T.PersonD(id)
s i = 1
s name = $lg(data, $i(i))
s age = $lg(data, $i(i))
s no = $lg(data, $i(i))
s data = $lb(id, name, no)
q:(id > 5)
d output
}
s $li(qHandle, 4) = "id,name,age" // Option 3 This position is required
q $$$OK
output
s ^||GlobalTemp(pid, ind)=data
s ind = ind + 1
}
USER>d ##class(%ResultSet).RunQuery("M.Query","CustomColumnQuery")
id:name:age:
1:yaoxin:314629:
2:yx:685381:
3:Umansky,Josephine Q.:419268:
4:Pape,Ted F.:241661:
5:Russell,Howard T.:873214:
Execute CustomColumnQuery
USER>d ##class(%ResultSet).RunQuery("M.Query","CustomColumnQuery","ID,Name")
ID:Name:
1:yaoxin:
2:yx:
3:Umansky,Josephine Q.:
4:Pape,Ted F.:
5:Russell,Howard T.:
6:Xenia,Ashley U.:
Definição de uma Query genérica, só com a implementação do método Execute
Para implementar uma Query geral, você precisa abstrair os métodos e subclasses para substituí-los. Então, primeiro defina a classe mãe.
Defina M.CommonQuery
Class M.BaseQuery Extends %RegisteredObject
{
/// d ##class(%ResultSet).RunQuery("M.BaseQuery","CustomQuery","id,name")
Query CustomQuery(column As %List, arg...) As %Query
{
}
ClassMethod CustomQueryExecute(ByRef qHandle As %Binary, column As %List, arg...) As %Status
{
s qHandle = $lb(0, 0) // comment1
s $li(qHandle, 3) = column // comment2
d ..QueryLogic(arg...) // comment3
q $$$OK
}
ClassMethod CustomQueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, ByRef extinfo As %List) As %Status
{
s colinfo = $lb()
s column = $lg(qHandle ,3)
s len = $l(column, ",")
for i = 1 : 1 : len {
s $li(colinfo, i) = $lb($p(column, ",", i)) // comment5
}
s parminfo = ""
s idinfo = ""
q $$$OK
}
ClassMethod CustomQueryClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = CustomQueryExecute ]
{
k %zQueryList // comment7
q $$$OK
}
ClassMethod CustomQueryFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = CustomQueryExecute ]
{
s end = $li(qHandle,1)
s index = $li(qHandle,2)
s index = $o(%zQueryList(index))
if index = "" { // comment6
s end = 1
s row = ""
} else {
s row = %zQueryList(index)
}
s qHandle = $lb(end, index)
Quit $$$OK
}
ClassMethod QueryLogic(arg...) [ Abstract ]
{
// comment4
}
}
column- indica a variável para personalizar a coluna de parâmetro.arg...- os parâmetros a serem transmitidos.- Comment 1,aqui são feitas algumas mudanças,
qHandlesó registra "end" e "index", porque, se for uma variável global ou uma global de processo particular, só é válida para o processo atual, entãopidpode ser omitido. - Comment 2,a terceira posição de
qHandleserá transmitida no nome do cabeçalho da coluna. - Comment 3,chama o método de lógica de negócio a ser implementado, esse método é abstrato e precisa de subclasse para a implementação.
- Comment 4,chama o método de lógica de negócio a ser implementado, esse método é abstrato e precisa de subclasse para a implementação.
- Comment 5,obtém o cabeçalho da coluna definido dinamicamente.
- Comment 6,itera as variáveis globais.
- Comment 7,após a travessia, as variáveis globais serão limpas.
Defina a subclasse M.PersonQuery para herdar de M.BaseQuery e implementar o método QueryLogic
- Tudo o que precisamos aqui é atribuir um valor à variável global
%zQueryList($i(count)). O modelo fixo foi abstraído para a classe mãe.
ClassMethod QueryLogic(arg...)
{
s pName = arg(1)
s id = ""
for {
s id = $o(^M.T.PersonD(id))
q:(id = "")
s data = ^M.T.PersonD(id)
s i = 1
s name = $lg(data, $i(i))
continue:(pName '= "")&&(name '= pName)
s age = $lg(data, $i(i))
s no = $lg(data, $i(i))
s %zQueryList($i(count)) = $lb(id, name, age)
}
}
Chame o método CustomQuery
USER>d ##class(%ResultSet).RunQuery("M.PersonQuery","CustomQuery","ID,Name,Age", "yaoxin")
ID:Name:Age:
1:yaoxin:21:
Observação: as variáveis globais são usadas aqui como transmissão de dados, então, se os dados forem muito grandes, pode ocorrer vazamento de memória. Basta mudar para global de processo particular. Cabe ao leitor implementá-lo com base nessa lógica.
Observação: assim, uma classe só pode declarar uma Query. Se você quiser declarar mais de uma Query para uma classe, considere mudar para a abordagem de compatibilidade com a Query tradicional.
Gere o Json por Query
ClassMethod Query2Json(className, queryName, arg...)
{
s array = []
s rs = ##class(%ResultSet).%New()
s rs.ClassName = className
s rs.QueryName = queryName
d rs.Execute(arg...)
s array = []
#; 属性值
while (rs.Next()) {
s valStr = ""
s obj = {}
for i = 1 : 1 : rs.GetColumnCount(){
s columnName = rs.GetColumnName(i)
s val = rs.Data(columnName)
d obj.%Set(columnName, val)
}
d array.%Push(obj)
}
q array.%ToJSON()
}
USER>w ##class(Util.JsonUtils).Query2Json("%SYSTEM.License","Summary")
[{"LicenseUnitUse":"当前使用的软件许可单元 ","Local":"1","Distributed":"1"},{"Li censeUnitUse":"使用的最大软件许可单元数 ","Local":"15","Distributed":"15"},{"Lic enseUnitUse":"授权的软件许可单元 ","Local":"300","Distributed":"300"},{"LicenseU nitUse":"当前连接 ","Local":"3","Distributed":"3"},{"LicenseUnitUse":"最大连接数 ","Local":"17","Distributed":"17"}]
Gere o csv por Query
ClassMethod Query2Csv(className, queryName, filePath, arg...)
{
s file = ##class(%FileCharacterStream).%New()
s file.Filename = filePath
s array = []
s rs = ##class(%ResultSet).%New()
s rs.ClassName = className
s rs.QueryName = queryName
d rs.Execute(arg...)
#; 列名
s colStr = ""
for i = 1 : 1 : rs.GetColumnCount(){
s columnName = rs.GetColumnName(i)
s colStr = $s(colStr = "" : columnName, 1 : colStr _ "," _ columnName)
}
d file.Write(colStr)
#; 属性值
while (rs.Next()) {
s valStr = ""
for i = 1 : 1 : rs.GetColumnCount(){
s columnName = rs.GetColumnName(i)
s val = rs.Data(columnName)
s valStr = $s(valStr = "" : val, 1 : valStr _ "," _ val)
}
d file.Write($c(10) _ valStr)
}
d file.%Save()
q $$$OK
}
USER>w ##class(Util.FileUtils).Query2Csv("%SYSTEM.License","Summary","E:\m\CsvFile2.csv")
1


Resumo
- Entender o parâmetro
qHandlee o métodoGetInfoé fundamental para implementar umaQuerygenérica. - O uso de uma Query genérica pode melhorar a eficácia do desenvolvimento.
- O uso de uma Query genérica pode solucionar o problema de adaptação dos dados.
Acima estão alguns dos meus entendimentos sobre Query. Devido à minha compreensão limitada, comentários e outros contatos são bem-vindos.
Se uma boa ideia tiver o uso banido no futuro só porque alguém pensou nela primeiro, a sociedade inteira precisará fazer muito mais desvios, algo que o espírito do software gratuito sempre expressou. - Richard Matthew Stallman