Wednesday, October 31, 2012

Online LRU cleaning changes / mudanças na limpeza das LRUs online

This article is written in English and Portuguese
Este artigo está escrito em Inglês e Português

English version:

Very quick post to document something which is not properly documented. While working at a customer I had the need to change the LRU cleaning parameters (min_dirty, max_dirty) without stopping the engine. The fine manual says this can be done by dynamically change the AUTO_LRU_TUNING parameter:

onmode -wm AUTO_LRU_TUNING=1,min=0.3,max=1

And this works fine... But there is a catch: If, like me yesterday, you have more than one BUFFERPOOL this will change the max/min for all of them. You may not want that... I had the solution somewhere in my personal notes, but as usual this was hard to find. Now that I did, I'm just sharing.... So, the solution would be:

onmode -wm AUTO_LRU_TUNING=bpool=0,1,min=0.3,max=1

Et voilá... the non documented option bpool allows us to define which of the BUFFERPOOL we want to change.


Versão Portuguesa:

Um artigo muito rápido para deixar documentado algo que não está completament documentado. Enquanto estava a trabalhar num cliente surgiu a necessidade de alterar os parâmetros de limpeza das LRUs (min_dirty, max_dirty) sem parar o motor. O manual refere que tal pode ser feito dinamicamente mudando o parâmetro AUTO_LRU_TUNING:

onmode -wm AUTO_LRU_TUNING=1,min=0.3,max=1


E isto funciona como esperado. Mas há um problema: Se estiver como eu ontem, em que tinha mais que uma BUFFERPOOL isto mudaria os parâmetros da todas elas. Podemos não desejar isto. Tinha a solução algures nas minhas notas, mas como é habitual não foi fácil de encontrar. Agora que o fiz estou apenas a partilhar informação. Assim a solução seria:


onmode -wm AUTO_LRU_TUNING=bpool=0,1,min=0.3,max=1


Et voilá... a opção não documentada "bpool" permite-nos definir qual das pools queremos mudar

Monday, October 29, 2012

Informix for newbies / Informix para novatos

This article is written in English and Portuguese
Este artigo está escrito em Inglês e Português


English Version:

In recent weeks I've received or seen several requests for information from newcomers to Informix. This was requested directly while working at a customer, through direct email (blog driven) and was a public request in the IIUG mailing list. I suppose I could create a great blog post talking about how this means that Informix is raising more and more interest, but sharing information was the main reason to create this blog and I intend to stick with that. So, the purpose of this article is simply to share some information with the hope that it can help people who is interested in finding more about Informix. So my suggestions are (no special order):

  • http://www.informix-zone.com/
    This is a site created by Eric Herber. It was created several years ago (before I created my blog) and contains a lot of Informix related information. Specifically and for beginners, you can find an installation guide, a link to Informix Developer Edition download and links to the Informix Virtual appliance, a virtual machine image which already contains a pre-installed Informix environment and a guide to learn more from it
  • http://www.freeinformix.com/
    This is another site (created by Oninit) which contains a lot of Informix information and news. For learning purposes I recommend the Informix certification tutorials, which are well organized and provide information about several product areas
  • http://www-01.ibm.com/support/docview.wss?uid=swg27019520
    A central place to get links of most (if not all) Informix documentation.
  • http://planetids.com/
    A site that acts as a blog and other publications aggregator. You'll find links to a lot of blogs and also several Informix related publications (books, IBM redbooks etc.). We should thank again to Eric Herber for this excellent repository of information
  • Administering Informix Dynamic Server Building the Foundation (book by Carlton Doe)
    This is probably the most recent book about Informix. Written by a long time and distinguish element of the Informix community. You can find it easily in the usual bookstores (online or not)
  • Informix Library
    A page inside the IBM site where you can find lot's of links to Informix related documents. In particular I'd like to point the link to Informix related Redbooks. Redbooks are freely available books (PDF and recently ePub), sponsored by IBM
  • International Informix Users Group
    This is the main Informix user's group. The registration is free, by email, and you'll not receive spam.The group does a lot to promote Informix and to spread information related to it.
    You can find links, news, utilities etc. on the IIUG website.
    I really recommend that you subscribe to the group and participate in the several mailing lists it provides. You'll always find someone with some free time to answer your questions
  • Informix Chat with the labs
    Monthly webcasts which cover inumerous technical topics. These webcasts are recorded. Currently to access the files from past sessions require sending an email to request access
  • Informix on Facebook and Informix on Twitter
    The so called social networks are great places to interact and find information

For someone ho wants to start with Informix I'd recommend the use of the Virtual Appliance (everything already installed), or if you feel more adventurous just pick up the Developer Edition and follow the installation guide in Informix-Zone website. From there on, it really depends on what you want to do, but the certification tutorials seem a good choice.
Feel free to comment on this post if you'd like to add references.




Versão Portuguesa:

