Artigo
· Set. 4, 2023 8min de leitura

Consulta como %Query ou Query baseada em ObjectScript

Neste tutorial, quero falar sobre consultas de classe. Para ser mais precisa, sobre as consultas baseadas em código escrito pelo usuário:

Muitas pessoas ignoram esse tipo de consulta só porque não estão confortáveis em escrever uma grande quantidade de código ObjectScript para os métodos ou não veem como podem usá-lo nos apps relacionais. No entanto, para ser sincera, para mim — é uma das criações mais legais para o modelo relacional no IRIS! Ele deixa você expor as informações que quiser (não se limita a tabelas do seu banco de dados) como um conjunto de resultados para um cliente. Portanto, você pode basicamente empacotar qualquer dado armazenado no seu banco de dados e além dele em uma "tabela virtual ordenada" que o usuário pode consultar. E, do ponto de vista de um usuário, será o mesmo conjunto de resultados como se ele(a) tivesse consultado uma tabela ou uma visualização.

Como fundamento, aqui estão alguns exemplos do que você pode usar como dados para sua consulta:

  • Indicadores de sistema do IRIS e seu SO local (se o IRIS tiver acesso a eles)
  • Resultados de consultas externas, como REST ou SOAP (por exemplo, se você não quiser enviar solicitações diretamente de um cliente devido à segurança ou por outros motivos)
  • Resultados do cursor incorporado ou da declaração simples
  • Quaisquer dados ad hoc que você mesmo pode compor
  • Praticamente qualquer outra coisa que venha à mente e seja possível obter usando ObjectScript

Vamos ver agora como isso funciona.

Se você estiver usando o Studio – é bastante fácil e simples.

Na sua classe cls, você pode acessar o menu Class – Add – Query:

Na barra de ferramentas "Members", clique no ícone Query:

O New Query Wizard (Assistente de nova consulta) será aberto. Ele guiará você pelas etapas da criação dos métodos necessários para sua consulta funcionar.

Vamos analisar esse processo usando o exemplo mais simples de empacotamento do resultado do cursor incorporado. Aqui, na verdade, vamos escrever uma consulta em uma tabela, mas é apenas para simplificar. Além disso, vamos analisar exemplos do uso de %Query para retornar dados de outras fontes.

Vamos supor que temos uma classe Sample.Human que estende %Persistent e %Populate. Usarei o último para preencher os dados porque sou preguiçosa e porque eu posso:

Class Sample.Human Extends (%Persistent, %Populate)
{

Property Name As %Name;
Property DoB As %Date(MAXVAL = 61727, MINVAL = 32507);
Property Age As %Integer [ Calculated, SqlComputeCode = {set {Age} = $h - {DoB} \ 365.25}, SqlComputed ];
}

Quando começamos a criar a consulta, o primeiro passo do Wizard é definir um nome e um tipo:

O próximo passo é definir os parâmetros de entrada, se houver algum. Para adicionar um novo parâmetro, clique no botão no topo da coluna de botões à direita:

A próxima etapa é muito importante – você precisa definir nomes e tipos ou colunas em um conjunto de resultados resultante. Ao criar uma consulta baseada em SQL, essa etapa é feita automaticamente pelo sistema – ele só analisa as colunas que você adicionou na consulta e pega os nomes e tipos de dados. A consulta baseada em ObjectScript talvez não tenha nenhum campo, então você precisa informar ao sistema o que esperar.

É isso. Como resultado do Wizard, você terá 3 novos ClassMethods e uma Query na sua classe:

/// Get all the names and ages of people whose age is greater or equal than nput parameter
Query GetAllOlderThan(Age As %Integer = 65) As %Query(ROWSPEC = "Name:%Name,Age:%Integer")
{
}

ClassMethod GetAllOlderThanExecute(ByRef qHandle As %Binary, Age As %Integer = 65) As %Status
{
               Quit $$$OK
}

ClassMethod GetAllOlderThanClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = GetAllOlderThanExecute ]
{
               Quit $$$OK
}

ClassMethod GetAllOlderThanFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = GetAllOlderThanExecute ]
{
               Quit $$$OK
}

}

A Query só informa ao sistema que existe uma consulta em uma classe que pode ser chamada por um nome com um parâmetro e retornará 2 colunas no resultado. Qualquer código inserido dentro da Query será desconsiderado.

Toda a codificação é feita no ClassMethods. Eles são responsáveis por preencher nosso conjunto de resultados "virtual" ( <QueryName>Execute– chamado quando uma declaração SQL é executada), retornando a próxima linha (<QueryName>Fetch) e deixando tudo organizado (<QueryName>Close. Eles têm um parâmetro em comum – ByRef qHandle As %Binary em que os dados são armazenados e transmitidos entre métodos. Como você pode ver, os dados são binários, então você pode colocar qualquer coisa dentro dele (seguindo seu bom senso). Em  Age As %Integer = 65. And in <QueryName>Fetch there are two additional parameters:

  • ByRef Row As %Listé uma %List que contém a linha atual do conjunto de resultados "virtual" com o número e os valores das colunas descritos na ROWSPEC da Query (Name:%Name, Age:%Integer).
  • ByRef AtEnd As %Integer = 0 – isso é uma sinalização que indica se a linha atual é a última (1) ou não (0).

Se você estiver trabalhando no VSCode, terá que escrever todas as assinaturas por conta própria e tentar não cometer erros😊

Agora que sabemos qual parâmetro é responsável pelo quê, vamos analisar o código:

/// Get all the names and ages of people whose age is greater or equal than nput parameter
Query GetAllOlderThan(Age As %Integer = 65) As %Query(ROWSPEC = "Name:%Name,Age:%Integer") [ SqlProc ]
{
}

ClassMethod GetAllOlderThanExecute(ByRef qHandle As %Binary, Age As %Integer = 65) As %Status
{
     &sql(DECLARE C CURSOR FOR
        SELECT Name, Age
          FROM Sample.Human 
         WHERE Age >= :Age
     )
     &sql(OPEN C)
    Quit $$$OK
}

ClassMethod GetAllOlderThanClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = GetAllOlderThanExecute ]
{
    &sql(CLOSE C)
    Quit $$$OK
}

ClassMethod GetAllOlderThanFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = GetAllOlderThanExecute ]
{
    &sql(FETCH C INTO :Name, :Age)
    If (SQLCODE'=0) {
        Set AtEnd = 1
        Set Row = ""
        Quit $$$OK
    }
    Set Row = $Lb(Name, Age)
    Quit $$$OK
}

Esse código declara e abre um cursor incorporado que seleciona os nomes e as idades das pessoas com mais de Age em <QueryName>Execute, busca o resultado do cursor, forma uma lista em <QueryName>Fetch e depois a fecha em &;lt;QueryName>Close.

Podemos chamá-lo usando código:

 SET tStatement = ##class(%SQL.Statement).%New()
 SET rstatus = tStatement.%PrepareClassQuery("Sample.Human","GetAllOlderThan")
 SET rset = tStatement.%Execute()
 DO rset.%Display()

Muito bom e simples. E provavelmente não é o que você estava procurando.

Como exemplo do uso sem dados armazenados no banco de dados, digamos que por algum motivo não temos acesso às tabelas reais, mas precisamos conferir se nosso aplicativo funciona como esperado. Precisamos que a consulta retorne alguns dados de teste. Podemos reescrever esse exemplo para que os dados sejam gerados automaticamente durante a busca de uma nova linha.

Obviamente, não precisamos salvar os dados na memória, então não há necessidade de preencher a variável qHandle em <QueryName>Execute – podemos criar os dados dentro de <QueryName>Fetch. E, em qHandle vamos armazenar o tamanho do conjunto de resultados a ser retornado (um número aleatório menor que 200, por exemplo) e o número da linha atual que vamos incrementar em <QueryName>Fetch. No final, vamos obter este código:

Query GetAllOlderThan(Age As %Integer = 65) As %Query(ROWSPEC = "Name:%Name,Age:%Integer") [ SqlProc ]
{
}

ClassMethod GetAllOlderThanExecute(ByRef qHandle As %Binary, Age As %Integer = 65) As %Status
{
    set qHandle = $lb($random(200), 0, Age)
    Quit $$$OK
}

ClassMethod GetAllOlderThanClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = GetAllOlderThanExecute ]
{
    set qHandle = ""
    Quit $$$OK
}

ClassMethod GetAllOlderThanFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = GetAllOlderThanExecute ]
{
    if $ListGet(qHandle, 2) = $ListGet(qHandle, 1)
    {
        Set Row = ""
        set AtEnd = 1
    } else
    {
        Set Row = $Lb(##class(%PopulateUtils).Name(), ##class(%PopulateUtils).Integer($ListGet(qHandle, 3), 90))
        set $list(qHandle, 2) = $list(qHandle, 2) + 1
    }
    Quit $$$OK
}

}

Como você pode ver, estou gerando dados ad hoc. Isso significa que você pode obter seus dados de qualquer lugar, empacotar em um conjunto de resultados e torná-lo acessível pelo app que usa ODBC/JDBC fora do banco de dados IRIS. O que, por sua vez, significa que você pode usar o acesso relacional habitual para obter dados não relacionais se você conseguir estruturá-lo.

Discussão (0)1
Entre ou crie uma conta para continuar