Nas últimas semanas recebi vários pedidos de informação de recém-chegados ao Informix. Foi-me pedida ajuda durante uma deslocação a um cliente, recebi um pedido diretamente por email (através do blog) e houve também um pedido público na lista de correio do IIUG. Provavelmente poderia criar um ótimo artigo falando de como isto significa que há mais interesse no Informix, mas o objetivo principal da criação deste blog foi a partilha de informação e tento manter-me fiel a esse princípio. Assim sendo, o que pretendo aqui é deixar alguma informação com a esperança que possa ajudar quem esteja interessado em aprender mais sobre Informix. As minhas sugestões são (sem nenhuma ordem especial):
  • http://www.informix-zone.com/
    Este é um website criado por Eric Herber. Foi criado há vários anos (antes de ter criado o meu blog) e contém muita informação relacionada com Informix. Em particular e para iniciados, pode encontrar um guia de instalação, uma ligação ao download do Informix Developer Edition e ligações à Informix Virtual appliance, uma imagem de uma máquina virtual que contém já pré-instalado um ambiente Informix e um guia para aprender mais com ele
  • http://www.freeinformix.com/
    Outro website com muita informação sobre Informix (criado pela Oninit). Para aprender sugiro os Informix certification tutorials, que são bem organizados e fornecem muita informação sobre vários assuntos relacionados com o produto
  • http://www-01.ibm.com/support/docview.wss?uid=swg27019520
    Uma única localização onde se pode aceder a quase toda (senão toda) a documentação sobre Informix
  • http://planetids.com/
    Um website que atua como agregador de blogs e outras publicações. Encontrará ligações para artigos de blogs e também outro tipo de publicações relacionadas com Informix (livros, redbooks da IBM etc.). Devemos agradecer novamente ao Eric Herber a existência deste website.
  • Administering Informix Dynamic Server Building the Foundation (livro de Carlton Doe)
    É provavelmente o livro mais recente sobre Informix. Escrito por um membro bem conhecido e de longa data da comunidade Informix. Pode facilmente encontrá-lo nas livrarias habituais (online ou físicas)
  • Informix Library
    Uma página dentro do website da IBM onde pode encontrar inúmeras ligações para documentação relacionada com Informix. Gostaria de salientar a ligação aos Redbooks relacionados com Informix. Os Redbooks são livros de acesso livre (PDFs e mais recentemente ePubs), patrocinados pela IBM
  • Grupo International de Utilizadores Informix
    Este é o grupo de utilizadores principal. O registo é gratuito, por correio eletrónico, e não receberá spam. O grupo faz muito para promover o Informix e para divulgar informação relacionada com o produto. Pode encontrar ligações, noticias, scripts etc.
    Recomendo vivamente a inscrição no grupo e a participação nas várias listas de email que disponibiliza. Encontrará sempre alguém com algum tempo livre para responder às suas questões. Para os leitores que preferem o Português, existe também o Grupo Brasileiro de Usuários de Informix - BRIUG
  • Informix Chat with the labs
    Webcasts com regularidade mensal que cobrem inúmeros temas técnicos. Estes webcasts são gravados. Neste momento o acesso requer o envio de um email com o pedido de acesos aos ficheiros das conferências passadas
  • Informix no Facebook (e em Português) e Informix no Twitter
    As redes sociais são lugares ótimos para interagir e procurar informação

Para quem queira começar com o Informix sugiro a utilização da Virtual Applliance (com tudo já instalado) ou caso se sinta mais aventureiro, efectuar o download da Developer Edition e seguir o guia de instalação do website Informix-Zone. A partir daí, realmente depende do que quer fazer, mas os guias para certificação parecem ser uma boa escolha
 Não hesite em comentar este artigo caso queira adicionar outras referências.

Wednesday, October 24, 2012

Informix 11.70.xC6

This article is written in English and Portuguese
Este artigo está escrito em Inglês e Português


English Version:

As usual, I'm just reviewing the latest IBM Informix fixpack. The software was just released (yesterday, October 23). It's important to note that this fixpack is clearly on the "mature" phase of 11.70 and also that IBM is already working with selected customers on the next major release. As such, we should not expect too many news... Nevertheless, and I've been mentioning this to several customers, IBM uses the Agile methodology in Informix development. And one of the goals of this methodology is to provide new functionalities to customers as soon as possible. So, what am I saying with this mixed message? That even for a "quiet" fixpack we can find surprising new features. As usual, let's take a look at the release notes and comment on the new features section:

  • Administration
    • Enhancements to the OpenAdmin Tool (OAT) for Informix
      Just a version refresh for several components (Apache, PHP and PDO_INFORMIX)
    • Support for the same XID for transactions on different databases
      A new parameter IFX_XA_UNIQUEXID_IN_DATABASE allows that a global (XA) transaction ID can be repeated in different databases in the same instance. Or in other words, the scope of the transaction ID can be changed from the instance to the database level. This is essentially a compatibility feature.
    • Coordinating transactions within a high-availability cluster
      This is one of the surprises... A new parameter CLUSTER_TXN_SCOPE allows a DBA to define if the applications receive the COMMIT confirmation immediately or after the transaction information was propagated through the cluster (or just the current secondary server). This can be a bit confusing and some experimentation is required, but it basically avoids that an application that takes advantage of several cluster nodes hits some apparent data consistency issue caused by the asynchronous nature of the logical log propagation. It can also be useful if an application that changes data, triggers some other process that will read that same data from another node in the cluster. I would say this can be an invaluable option for future expansion of the clustering capabilities in Informix
    • Easier failover configuration for Connection Managers in a high-availability cluster
      We can now specify the failover policy at the engine level by setting a new parameter HA_FOC_ORDER that will take priority over the policy specified in the connection manager(s) connected to the instance
  • Application development
    • Enhanced support for OUT and INOUT parameters in SPL routines
      Another surprise. For most cases and most people this will seem irrelevant. But I recall a couple of situations where I've found the lack of support for OUT/INOUT parameters in SPL a big limitation. I'd like to return to this topic in a future blog post... Time will tell...
    • Additional functions for spatial data
      Enhanced compatibility with the ISO/OpenGIS standards
    • Return the default values of columns
      Enhancement to the Datablade API
    • SPL routines for application compatibility
      Yet another surprise. The functionality will not be a surprise for anyone participating in the vNext process (EVP), but it surely is surprising to see it in 11.7. The idea behind this is terribly simple, and I must confess I'm a big fan for this. We all know that Informix is extensible and that it's easy to create new functions in C language to implement some lacking functions. In fact I've been publishing a few examples of this. So, it's a great pleasure to see that IBM decided to do that and implement some compatibility functions in a new datablade. More specifically some well known packages called DBMS_ALERT, DBMS_LOB, DBMS_OUTPUT, DBMS_RANDOM, UTL_FILE are implemented.
      This can be a great help for anyone porting applications to Informix
  • Time Series data
    • Load time series data faster through a virtual table
      Performance improvement for the virtual table interface for TimeSeries containers
A few final comments:
  1. During an exchange of emails with one of the product architects a few years ago, he wrote something like "we should avoid creating too many parameters... Let's try to keep it simple"
    I could not agree more with him, and seeing so many new parameters worries me a bit and reminds me of this talk. On the other hand, a parameter means control, and I like that. So I'd say they're a necessary evil. On the good side, they're normally well commented in the default ONCONFIG and the newly introduced ones tend to be dynamic.
  2. If I worked in marketing, at this time I'd probably be keeping new features hidden so that I'd include them in the next major version as new stuff. It's good to see that this is not hapening and the main concern is clearly to provide value for customers ASAP
  3. Please, please, please.... Let the compatibility packages be just the beginning. I cannot find any reason why functions are not implemented this way, even if it's not the fastest implementation. In most cases developers need the functionality more than the performance. And later it can be moved into the native SQL layer implementation without breaking compatibility. This cannot be a solution for some SQL constructs, but for "pure" functions I'd use it. In fact it's not new... We've seen this in publicly available bladelets

Versão Portuguesa:

Como vem sendo habitual vou rever a última release do IBM Informix. O software acabou de ser disponibilizado (ontem, 23 de Outubro). É importante notar que esta release está já na fase "madura" da versão 11.70 e também que a IBM está já a trabalhar com clientes selecionados sobre a próxima versão do produto. Como tal não deveremos esperar grandes novidades... Ainda assim, e tenho referido isto a vários clientes recentemente, a IBM utiliza a metodologia Agile no desenvolvimento do Informix. E um dos objetivos do Agile é disponibilizar novas funcionalidades aos utilizadores tão cedo quanto possível. Portanto, o que estou a querer dizer com estas ideias contraditórias? Que mesmo numa release "calma" podemos encontrar novas e surpreendentes funcionalidades. Como é costume vamos espreitar as notas da release e comentar a secção de novas funcionalidades: 

  • Administração
    • Melhorias no Open Admin Tool (OAT) para Informix
      Apenas o refrescamento de versões de vários componentes (Apache, PHP e PDO_INFORMIX)
    • Suporte para o mesmo XID para transacções em bases de dados diferentes
      Um novo parâmetro IFX_XA_UNIQUEXID_IN_DATABASE permite que o identificador de uma transação global (XA) possa repetir-se em bases de dados diferentes dentro da mesma instância. Ou por outras palavras, que o alcance de um identificador de transação possa ser alterado da instância para o nível da base de dados. É essencialmente uma funcionalidade para melhorar a compatibilidade com ferramentas externas
    • Coordenação de transações dentro de um cluster de alta disponibilidade
      Esta é uma das surpresas... Um novo parâmetro (CLUSTER_TXN_SCOPE) permite definir se a aplicação recebe a confirmação de um COMIIT imediatamente ou depois de a transação se propagar pelo cluster. (ou apenas para o nó secundário corrente). Isto pode parecer um pouco confuso e é necessária alguma experiência, mas basicamente evita que uma aplicação que tire proveito de vários nós do cluster encontre alguma aparente inconsistência de dados, causada pela natureza assíncrona da propagação dos logical logs. Pode ser também útil se uma aplicação que altere dados, despolete depois outro processo que vá ler esses mesmos dados em outro nó do cluster. Diria que isto pode ser uma opção de muito valor para as futuras expansões de funcionalidade de clustering do Informix
    • Mais fácil configuração de failover para os Connection Managers num cluster de alta disponibilidade
      Podemos especificar a política de failover ao nível do motor através de um novo parâmetro (HA_FOC_ORDER) que terá prioridade sobre o que for especificado na configuração do Connection Manager(s) ligado(s) à instância
  • Desenvolvimento aplicacional
    • Melhoria no suporte a parâmetros OUT e INOUT nos procedimentos/funções em SPL
      Mais uma surpresa. Na maioria dos casos e para a maioria das pessoas isto parecerá irrelevante. Mas lembro-me de um par de situações onde a falta de suporte a parâmetros OUT/INOUT foi uma grande limitação. Gostaria de retornar ao tema num próximo artigo... O tempo o dirá...
    • Mais funções para dados spatial
      Melhoria na compatibilidade com os standards  ISO/OpenGIS
    • Retornar os valores por omissão das colunas
      Melhorias na API dos Datablades
    • Rotinas SPL para compatibilidade aplicacional
      Mais uma surpresa. Esta funcionalidade não será novidade para quem esteja a participar no processo de avaliação da próxima versão (EVP), mas é surpreendente vê-la na 11.7. A ideia por detrás disto é extremamente simples, e devo confessar que sou um fã da mesma. Todos sabemos que o Informix é facilmente extensível e que é fácil criar funções em C que implementem funções que não existem no Informix. Inclusivamente já tenho publicado alguns artigos dando alguns exemplos. Assim, é com grande prazer que vejo a IBM decidir implementar funções "de compatibilidade" num novo datablade. Mais especificamente alguns pacotes bem conhecidos, chamados DBMS_ALERT, DBMS_LOB, DBMS_OUTPUT, DBMS_RANDOM, UTL_FILE foram implementados.
      Isto pode ser uma enorme ajuda a quem esteja a portar aplicações para Informix
  • Dados Time Serie
    • Carregar dados time series mais rápido através de uma tabela virtual
      Melhoria de performance na interface de tabela virtual sobre repositórios TimeSeries
Alguns comentários finais:
  1. Durante uma troca de emails com um dos arquitetos do Informix, há alguns anos atrás, ele escreveu algo como "devemos evitar criar muitos parâmetros... Tentemos manter a coisa simples"
    Não posso estar mais de acordo com ele, e ver tantos novos parâmetros assusta-me um pouco e relembra-me esta conversa. Por outro lado um parâmetro significa controlo e eu gosto disso. Diria que são um mal necessário. A parte boa é que estão normalmente bem comentados no ONCONFIG de exemplo e que os novos que têm sido introduzidos são normalmente dinâmicos
  2. Se trabalhasse no marketing, por esta altura estaria provavelmente a esconder funcionalidades para que as pudesse anunciar aquando da próxima versão. É bom ver que tal não está a acontecer, e que a preocupação continua a ser entregar funcionalidades aos clientes logo que possível
  3. Por favor, por favor, por favor!.... Que a introdução dos packages de compatibilidade seja apenas o princípio. Não consigo encontrar nenhuma razão para que funções não sejam implementadas desta forma, mesmo que isto não seja a implementação mais rápida. Na maioria dos casos os programadores necessitam da funcionalidade mais que da performance. E mais tarde podem ser movidas para a implementação na camada de SQL nativa sem quebrar a compatibilidade. Isto não poderá ser a solução para alguma sintaxe do SQL, mas para funções "puras" eu escolheria este caminho. E na realidade não é novo. Já vimos isto em bladelets disponíveis para o público



Saturday, October 13, 2012

ORDBMs: Informix vs Postgres vs Oracle

This article is written in English and Portuguese
Este artigo está escrito em Inglês e Português

English version:
I just noticed on Twitter and other feeds that a recent article compares the object oriented features of three databases. And yes, one of them is Informix, which is not that bad for a database that many consider "old", "outdated", "gone" etc.
The author picked up Informix, Postgres and Oracle and makes some comparison between the way object oriented features are implemented.
You should read it yourself (it's a short article), but my understanding is:
  1. Informix and Postgres are more mature and have more deeply embedded concepts than Oracle
  2. Informix inherits primary keys and indexes in sub-types which Postgres can't do
  3. Informix does not support multiple inheritance which Postgres does (and because of that can't really support primary key inheritance)
  4. Oracle syntax for method reference looks "cleaner" than Informix and Postgres (which are identical)
  5. Oracle uses the tables as mere object repositories, and doesn't implement the OO features at the table level
Although the object oriented features are not usually used directly by customers, and in fact we haven't been seeing to many improvements, the fact is that the Informix extensibility is based on these characteristics. And it's much easier to understand it's importance if we consider all the datablades currently shipped with Informix (for free). I'm thinking about Basic Text Search (BTS), Spatial, Timeseries, Node, Binary, MQ, and we may see more in the future...


Versão Portuguesa:

Encontrei recentemente no Twitter e noutras fontes referências a um artigo que compara as funcionalidades de orientação a objetos de três bases de dados. E sim, uma delas é o Informix, o que não é mau de todo para uma base de dados que muitos consideram "antiga", "desatualizada", "extinta" etc.
O autor pegou no Informix, Postgres e Oracle e comparou a forma como foram implementas as funcionalidades de orientação a objetos.
Deverá ler por si (o artigo é pequeno), mas o meu entendimento foi o seguinte:
  1. O Informix e Postgres são mais maduros e embeberam os conceitos mas profundamente que o Oracle
  2. O Informix permite a herança das chaves primárias e índices, o que o Postgres não permite
  3. O Informix não suporta a herança múltipla o que o Postgres permite (e por causa disso não pode suportar a herança das chaves primárias)
  4. A sintaxe usada pelo Oracle parece mais "limpa" que a do Informix e Postgres (que são identicas)
  5. O Oracle utiliza as tabelas como mero repositório dos objetos não implementando as características OO ao nível das tabelas
Apesar de as funcionalidades de orientação a objetos não serem normalmente usadas diretamente pelos clientes, e de facto não termos vindo a ver grandes melhorias nestas funcionalidades, a verdade é que a extensibilidade do Informix é baseada nestes conceitos. E é muito mais fácil compreender a sua importância se considerarmos todos os datablades que acompanham o Informix (sem custos). Estou a pensar no Text Search (BTS), Spatial, Timeseries, Node, Binary, MQ, e poderemos ver mais no futuro...

Thursday, October 11, 2012

Chat with the Labs about technical support

This article is written only in English (given the announcement is for an English webcast)

Just a quick note to spread the word around about the next Chat with the Labs webcast. If you don't know what I'm talking about, the Chat with the Labs calls are regular presentations (webcasts) organized by IBM Informix team that talk about several topics. This tales place around once per month, and the next one on October 13 (10:30 AM - 12:00 PM Central time) will talk about "Informix Support - Helping us to help you".
 

The announcement text is the following:

Informix Support is there when you need us most. You may not know that you can help Informix Support to diagnose and debug a problem by providing 'Must Gather Documents'. These documents aid in problem determination, and save time resolving problems by explaining what information needs to be gathered prior to contacting Informix Support. Gathering this information helps familiarize you with the troubleshooting process and saves you time.

Support is working behind the scenes to make diagnose and debug problem easier and fulfill your troubleshooting needs. Over the time Informix Support added features that minimize downtime. You will see some of these features in daily use to reduce downtime and troubleshoot problems.


So, if you can spare around 1H and listen to this call, I'm sure you'll get a better understanding about how to interact and take more advantage of IBM Informix technical support (which you pay for). In order to attend and receive the slide presentation and/or access the webcast media you should access the following link:

https://events.webdialogs.com/register.php?id=91a1ae3db1&l=en-US

vNext not far away... / vNext não demorará muito...

Este artigo está escrito em Inglês e Português
This article is written in English and Portuguese

English version:
Once we heard that the "databases wars are over...". I already gave my opinion that I don't believe this. The various databases continue to be improved and seeing some new features being introduced. Just recently Oracle talked about the 12c release during their Oracle Openworld conference. And IBM Informix is going through the EVP (Early Validation Program - or beta period in other words) for the next Informix version. Yesterday, in a blog post, Pradeep Kutty just mentioned that it should be GA in Q1 2013. I never take these dates as granted, and I always assume some delay. But in any case, considering the EVP is rolling, with the first code drop available to selected customers, we know it's coming. I will try to dedicate some time to look into it, but for obvious reasons (which include market regulation rules) we cannot talk about any new features. For now, what I can say is that it looks consistent with the public roadmap, meaning there is some continuity with some of the latest features that were introduced.



Versão Portuguesa:
Em tempos ouvimos dizer que "as guerras das bases de dados terminaram"... Eu já emiti a minha opinião explicando que não concordo com isso. As várias bases de dados continuam a sofrer melhorias e vemos algumas novas funcionalidades serem introduzidas. A Oracle acabou de falar sobre a sua versão 12c durante a conferência Oracle Openworld. E o IBM Informix está na fase EVP (Early Validation Program - ou por outras palavras em fase beta) da sua versão vNext. Ontem, Pradeep Kutty, num artigo de blog acabou por mencionar que deverá ser lançada durante Q1 de 2013. Pessoalmente nunca assumo estas datas como garantidas e dou sempre um desconto para atrasos, mas tendo em conta que se está na fase EVP, com a primeira versão de código disponível para clientes selecionados, é garantido que está para chegar. Vou tentar dedicar algum tempo para a examinar, mas por razões óbvias (que incluem regras de regulação de mercado), não podemos falar sobre qualquer nova funcionalidade. Por agora, a única coisa que posso dizer é que parece consistente com o roadmap traçado, ou seja que há uma certa continuidade com algumas das últimas funcionalidades introduzidas.

Tuesday, October 02, 2012

UDRs: COALESCE

This article is written in English and Portuguese
Este artigo está escrito em Inglês e Português


English version:

Introduction
This is another article in the UDR series. In a discussion on IIUG mailing list someone was complaining about the lack of COALESCE function in Informix. The first answer was that nested NVL() calls could be used as a replacement, or an SPL function could be written to implement it. But the same person warned that the SPL procedure would have a significant performance impact. I made a couple of tests and verified the same. So I decided to test a UDR to implement it.
But before diving into it, a few warnings are in order... The true COALESCE() function or SQL construct is a very flexible operation that takes an undetermined number of arguments of unknown and possibly different data types. I'm not sure if something like that can be implemented in a user defined function (UDR). So, for the scope of this article I'll assume two restrictions: A fixed number of maximum arguments (this would be easy to change) and that all the arguments belong to the same type (although the engine could cast them).
I'd also like to thank John Miller, the Senior Technical Staff Member of the Informix team, and a well known member of the Informix community, for his input, suggestions and code review.

The code
You can find the C UDR source code at the end of this article. I'll just go through it, to better explain how it works, but the juicy part is the comparison between several methods that will follow.

Lines 1 to 8 are just the usual include sections.

Lines 11 to 15 is the function header. As you can see at the C code level it receives ten LVARCHARs and returns an LVARCHAR. The reason why the LVARCHAR was choose is because it has implicit casts for most if not all the data types. This means that when we define the function at the SQL level we can use any data type we like (or create several functions with different signatures that allows for a broader use).

Lines 17 to 20 include an auxiliary variable declaration and initialization with the function mi_fp_nargs() which returns the number of parameters defined for the function.

Line 23 defines a loop that checks if any of the arguments is not null. If it finds one, it returns that argument. Unfortunately I could not find an easy way to make this piece of code generic (automatically adaptable to a different number of parameters), so a long switch statement was used.

If none of the arguments is non-NULL, then at lines 62-63 it returns a NULL value.

The compilation

As usual I use a simple makefile to generate the dynamic library containing the code:
include $(INFORMIXDIR)/incl/dbdk/makeinc.linux86_64


MI_INCL = $(INFORMIXDIR)/incl
CFLAGS = -DMI_SERVBUILD $(CC_PIC) -I$(MI_INCL)/public $(COPTS)
LINKFLAGS = $(SHLIBLFLAG) $(SYMFLAG) $(LD_SHARED_FLAGS)

all: ix_coalesce

clean:
        rm *.udr *.o

ix_coalesce: ix_coalesce.udr
        @echo "Library genaration done"

ix_coalesce.o: ix_coalesce.c
        @echo "Compiling..."
        $(CC) -c $(CFLAGS) -o $@ $?

ix_coalesce.udr: ix_coalesce.o
        @echo "Creating the library..."
        $(SHLIBLOD) $(LINKFLAGS) -o $@ $?



In the end, after the make command we should have a dynamic linked library that we can use to create the function in SQL. The make process is simply:

tpch@kimball:informix-> make
Compiling...
cc -c -DMI_SERVBUILD -fPIC -I/opt/informix/srvr1170fc5/incl/public -g -o ix_coalesce.o ix_coalesce.c
Creating the library...
gcc -shared -m64 -Bsymbolic -shared -m64 -o ix_coalesce.udr ix_coalesce.o
Library genaration done

tpch@kimball:informix-> ls -lia ix_coalesce.udr
1654803 -rwxr-xr-x 1 informix informix 8273 Aug 13 00:00 ix_coalesce.udr

Creating the function in SQL
Once we get the compiled code in the form of a dynamic loadable library we need to create the function in SQL, referencing the C code function.
This is done with this simple SQL code:
DROP FUNCTION IF EXISTS coalesce_udr;
CREATE FUNCTION coalesce_udr(
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL
) RETURNING INTEGER
WITH (NOT VARIANT, HANDLESNULLS)
EXTERNAL NAME '/opt/informix/work/coalesce/ix_coalesce.udr(ix_coalesce)'
LANGUAGE C;

A few notes:
  1. I'm defining an UDR that takes INTEGERs as arguments. Internally they'll be treated as LVARCHARs. If we need COALESCE() for other types of arguments we could create other functions, with the same name but different parameter types. This is called overloading and is perfectly supported in Informix
  2. I used HANDLESNULLS because without it, if we use NULL as arguments the C functions are not called and return NULL
  3. EXTERNAL NAME specifies the "path" to the C code function. In this case it's the pathname of the dynamic loadable library we created and the function name (inside the library) between parentheses

Tests and speed comparison
So, the purpose of this is to compare several ways to overcome the lack of a native COALESCE SQL construct. The ways I considered were:
  1. Using nested NVL() functions directly in the SQL statement. This is the fastest and was considered the reference
  2. Creating an SPL function called coalesce_basic that receives the arguments and has a nested NVL() statement inside the SPL
  3. Creating an SPL function called coalesce_if that is similar to the previous, but instead of a nested NVL() structure has a sequence of IF statements
  4. The C code function explained above
You can find the code for all this alternatives at the end of the article. After creating the functions I have created a test set of data which has 10M rows and 10 fields on each row.
Each row has only one field which is not NULL. And the non NULL field changes sequentially in each row. An AWK script used to generate the test data is also shown at the end.

I've loaded the file into a table and I've run an UNLOAD just to make sure I get the table into memory. After all this I run the UNLOAD again, using the 10 columns as arguments for the several functions (or SQL) I mentioned above. The UNLOAD is made to /dev/null to minimize I/O times. Again you can check the SQL code at the end of the article. For each function type I insert into a temporary table the start and finish time. In the end I run a query that obtains the time comparison of each of he functions with the simple SQL construct using the nested NVLs directly on the query.

I've run this several times and the results were always very similar. And the timings obtained look like this (two samples):
using  Nested NVL:        0 00:00:35.48369 100.00%
using  Nested NVL in SPL: 0 00:02:47.96074 473.34%
using  IF based in SPL:   0 00:03:27.54880 584.91%
using  COALESCE_UDR:      0 00:00:47.85661 134.86%


using  Nested NVL:        0 00:00:35.31295 100.00%
using  Nested NVL in SPL: 0 00:02:45.31578 468.14%
using  IF based in SPL:   0 00:03:26.77770 585.55%
using  COALESCE_UDR:      0 00:00:47.78794 135.32%


Conclusions

The conclusions are pretty obvious, but let's go through them:
  1. There is nothing faster than the nested NVLs. So if your main concern is the the speed you should use it. The obvious drawback is that the code will look "strange" to users of other databases and it's not a good solution if you're porting an application or query
  2. Nested NVL in SPL has a huge performance impact. Note that the code is similar to the above but will run in an SPL, so it will require function calling.
  3. IF based SPL is even slower than the previous
  4. The performance of the C UDR that we've created is not that bad... Yes, we see a performance impact of between 30 and 40%, but would that be significant? Note that in the test case we're calling the function 10M times... and the impact was 13s... What would happen if the result set had a few lines? Or a few hundreds? Also note that the test case is accessing data in the database cache. And it's not evaluating any WHERE clause. So, it lloks like a good compromise between performance and flexibility. As usual, the C UDR's are pretty fast.

Versão Portuguesa:

Introdução
Eis mais um artigo da série dedicada às UDRs (User Defined Routines). Numa discussão na lista de correio do IIUG, alguém se queixava da inexistência da função COALESCE no Informix. A primeira resposta foi que NVL() encadeados poderiam ser usados como substituto, ou também um procedimento SPL. Mas a mesma pessoa alertou que um procedimento SPL teria um impacto de performance muito significativo. Executei alguns testes e pareceu-me que isto se comprovava. Daí ter decidido criar uma UDR para implementar o COALESCE.
Mas antes de mergulhar no assunto convém analisar alguns pontos... O verdadeiro COALESCE é mais uma construção SQL que uma função. É uma operação muito flexível que recebe um número não determinado de argumentos de tipos possivelmente diferentes. Não sei se tal seria possível implementar numa UDR. Portanto, no contexto deste artigo vou assumir duas restrições: Um número fixo para o máximo de argumentos (isto poderia ser facilmente mudado) e que todos os argumentos são do mesmo tipo (embora o motor pudesse fazer a conversão dos parâmetros).
Gostaria também de agradecer ao John Miller, membro do staff técnico da equipa Informix, um membro muito reconhecido da comunidade Informix internacional, pelo seu input, sugestões e revisão de código.

O código
Pode consultar o código C do UDR no final deste artigo. Vou apenas explicar alguns passos desse código para que entenda como funciona, mas a parte mais interessante será a comparação entre os diversos métodos que se seguirá.

Nas linhas 1 a 8 estão os habituais includes.

Linhas 11 a 15 são o cabeçalho da função. Como pode verificar, ao nível do código C a função recebe e retorna LVARCHARs. A razão para tal é que existem conversões (casts) implícitos para quase todos, senão todos, os tipos de dados. Isto significa que quando se definem as funções ao nível do SQL podemos usar qualquer tipo de dados que queiramos (ou criar várias funções com assinaturas diferentes para permitir um utilização mais abrangente).

Linhas 17 a 20 incluem uma declaração de variável auxiliar e respetiva inicialização com a função mi_fp_nargs() que retorna o número de parâmetros definidos para a função.

A linha 23 define um ciclo que verifica se algum dos argumentos contém um valor não nulo. Se encontrar um, retorna esse argumento. Infelizmente não encontrei uma forma de tornar esta parte do código genérica (adaptável automaticamente a um diferente número de parâmetros), por isso uma longa instrução switch foi usada.

Se nenhum dos argumentos for não nulo, então nas linhas 62-63 um valor NULL é retornado.


A compilação

Como vem sendo hábito nestes artigos sobre UDRs, utilizo um makefile simples para gerar a biblioteca dinâmica contendo o código:
include $(INFORMIXDIR)/incl/dbdk/makeinc.linux86_64


MI_INCL = $(INFORMIXDIR)/incl
CFLAGS = -DMI_SERVBUILD $(CC_PIC) -I$(MI_INCL)/public $(COPTS)
LINKFLAGS = $(SHLIBLFLAG) $(SYMFLAG) $(LD_SHARED_FLAGS)

all: ix_coalesce

clean:
        rm *.udr *.o

ix_coalesce: ix_coalesce.udr
        @echo "Library genaration done"

ix_coalesce.o: ix_coalesce.c
        @echo "Compiling..."
        $(CC) -c $(CFLAGS) -o $@ $?

ix_coalesce.udr: ix_coalesce.o
        @echo "Creating the library..."
        $(SHLIBLOD) $(LINKFLAGS) -o $@ $? 
 
No final, após executar o comando make devemos ter uma biblioteca dinâmica que podemos usar para criar a nossa função ao nível do SQL. O processo de make é simples:
tpch@kimball:informix-> make
Compiling...
cc -c -DMI_SERVBUILD -fPIC -I/opt/informix/srvr1170fc5/incl/public -g -o ix_coalesce.o ix_coalesce.c
Creating the library...
gcc -shared -m64 -Bsymbolic -shared -m64 -o ix_coalesce.udr ix_coalesce.o
Library genaration done

tpch@kimball:informix-> ls -lia ix_coalesce.udr
1654803 -rwxr-xr-x 1 informix informix 8273 Aug 13 00:00 ix_coalesce.udr

Criando a função em SQL
Após termos compilado o código e produzido a biblioteca dinâmica necessitamos de criar a função em SQL, referenciando a função em C.
Isto é feito com este simples código SQL:
DROP FUNCTION IF EXISTS coalesce_udr;
CREATE FUNCTION coalesce_udr(
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL,
        INTEGER DEFAULT NULL
) RETURNING INTEGER
WITH (NOT VARIANT, HANDLESNULLS)
EXTERNAL NAME '/opt/informix/work/coalesce/ix_coalesce.udr(ix_coalesce)'
LANGUAGE C;

Algumas notas:
  1. Estou a definir uma UDR que recebe INTEGERs como argumentos. Internamente serão tratados como LVARCHARs. Se necessitarmos de COALESCE para outros tipos de dados podíamos criar outras funções, com o mesmo nome mas diferentes tipos de parâmetros. A isto chama-se overloading e é perfeitamente suportado em Informix
  2. Usei a opção HANDLESNULLS porque sem ela, se usarmos NULL num argumento a função não é chamada e o retorno será NULL
  3. EXTERNAL NAME indica o caminho (path) da função em C. Neste caso é o caminho para a biblioteca dinâmica criada anteriormente e o nome da função (dentro da biblioteca) entre parênteses

Testes e comparação de tempos
Portanto, o objetivo é comparar várias formas de contornar a falta de um COALESCE nativo. As alternativas consideradas foram:
  1. Usar NVL() encadeados diretamente nas instruções SQL. Esta é a mais rápida e será considerada como referência
  2. Criar uma SPL chamada coalesce_basic que recebe argumentos e que contém a estrutura de NVL encadeados no seu código
  3. Criar uma SPL chamada coalesce_if semelhante à anterior, mas que em vez dos NVL() encadeados tem uma estrutura de IFs
  4. O código C explicado neste artigo

Pode encontrar o código para todas estas alternativas no final do artigo. Depois de criar as funções criei um conjunto de dados de teste, composto de 10M de linhas com 10 campos cada uma.
Cada linha tem apenas um campo não NULL. E o campo não NULL muda sequencialmente em cada linha. Utilizei um script AWK para criar os dados, e este script encontra-se também no final do artigo.

Carreguei o ficheiro numa tabela e executei um UNLOAD só para garantir que os dados desta tabela são colocados em memória (na buffer cache). Após tudo isto executo o UNLOAD novamente, usando as 10 colunas como argumentos para as várias alternativas acima. O UNLOAD é efectuado para /dev/null para minimizar os tempos de I/O. Mais uma vez, o código SQL está incluído no final. Para cada alternativa insiro numa tabela temporária o tempo inicial e final. No final executo uma instrução que fornece a comparação de tempos para cada alternativa vs a instrução SQL com os NVL() encadeados.

Executei o procedimento várias vezes para verificar se os resultados eram semelhantes, o que se confirmou. E os tempos obtidos são semelhantes aos seguintes (dois exemplos):

using  Nested NVL:        0 00:00:35.48369 100.00%
using  Nested NVL in SPL: 0 00:02:47.96074 473.34%
using  IF based in SPL:   0 00:03:27.54880 584.91%
using  COALESCE_UDR:      0 00:00:47.85661 134.86%


using  Nested NVL:        0 00:00:35.31295 100.00%
using  Nested NVL in SPL: 0 00:02:45.31578 468.14%
using  IF based in SPL:   0 00:03:26.77770 585.55%
using  COALESCE_UDR:      0 00:00:47.78794 135.32%


Conclusões

As conclusões são bastante óbvias, e os tempos falam por si, mas vou enumerá-las:
  1. Não há alternativa mais rápida que os NVL() encadeados. Portanto se a única preocupação é a performance é esta que deve usar. O obstáculo óbvio é que o código parecerá "estranho" para utilizadores de outras bases de dados e não será uma boa solução se está a portar uma aplicação ou query.
  2. NVL() encadeados dentro de um procedimento em SPL tem um enorme impacto de performance. Note que o código é semelhante ao anterior, mas corre dentro de uma SPL, por isso terá de haver chamadas à função
  3. O procedimento SPL com IFs é ainda mais lento que o anterior
  4. A performance da UDR C que criámos não é de todo muito má... Sim, vemos algum impacto, entre 30 a 40%, mas será isso significativo? Note-se que no caso de teste estamos a chamar a função 10M de vezes. E o impacto foi de 13s. Note-se ainda que o teste foi pensado para aceder a dados que estão em memória, e que não estamos a avaliar nenhuma cláusula WHERE. Ou seja, parece um bom compromisso entre flexibilidade e performance. Como é hábito a UDR em C é bastante rápida






1    /*
2    ------------------------------------------
3     include section
4    ------------------------------------------
5    */
6    #include <stdio.h>
7    #include <milib.h>
8    #include <sqlhdr.h>
9
10   // Define as much i* as the maximum number of arguments wanted
11   mi_lvarchar * ix_coalesce(
12      mi_lvarchar *i0,mi_lvarchar *i1,mi_lvarchar *i2,
13      mi_lvarchar *i3,mi_lvarchar *i4,mi_lvarchar *i5,
14      mi_lvarchar *i6,mi_lvarchar *i7,mi_lvarchar *i8,
15      mi_lvarchar *i9, MI_FPARAM *fParam)
16   {
17   int arg_count,a;
18
19      /* Adjust the code to the number of parameters */
20      arg_count = mi_fp_nargs(fParam);
21
22      // loop through the arguments...
23      for(a=0;a<arg_count ;a++)
24      {
25              if ( mi_fp_argisnull(fParam, a) == MI_FALSE )
26              {
27                      switch(a)
28                      {
29                              case 0:
30                                      return(i0);
31                                      ;;
32                              case 1:
33                                      return(i1);
34                                      ;;
35                              case 2:
36                                      return(i2);
37                                      ;;
38                              case 3:
39                                      return(i3);
40                                      ;;
41                              case 4:
42                                      return(i4);
43                                      ;;
44                              case 5:
45                                      return(i5);
46                                      ;;
47                              case 6:
48                                      return(i6);
49                                      ;;
50                              case 7:
51                                      return(i7);
52                                      ;;
53                              case 8:
54                                      return(i8);
55                                      ;;
56                              case 9:
57                                      return(i9);
58                                      ;;
59                      }
60              }
61      }
62      mi_fp_setreturnisnull(fParam, 0, MI_TRUE);
63      return NULL;
64   }
 
AWK script (echo "10000000 10 test_coalesce.unl" | awk -f gen_data.awk):
 
#-------------------------------------------------------
BEGIN { cycle = 1 }
{
        TOP_LIMIT=$1;
        NUM_FIELDS=$2;
        FILE=$3;

        for (a=1;a<=TOP_LIMIT;a++)
        {
                LINE="";
                for (b=1;b<=NUM_FIELDS;b++)
                        if ( b == cycle)
                                LINE=LINE a "|";
                        else
                                LINE=LINE "|";
                cycle++;
                if ( cycle > NUM_FIELDS )
                        cycle=1;
                print LINE >> FILE
        }
}
#-------------------------------------------------------

Functions SQL 
SQL de criação das funções:

DROP FUNCTION IF EXISTS coalesce_basic;
CREATE FUNCTION coalesce_basic (
        i1 INTEGER DEFAULT NULL, i2 INTEGER DEFAULT NULL,
        i3 INTEGER DEFAULT NULL, i4 INTEGER DEFAULT NULL,
        i5 INTEGER DEFAULT NULL, i6 INTEGER DEFAULT NULL,
        i7 INTEGER DEFAULT NULL, i8 INTEGER DEFAULT NULL,
        i9 INTEGER DEFAULT NULL, i10 INTEGER DEFAULT NULL
) RETURNING INTEGER;

        RETURN NVL(NVL(NVL(NVL(NVL(NVL(NVL(NVL(NVL(i1,i2),i3),i4),i5),i6),i7),i8),i9),i10);
END FUNCTION;


DROP FUNCTION IF EXISTS coalesce_if;
CREATE FUNCTION coalesce_if (
        i1 INTEGER DEFAULT NULL, i2 INTEGER DEFAULT NULL,
        i3 INTEGER DEFAULT NULL, i4 INTEGER DEFAULT NULL,
        i5 INTEGER DEFAULT NULL, i6 INTEGER DEFAULT NULL,
        i7 INTEGER DEFAULT NULL, i8 INTEGER DEFAULT NULL,
        i9 INTEGER DEFAULT NULL, i10 INTEGER DEFAULT NULL
) RETURNING INTEGER;

        IF ( i1 IS NOT NULL) THEN
                RETURN i1;
        END IF;
        IF ( i2 IS NOT NULL) THEN
                RETURN i2;
        END IF;
        IF ( i3 IS NOT NULL) THEN
                RETURN i3;
        END IF;
        IF ( i4 IS NOT NULL) THEN
                RETURN i4;
        END IF;
        IF ( i5 IS NOT NULL) THEN
                RETURN i5;
        END IF;
        IF ( i6 IS NOT NULL) THEN
                RETURN i6;
        END IF;
        IF ( i7 IS NOT NULL) THEN
                RETURN i7;
        END IF;
        IF ( i8 IS NOT NULL) THEN
                RETURN i8;
        END IF;
        IF ( i9 IS NOT NULL) THEN
                RETURN i9;
        END IF;
        RETURN i10;
END FUNCTION;


Test run (coalesce.sql)
Execução do teste (coalesce.sql):
 
DROP TABLE IF EXISTS test_coalesce;
CREATE RAW TABLE test_coalesce
(
        col1 INTEGER,
        col2 INTEGER,
        col3 INTEGER,
        col4 INTEGER,
        col5 INTEGER,
        col6 INTEGER,
        col7 INTEGER,
        col8 INTEGER,
        col9 INTEGER,
        col10 INTEGER
) EXTENT SIZE 50000 NEXT SIZE 50000 LOCK MODE ROW;

-- Load the test data / Carregar os dados de teste
LOAD FROM test_coalesce.unl INSERT INTO test_coalesce;
ALTER TABLE test_coalesce TYPE (standard);



-- Force data to cache / colocar os dados em memória
UNLOAD TO /dev/null SELECT * FROM test_coalesce;

-- Create execution times table / Criar tabela de recolha de tempos
DROP TABLE IF EXISTS timmings;
CREATE TABLE timmings
(
        method VARCHAR(30),
        start DATETIME YEAR TO FRACTION(5),
        end DATETIME YEAR TO FRACTION(5)
);

-- Using nested NVL() / Utilizacao de NVL() encadeados
INSERT INTO timmings (method, start) VALUES ( 'Nested NVL', CURRENT YEAR TO FRACTION(5));
UNLOAD TO /dev/null
SELECT
nvl(nvl(nvl(nvl(nvl(nvl(nvl(nvl(nvl(col1,col2),col3),col4),col5),col6),col7),col8),col9),col10)
FROM test_coalesce;
UPDATE timmings SET end = CURRENT YEAR TO FRACTION(5) WHERE method = 'Nested NVL';

-- Using the nested NVL procedure / Utilizacao do procedimento com os NVL() encadeados
INSERT INTO timmings (method, start) VALUES ( 'Nested NVL in SPL', CURRENT YEAR TO FRACTION(5));
UNLOAD TO /dev/null
SELECT
COALESCE_BASIC(col1,col2,col3,col4,col5,col6,col7,col8,col9,col10)
FROM test_coalesce;
UPDATE timmings SET end = CURRENT YEAR TO FRACTION(5) WHERE method = 'Nested NVL in SPL';

-- Using the IF based SPL / Utilizacao da SPL baseada em IFs
INSERT INTO timmings (method, start) VALUES ( 'IF based in SPL', CURRENT YEAR TO FRACTION(5));
UNLOAD TO /dev/null
SELECT
COALESCE_IF(col1,col2,col3,col4,col5,col6,col7,col8,col9,col10)
FROM test_coalesce;
UPDATE timmings SET end = CURRENT YEAR TO FRACTION(5) WHERE method = 'IF based in SPL';

-- Using this article COALESCE UDR / Utilizacao da funcao UDR criada no artigo 
INSERT INTO timmings (method, start) VALUES ( 'COALESCE_UDR', CURRENT YEAR TO FRACTION(5));
UNLOAD TO /dev/null
SELECT
COALESCE_UDR(col1,col2,col3,col4,col5,col6,col7,col8,col9,col10)
FROM test_coalesce;
UPDATE timmings SET end = CURRENT YEAR TO FRACTION(5) WHERE method = 'COALESCE_UDR';


-- Auxiliary function to calculate the relative percentage / Funcao auxiliar para calculo das percentagens relativas
DROP FUNCTION IF EXISTS interval_to_second;
CREATE FUNCTION interval_to_second(i INTERVAL MINUTE TO FRACTION(5)) RETURNING DECIMAL(10,5);

DEFINE v_hours, v_minutes, v_seconds SMALLINT;
DEFINE v_fraction DECIMAL(10,5);
LET v_hours = 0;
LET v_minutes = SUBSTR(i::CHAR(12), CHARINDEX(':',i::CHAR(12))-2,2);
LET v_seconds = SUBSTR(i::CHAR(12), CHARINDEX(':',i::CHAR(12))+1, 2);
LET v_fraction = "0." || SUBSTR(i::CHAR(12), CHARINDEX(':',i::CHAR(12))+4, 5);

RETURN v_hours * 3600 + v_minutes * 60 + v_seconds + v_fraction;
END FUNCTION;

SELECT
        method || ': ' || (end - start) || ' ' || TRUNC(
                (
                        interval_to_second((end - start)::INTERVAL MINUTE TO FRACTION(5))
                        /
                        (SELECT interval_to_second((t1.end - t1.start)::INTERVAL MINUTE TO FRACTION(5)) FROM timmings t1 where t1.method = 'Nested NVL')
                        )*100
                ,2) ||'%' as Using
FROM
        timmings;