• Sintaxe no PostgreSQL: ENUM

    Os tipos enumerados ou ENUM, são uma das melhores ideias que o PostgreSQL copiou do MySQL. Sério! Eles foram introduzidos na versão 8.3 do Postgres e são realmente muito úteis. Claro que a implementação no PostgreSQL é bem mais elegantes, pois utiliza o CREATE TYPE e ALTER TYPE como veremos a seguir. Isso nos dá uma implementação mais robusta e flexível para o ENUM. O ENUM é ideal para para colunas que podem receber um conjunto relativamente estável de valores possíveis. Sabe aquelas colunas que você usa com frequência como status, estado civil, sexo, etc. Você geralmente tem 3 opções quando não tem o ENUM como tipo de dado:

    • Cria uma tabela auxiliar com um ID e o nome da característica em questão:
    SQL
    CREATE TABLE estado_civil (
         id      SERIAL PRIMARY KEY,
         nome    VARCHAR(15) NOT NULL UNIQUE ); 
    
    INSERT INTO estado_civil(nome) VALUES ('Solteiro'); 
    INSERT INTO estado_civil(nome) VALUES ('Casado'); 
    INSERT INTO estado_civil(nome) VALUES ('Divorciado'); 
    INSERT INTO estado_civil(nome) VALUES ('Viúvo');
    • Criar uma coluna com os mesmos valores possíveis:
    SQL
    CREATE TABLE pessoa(
         id      SERIAL PRIMARY KEY,
         nome    VARCHAR(50) NOT NULL UNIQUE,
         estado_civil VARCHAR(15),
         CONSTRAINT estado_civil_check CHECK (
             estado_civil IN ('Solteiro', 'Casado', 'Divorciado', 'Viúvo')
             ) 
     );

    • Criar uma coluna com códigos que representem os valores possíveis:
    SQL
    CREATE TABLE pessoa (
         id      SERIAL PRIMARY KEY,
         nome    VARCHAR(50) NOT NULL UNIQUE,
         estado_civil CHAR(1),
         CONSTRAINT estado_civil_check CHECK (estado_civil IN ('S', 'C', 'D', 'V')
         ) 
    ); 
    COMMENT ON COLUMN pessoa.estado_civil IS 'S = Solteiro, C = Casado, D = Divorciado, V = Viúvo';

    A primeira opção é a que apresenta a melhor modelagem. Permite que você adicione novos tipos de estado civil sem ter que mexer na tabela. Se você tiver varias tabelas com uma FK para a tabela estado_civil, alterando os seus dados, você altera para todas as tabelas relacionadas a identidade do campo estado civil. A única coisa chata é ter que criar uma tabela nova só para isso, e ter que fazer mais um JOIN toda vez que quiser resgatar os nomes dos estados civis.

    A segunda opção deverá consumir muito espaço em disco desnecessariamente, uma vez que vai armazenar a palavra inteira e repeti-la varias vezes. Não é uma opção muito elegante.

    A terceira opção, não tem o nome do estado civil explícito no valor do campo. Você vai ter que traduzir ‘S’ para ‘Solteiro’ no código da sua aplicação. Isso é uma operação bastante indesejada, pois algo que deveria ser dinâmico, se torna hard coded, com uma manutenção ruim para fazer.

    Criando seu ENUM

    Vejamos como ficaria o mesmo problema utilizando ENUM:

    SQL
    CREATE TYPE estado_civil AS ENUM('Solteiro', 'Casado', 'Divorciado', 'Viúvo');
    
    CREATE TABLE pessoa (
        id      SERIAL PRIMARY KEY,
        nome    VARCHAR(50) NOT NULL UNIQUE,
        estado_civil_pessoa estado_civil
    );

    Veja que você criou um tipo de dados chamado estado_civil e utilizou ele na tabela pessoa. É quase como criar uma tabela auxiliar. Mas não existe um ID no meio. Quando você for fazer um INSERT na tabela pessoa, você tem que utilizar os nomes completos de cada tipo:

    SQL
    teste=# INSERT INTO pessoa (nome, estado_civil_pessoa) VALUES ('Paulo', 'Solteiro');
    INSERT 0 1
    teste=# INSERT INTO pessoa (nome, estado_civil_pessoa) VALUES ('João', 'Casado');
    INSERT 0 1
    teste=# INSERT INTO pessoa (nome, estado_civil_pessoa) VALUES ('Joaquim', 'Enrolado');
    ERROR:  invalid input value for enum estado_civil: "Enrolado"
    LINE 1: ...oa (nome, estado_civil_pessoa) VALUES ('Joaquim', 'Enrolado'...

    Veja que você não precisa adicionar uma constraint para limitar os valores possíveis. Se você tentar inserir um valor inválido, o Postgres vai lhe avisar que aquele valor não é aceito, pois não faz parte do tipo de dados estado_civil. No entanto, você pode querer alterar o tipo de dados e incluir um novo elemento:

    SQL
    teste=# ALTER TYPE estado_civil ADD VALUE 'Enrolado';
    ALTER TYPE
    teste=# INSERT INTO pessoa (nome, estado_civil_pessoa) VALUES ('Joaquim', 'Enrolado');
    INSERT 0 1

    Renomeando

    Por fim, você ainda pode alterar o nome de um dos elementos (ou rótulos ou em inglês, labels). A sintaxe é a seguinte:

    SQL
    ALTER TYPE name RENAME VALUE existing_enum_value TO new_enum_value

    Vejamos um exemplo para o tipo estado_civil:

    SQL
    teste=# SELECT * FROM pessoa WHERE nome = 'Joaquim';
     id |  nome   | estado_civil_pessoa
    ----+---------+---------------------
      3 | Joaquim | Enrolado
    (1 row)
    
    teste=# ALTER TYPE estado_civil RENAME VALUE 'Enrolado' TO 'Amasiado';
    ALTER TYPE
    teste=# SELECT * FROM pessoa WHERE nome = 'Joaquim';
     id |  nome   | estado_civil_pessoa
    ----+---------+---------------------
      3 | Joaquim | Amasiado
    (1 row)

    Ordenação

    Se você quiser ordenar as linhas de uma tabela com base em um campo ENUM a ordenação não será feita com base nos valores do tipo estado_civil e sim pela ordem em que eles foram criados no seu tipo:

    SQL
    teste=# SELECT * FROM pessoa ORDER BY estado_civil_pessoa;
     id |  nome   | estado_civil_pessoa
    ----+---------+---------------------
      1 | Paulo   | Solteiro
      2 | João    | Casado
      3 | Joaquim | Amasiado
    (3 rows)

    Se você quiser consultar como estão os elementos do seu tipo ENUM, você pode consultar o catálogo do Postgres com a seguinte consulta:

    SQL
    SELECT 
        typname         AS nome_tipo,
        typlen          AS tamanho_elemento, 
        enumsortorder   AS ordem,
        enumlabel       AS label
    FROM 
        pg_type t 
        JOIN pg_enum e ON t.oid = e.enumtypid 
    WHERE typname = 'estado_civil';
    
      nome_tipo   | tamanho_elemento | ordem |   label
    --------------+------------------+-------+------------
     estado_civil |                4 |     1 | Solteiro
     estado_civil |                4 |     2 | Casado
     estado_civil |                4 |     3 | Divorciado
     estado_civil |                4 |     4 | Viúvo
     estado_civil |                4 |     5 | Amasiado
    (5 rows)

    Veja que todos elementos são armazenados com apenas 4 bytes enquanto o rótulo em si, pode ter até 63 bytes. Na tabela pessoa, independente do tamanho do rótulo serão armazenados apenas 4 bytes, o mesmo tamanho de um campo INTEGER.

    Você também pode utilizar um atalho no psql:

    SQL
    teste=# \dT+ estado_civil
                                               List of data types
     Schema |     Name     | Internal name | Size |  Elements  |  Owner   | Access privileges | Description
    --------+--------------+---------------+------+------------+----------+-------------------+------
     public | estado_civil | estado_civil  | 4    | Solteiro  +| postgres |                   |
            |              |               |      | Casado    +|          |                   |
            |              |               |      | Divorciado+|          |                   |
            |              |               |      | Viúvo     +|          |                   |
            |              |               |      | Amasiado   |          |                   |
    (1 row)

    Você pode ainda adicionar novos elementos em uma posição específica no seu ENUM. Vejamos a sintaxe para isso:

    SQL
    ALTER TYPE name ADD VALUE [ IF NOT EXISTS ] new_enum_value [ { BEFORE | AFTER } neighbor_enum_value ]

    Agora na prática:

    SQL
    teste=# ALTER TYPE estado_civil ADD VALUE 'Namorando' AFTER 'Solteiro';
    ALTER TYPE
    teste=# \dT+ estado_civil
                                               List of data types
     Schema |     Name     | Internal name | Size |  Elements  |  Owner   | Access privileges | Description
    --------+--------------+---------------+------+------------+----------+-------------------+-------------
     public | estado_civil | estado_civil  | 4    | Solteiro  +| postgres |                   |
            |              |               |      | Namorando +|          |                   |
            |              |               |      | Casado    +|          |                   |
            |              |               |      | Divorciado+|          |                   |
            |              |               |      | Viúvo     +|          |                   |
            |              |               |      | Amasiado   |          |                   |
    (1 row)

    Na verdade a sintaxe é um pouco mais elegante, você pode A sintaxe completa para alterar um tipo ENUM

    Restrições

    • O tamanho do rótulo ou label, é de no máximo 63 bytes, que é o valor da constante NAMEDATALEN definida no ato de compilação do PostgreSQL. Ou seja, você pode alterar este valor, mas você terá que trabalhar com uma versão compilada por você mesmo do Postgres para isso. Em geral 63 bytes costuma ser mais que suficiente;
    • Você não pode apagar elementos já existentes no seu ENUM nem trocar eles de lugar depois que já foram inseridos;
    • Você não pode rodar um ALTER TYPE … ADD VALUE dentro de uma transação (entre um BEGIN e um COMMIT ou ROLLBACK);
    • Se você tentar dar um DROP no seu tipo de dados ENUM e ele já estiver sendo utilizado em outras tabelas, vai ter que utilizar um CASCADE, o que vai excluir as colunas que utilizam o ENUM nestas tabelas:
    SQL
    teste=# DROP TYPE estado_civil ; 
    ERROR:  cannot drop type estado_civil because other objects depend on it 
    DETAIL:  table pessoa column estado_civil_pessoa depends on type estado_civil 
    HINT:  Use DROP ... CASCADE to drop the dependent objects too. 
    
    teste=# DROP TYPE estado_civil CASCADE; 
    NOTICE:  drop cascades to table pessoa column estado_civil_pessoa DROP TYPE 
    teste=# \d pessoa
                                        Table "public.pessoa"
      Column |         Type          | Collation | Nullable |              Default
     --------+-----------------------+-----------+----------+------------------------------------
      id     | integer               |           | not null | nextval('pessoa_id_seq'::regclass)
      nome   | character varying(50) |           | not null | 
    Indexes:     
    "pessoa_pkey" PRIMARY KEY, btree (id)     
    "pessoa_nome_key" UNIQUE CONSTRAINT, btree (nome) 

    Conclusão

    Podemos dizer que o uso do ENUM:

    • Tem uma sintaxe limpa e simples de utilizar;
    • Torna a sua vida mais simples, pois você declara o tipo ENUM uma vez e pode utilizar em varias tabelas;
    • Torna suas consultas mais simples do que utilizar uma tabela auxiliar, pois você não precisa fazer outro JOIN;
    • É eficiente em termos de armazenamento, pois ocupa pouco espaço em disco;
    • Não exige a utilização de códigos que precisam ser convertidos na aplicação;
    • Tem uma ordenação própria que leva em conta a ordem dos valores em que são adicionados no tipo e não a ordenação alfabética dos seus labels;
    • Tem uma boa flexibilidade para adicionar novos valores em uma ordem específica;
    • Não permite remover labels sem ter que destruir e recriar o ENUM.
    • Devem ser evitados quando a lista de valores possíveis for muito dinâmica. Neste caso uma tabela auxiliar deve ser mais adequada;

    Resumindo: não serve para tudo, mas no lugar certo, é uma mão na roda!

  • Sintaxe no PostgreSQL: booleanos

    Se você não leu, recomendo que veja o artigo sobre estrutura léxica do PostgreSQL antes de começar por aqui.

    Toda informática é baseada na lógica booleana. Os dados booleanos são utilizados geralmente como flags para marcar se determinado atributo de uma tabela está presente ou não. Por incrível que pareça, apesar de ser o tipo de dado mais simples que existe e fazer parte do padrão SQL, poucos SGDBs implementam este tipo de dado e armazenem ele como uma coluna de uma tabela.

    Uma coluna booleana pode ter apenas 3 valores distintos, falso, verdadeiro ou nulo. O PostgreSQL possui 3 palavras reservadas para lidar com estes valores: FALSE, TRUE e NULL. Veja como exemplo a tabela verdade envolvendo valores booleanos:

    SQL
    teste=# CREATE TABLE bol (x boolean, y boolean);
    CREATE TABLE
                             ^
    teste=# INSERT INTO bol VALUES (FALSE, FALSE);
    INSERT 0 1
    teste=# INSERT INTO bol VALUES (FALSE, TRUE);
    INSERT 0 1
    teste=# INSERT INTO bol VALUES (FALSE, NULL);
    INSERT 0 1
    teste=# INSERT INTO bol VALUES (TRUE, FALSE);
    INSERT 0 1
    teste=# INSERT INTO bol VALUES (TRUE, TRUE);
    INSERT 0 1
    teste=# INSERT INTO bol VALUES (TRUE, NULL);
    INSERT 0 1
    teste=# INSERT INTO bol VALUES (NULL, NULL);
    INSERT 0 1
    
    teste=# SELECT x, y, NOT x AS n_x, NOT y AS n_y, x OR y AS x_or_y, x AND y AS x_and_y FROM bol;
     x | y | n_x | n_y | x_or_y | x_and_y
    ---+---+-----+-----+--------+---------
     f | f | t   | t   | f      | f
     f | t | t   | f   | t      | f
     f |   | t   |     |        | f
     t | f | f   | t   | t      | f
     t | t | f   | f   | t      | t
     t |   | f   |     | t      |
       |   |     |     |        |
    (7 rows)

    Note que como resultado o PostgreSQL exibe uma representação dos valores de TRUE, FALSE e NULL como ‘f’, ‘t’ e vazio respectivamente, mas isso é apenas uma representação. Existem outras formas representar verdadeiro e falso:

    SQL
    teste=# INSERT INTO bol VALUES ('t', 'f');
    INSERT 0 1
    
    teste=# INSERT INTO bol VALUES ('T', 'F');
    INSERT 0 1
    
    teste=# INSERT INTO bol VALUES ('true', 'false');
    INSERT 0 1
    
    teste=# INSERT INTO bol VALUES ('TRUE', 'FALSE');
    INSERT 0 1
    
    teste=# INSERT INTO bol VALUES ('YES', 'NO');
    INSERT 0 1
    
    teste=# INSERT INTO bol VALUES ('yes', 'no');
    INSERT 0 1
    
    teste=# INSERT INTO bol VALUES ('y', 'n');
    INSERT 0 1
    
    teste=# INSERT INTO bol VALUES ('Y', 'N');
    INSERT 0 1
    
    teste=# INSERT INTO bol VALUES ('on', 'off');
    INSERT 0 1
    
    teste=# INSERT INTO bol VALUES ('ON', 'OFF');
    INSERT 0 1
    
    teste=# INSERT INTO bol VALUES ('0', '1');
    INSERT 0 1
    
    teste=# INSERT INTO bol VALUES (0, 1);
    ERROR:  column "x" is of type boolean but expression is of type integer
    LINE 1: INSERT INTO bol VALUES (0, 1);
    HINT:  You will need to rewrite or cast the expression.
    
    teste=# INSERT INTO bol VALUES ('NULL', 'null');
    ERROR:  invalid input syntax for type boolean: "NULL"
    LINE 1: INSERT INTO bol VALUES ('NULL', 'null');
    
    teste=# INSERT INTO bol VALUES ('', '');
    ERROR:  invalid input syntax for type boolean: ""
    LINE 1: INSERT INTO bol VALUES ('', '');

    Você não pode exagerar muito, se você colocar qualquer número sem aspas simples, o PostgreSQL sempre vai interpretar isso como um número. E NULL só pode ser representado com a sua palavra reservada. Não pode estar entre aspas simples e vazio é diferente de nulo. Você não pode inserir ” como sinônimo para NULL.

    O PostgreSQL aceita coisas como ‘t’, ‘true’, ‘y’, ‘yes’, ‘on’, ‘1’ como sinônimos para TRUE. Mas isso funciona dentro de um INSERT. Neste contexto o PostgreSQL faz uma coerção implícita para o tipo correto de dados. Vejamos o que ocorre em outros contextos:

    SQL
    teste=# SELECT TRUE AND FALSE;
     ?column?
    ----------
     f
    (1 row)
    
    teste=# SELECT 'TRUE' AND 'FALSE';
     ?column?
    ----------
     f
    (1 row)
    
    teste=# SELECT 't' AND 'f';
     ?column?
    ----------
     f
    (1 row)
    
    teste=# UPDATE bol SET x = 'on' WHERE y = 'off';
    UPDATE 12

    So far, so good. Mas existe um contexto que é mais sensível: a chamada de uma função. Suponhamos que você tenha uma função como esta:

    SQL
    CREATE FUNCTION not_and (boolean, boolean) RETURNS boolean AS
    $func$
    BEGIN
        RETURN NOT ($1 AND $2);
    END;
    $func$
    LANGUAGE plpgsql;

    Agora vamos chamar esta função:

    SQL
    teste=# SELECT not_and('true', 'false');
     not_and
    ---------
     t
    (1 row)

    Perfeito. Tudo tranquilo. Parece que o PostgreSQL está se comportando exatamente como você esperava. Então onde está o problema? Bom, acontece que no PostgreSQL você pode ter sobrecarga de funções. Ou seja, varias funções com o mesmo nome, recebendo parâmetros diferentes.  Vejamos um exemplo:

    SQL
    CREATE FUNCTION not_and (text, text) RETURNS text AS
    $func$
    BEGIN
        RETURN $1 || $2;
    END;
    $func$
    LANGUAGE plpgsql;

    A função tem o mesmo nome, mas recebe parâmetros do tipo TEXT. Vejamos o que acontece quando fazemos exatamente a mesma chamada que antes:

    SQL
    teste=# SELECT not_and('true', 'false');
      not_and
    -----------
     truefalse
    (1 row)

    Aqui o comportamento foi diferente. E veja que utilizamos exatamente a mesma chamada que antes! O PostgreSQL utilizou a função com parâmetros do tipo TEXT e não do tipo BOOLEAN. Para corrigir isso você tem duas opções:

    SQL
    teste=# SELECT not_and(boolean 'true', boolean  'false');
     not_and
    ---------
     t
    (1 row)
    
    teste=# SELECT not_and(TRUE, FALSE);
     not_and
    ---------
     t
    (1 row)

    OU seja: ou você utiliza uma coerção explícita (que você vai utilizar muito com outros tipos de dados em chamadas de funções) ou utiliza as palavras reservadas. Particularmente eu acho que utilizar sempre as palavras reservadas TRUE e FALSE muito mais elegante e evita possíveis dores de cabeça no futuro. Você pode perder um bom tempo até descobrir que existe outra função com o mesmo nome mas parâmetros diferentes…

    Existe ainda um contexto peculiar em que valores booleanos podem ser utilizados de forma peculiar. Quando você quer fazer um teste do tipo verdadeiro ou falso, como num IF ou num WHERE, você pode utilizar uma sintaxe mais direta. Veja o UPDATE abaixo com o WHERE escrito de duas formas diferentes:

    SQL
    teste=# UPDATE bol SET x = 'on' WHERE y = TRUE;
    UPDATE 3
    teste=# UPDATE bol SET x = 'on' WHERE y;
    UPDATE 3

    Observação importante

    Por último um pequeno comentário sobre o uso de booleanos como tipo de dados em suas tabelas. O PostgreSQL aloca um byte e não um bit para armazenar uma coluna do tipo booleano. Em muitos casos você pode utilizar outros tipos de dados no lugar do BOOLEAN, como um TIMESTAMP (que ocupa 8 bytes) por exemplo. Diferente de tempos passados, economizar demais em tipos de dados não é uma grande vantagem. Mas imagine o caso em que você quer um flag para saber se um cliente está inativo ou não. Se você utilizar um TIMESTAMP, você pode deixar a coluna como NULL se o cliente não estiver inativo e inserir a data de inativação caso o cliente tenha sido inativado. Se uma minoria dos clientes forem inativados, o consumo de espaço em disco a mais será mínimo e você está armazenando mais informação neste caso. Saber a data de inativação pode ser importante para você no futuro. Existem outros casos em que ativo e inativo pode não ser suficientes, você pode querer ter mais valores possíveis para a sua coluna. Neste caso você poderia utilizar o tipo ENUM.

  • Sintaxe no PostgreSQL: estrutura lexica

    Este post inicia uma série de artigos sobre a linguagem SQL no PostgreSQL. Na documentação oficial, o capítulo 4 possui uma longa explicação sobre sintaxe SQL no PostgreSQL. Confesso que quando eu li sobre isso há mais de 15 anos, tive vontade de pular essa parte. Bastante burocrática e pouco interessante. Mas… o mal mora nos detalhes! Entre as minhas abas abertas do meu navegador (quase sempre com uma ou duas abas da documentação do Postgres), me vejo revisitando este capítulo mais e mais vezes. Gostaria de começar pelo começo… e de fato, temos muitos detalhes importantes que vão influenciar na forma como você deve escrever seus comandos SQL no PostgreSQL.

    Dando nomes às partes

    Trazendo diretamente da documentação temos:

    “SQL input consists of a sequence of commands. A command is composed of a sequence of tokens, terminated by a semicolon (“;”).”

    A tradução de alguns termos é meio estranha aos ouvidos, por isso trouxe a expressão original do inglês, mas vamos lá: “Uma entrada SQL consiste numa sequência de comandos. Cada comando é composto por uma sequência de tokens terminadas por um ponto e vírgula (“;”). ”

    A tradução da palavra token é meio complicada, mas você pode chamar de símbolo, sinal, palavra ou outras possibilidades que o Google me sugeriu, mas isto iria confundir a definição a seguir:

    “Um token pode ser uma palavra chave, um identificador, um identificador entre aspas*, uma constante, ou um símbolo de caractere especial. As fichas são normalmente separadas por espaços em branco(espaços, tabulações ou nova linha), mas isso não é obrigatório se não houver ambiguidade (que é geralmente o caso se um caractere especial estiver entre fichas de outros tipos)”

    Uma definição relativamente simples, que já dá algumas dicas importantes, mas vamos por partes. Agora vamos ver isso em detalhes:

    Palavras chave

    São fichas que possuem um significado especial na linguagem SQL como UPDATE, SELECT, WHERE, FROM, etc. As palavras chave vão mudando com o passar do tempo como você pode ver na lista da documentação. Isso significa que conforme novas versões do padrão SQL vão surgindo, novas palavras chave vão sendo adicionadas. Da mesma forma, conforme novas versões do PostgreSQL vão sendo lançadas, novas implementações do padrão SQL e fora do padrão SQL vão sendo incorporadas a lista de palavras chave. Existem funcionalidades que o PostgreSQL lança antes de se tornarem padrão SQL e funcionalidades do padrão SQL que o PostgreSQL não implementa. Para complicar um pouco mais, existem palavras chaves reservadas e não reservadas. A diferença é que as palavras chave reservadas nunca podem ser utilizadas como identificadores e as palavras chaves não reservadas podem ser utilizadas em alguns tipos de identificadores. E tem mais uma questão importante… nada impede que no futuro, novas palavras chaves sejam criadas para novas funcionalidades que venham a ser implementadas no futuro.

    Então o mais importantes sobre palavras chaves que você deve entender é: não use uma palavra chave como identificador. Seja esse identificador o nome de uma tabela, coluna de tabela ou nome de uma função. Não use e pronto. Independente se ela é reservada no padrão SQL:92, SQL:2011 ou SQL:2016 ou apenas no PostgreSQL. Evite ao máximo. Sua vida vai ficar bem mais fácil assim.

    Uma excessão à regra é o uso de apelidos (alias em inglês) em nomes de colunas como em

    SELECT resultado AS table FROM foo;

    Palavras chave não fazem distinção de letras maiúsculas e minúsculas (ou em inglês case insensitive) no PostgreSQL. Isso significa que você pode escrever UPDATE, update, ou UPdate e o Postgres vai interpretar exatamente da mesma forma. Mas é um costume antigo e considerado desejável, que você escreva suas palavras reservadas sempre em letras maiúsculas. Algumas vezes as pessoas também tem o hábito de escrever nomes de funções em letras maiúsculas. No entanto, nomes de funções não são palavras reservadas, são identificadores, o que pode gerar alguns problemas em situações bastante específicas. Todas as funções internas do PostgreSQL possuem nomes em letras minúsculas e você deveria fazer o mesmo com as funções em PL que você criar. No entanto, como veremos a seguir, identificadores não são case insensitive.

    Identificadores

    Identificadores são nomes que você dá para os objetos que você cria no seu banco de dados, bem como variáveis em linguagens de programação como o PL/pgSQL. Como dissemos antes, não utilize nunca palavras reservadas como identificadores. Identificadores são sensíveis a diferenças entre maiúsculas e minúsculas (case sensitive em inglês) de uma forma particular. Se o seu identificador não estiver entre aspas duplas, o Postgres vai considerar todas as letras como minúsculas independente de como você escrever. Desta forma: ID, id e iD para o PostgreSQL sempre serão equivalentes a id. Por outro lado, se você utilizar aspas duplas, como em “ID”, “id” e “iD”, cada um deles se referem a um objeto diferente. Isso pode causar uma bela confusão, maior do que você possa imaginar. Então, por uma questão de sanidade mental e maior chance de não ser colocado de castigo na chuva num domingo de carnaval amarrado no meio da jaula dos leões, é melhor você criar identificadores com letras minúsculas e apenas letras minúsculas. Você não vai precisar nunca das aspas e nunca vai se confundir. Para separar várias palavras num identificador, você utiliza o sublinhado ( _ ), como em nome_produto_nota. Simples e fácil de ler. Você pode criar identificadores com até 63 caracteres, o que é mais do que suficiente para você criar um monstro se necessário.

    Então vamos às regras de ouro dos identificadores:

    • Use letras minúsculas, sempre;
    • Não utilize acentos ou melhor, utilize apenas letras do alfabeto latino sem acentos. Nada de cirílico, letras gregas, runas, ideogramas etc;
    • Se seu identificador possui varias palavras, separe elas por um sublinhado;
    • Todo identificador deve começar com uma letra;
    • Você pode utilizar alem de letras (eu te avisei para só utilizar as minúsculas, né?), algarismos de 0 a 9 e sublinhado. Também é possível usar o $, mas como isso não é padrão SQL, isso não é uma boa ideia;

    Por fim, uma recomendação importante: siga um padrão de nomenclatura para os seus identificadores. Não importa qual seja. A discussão sobre qual é o melhor padrão é interminável. Apenas escolha um que fizer mais sentido para a sua equipe siga ele até a morte. Se você mudar de padrão no meio de um projeto, vai instalar o caos. Mas adote um padrão. Qualquer um.

    Constantes

    Constantes são os dados atribuídos aos seus identificadores. Eles podem ter diversos tipos e cada um deles tem suas peculiaridades. O SQL é uma linguagem fortemente tipada. Isso significa que o tipo da constante faz muita diferença para ele e você deve se importar com isso. E muito. Você será uma pessoa muito mais feliz, se souber utilizar isso de forma correta. Pode não ficar milionário, mas as pessoas vão te respeitar mais, você terá mais sucesso no amor e terá menos doenças relacionadas ao stress.

    Dependendo de como você escrever uma determinada constante, ela vai ser automaticamente enquadrada em um tipo específico. Isso ocorre particularmente com texto (sempre entre aspas simples) e números. Em algumas situações, como durante um INSERT, UPDATE ou numa cláusula WHERE, o PostgreSQL vai interpretar o tipo da constante segundo o campo relacionado.

    Números

    Números possuem uma sintaxe específica. Sempre que você escrevem algarismos sem aspas simples ou duplas, o PostgreSQL vai tentar interpretar como um número. O detalhe importante é como lidar com o separador de casas decimais e com a notação científica:

      • Sem separador decimal, o PostgreSQL interpreta sempre a constante como um INTEGER. Se o número for muito grande, ele vai utilizar o BIGINT. Note que a vírgula é utilizada para separar duas constantes inteiras distintas. Ex:
    teste=# SELECT 42,8;
     ?column? | ?column?
    ----------+----------
           42 |        8
    • Com o separador decimal (que é sempre um ponto, não uma vírgula), o PostgreSQL interpreta como um NUMERIC. Ex:
    teste=# SELECT 4.2, .4, 4.;
     ?column? | ?column? | ?column?
    ----------+----------+----------
          4.2 |      0.4 |        4
    • Podemos usar a notação científica também usando a letra ‘e’ ou ‘E’. Então quando queremos um número como 42×10³ que é a mesma coisa que 42000, usamos 42e3. Ex:
    teste=# SELECT 42e5, 42e-5, 4.2e5, 4.2e-5;
     ?column? | ?column? | ?column? | ?column?
    ----------+----------+----------+----------
      4200000 |  0.00042 |   420000 | 0.000042
    (1 row)
    
    teste=# SELECT 42E5, 42E-5, 4.2E5, 4.2E-5;
     ?column? | ?column? | ?column? | ?column?
    ----------+----------+----------+----------
      4200000 |  0.00042 |   420000 | 0.000042
    (1 row)

    Cadeias de Caracteres

    Caracteres devem vir sempre entre aspas simples. Não confundir com aspas duplas que são utilizados para identificadores, como vimos acima e não para constantes. Existem 3 tipos de constantes relacionadas com caracteres no PostgreSQL: CHARACTER ou CHAR,  CHARACTER VARYNG ou VARCHAR e TEXT. Se você não especificar nada, o PostgreSQL vai sempre assumir como TEXT, que é semelhante ao VARCHAR sem limitação de tamanho (até o limite do PostgreSQL de 1GB). Trabalhar com cadeias de caracteres (ou strings em inglês) tem suas peculiaridades.

    Concatenação

    Juntar duas strings é fácil:

    teste=# SELECT 'foo','bar';
     ?column? | ?column?
    ----------+----------
     foo      | bar
    (1 row)
    
    teste=# SELECT 'foo' 'bar';
    ERROR:  syntax error at or near "'bar'"
    LINE 1: SELECT 'foo' 'bar';
                         ^
    teste=# SELECT 'foo' || 'bar';
     ?column?
    ----------
     foobar
    (1 row)
    
    teste=# SELECT 'foo'
    teste-# 'bar';
     ?column?
    ----------
     foobar
    (1 row)
    
    • No primeiro caso, a vírgula separa duas constantes distintas, assim como ocorreu com números.
    • No segundo caso tivemos um erro de sintaxe.
    • Para concatenar as duas cadeias de caracteres, nós usamos o operador ||, como mostrado no terceiro exemplo.
    • O último exemplo é um pouco bizarro (mas é o que o padrão SQL define): se duas cadeias de caracteres são separadas por uma ou mais linhas em branco, elas são consideradas como se estivessem concatenadas. Vai por mim, evite esse tipo de coisa e use o operador || quando precisar.

    Citação tipo dólar (dollar quoting)

    Um dos problemas que temos ao lidar com texto é que eles são delimitados por aspas simples, mas em varias situações você pode querer incluir uma aspa simples no seu texto. A forma tradicional de lidar com isso é duplicar a aspa simples. Vejamos o caso clássico da Joana d’Arc:

    teste=# SELECT 'Joana d''Arc';
      ?column?
    -------------
     Joana d'Arc

    O PostgreSQL possui uma alternativa para isso que não faz parte do padrão SQL mas que é bastante útil. Você pode ao invés de iniciar e terminar a sua cadeia de caracteres com uma aspa simples, você pode usar dois sinais de dólar, desta forma:

    teste=# SELECT $$Joana d'Arc$$;
      ?column?
    -------------
     Joana d'Arc
    

    Na verdade você pode ainda utilizar um identificador qualquer entre os dois sinais de dólar (mas precisa ser o mesmo identificador no início e no final), se preferir:

    teste=# SELECT $txt$Joana d'Arc$txt$;
      ?column?
    -------------
     Joana d'Arc

    Aqui, nós temos Joana d’Arc envolvidos por $txt$. De fato, isso ajuda muito em situações mais complexas. Quando você vai escrever uma função por exemplo, todo o código é considerado como texto. Para iniciar e terminar este texto, delimitamos ele com $function$ ou $BODY$.

    Um detalhe curioso é que a maioria dos editores de texto que utilizam o realce de sintaxe (Syntax highlighting) se perdem quando utilizamos este tipo de notação. Aqui no texto eu deliguei o realce no segundo exemplo para ajudar um pouco, no primeiro, veja que ele considera o início da constante no lugar errado.

    Escape no estilo C

    O PostgreSQL foi todo escrito na linguagem C e como tal traz algumas práticas desta linguagem. Antigamente, qualquer cadeia de caractere poderia ser recheada de escapes no estilo C, utilizando as famosas barras invertidas ( \ ). Isso é muito útil para incluir caracteres não imprimíveis como uma tabulação ou uma quebra de linha. Antigamente o PostgreSQL aceitava isso nativamente. Existe uma tabela na documentação com todas as possibilidades, mas as mais comuns são:

    • \n (quebra de linha)
    • \t (tabulação)
    • \r (equivalente ao “enter“)
    • \\ (o caractere \)
    • \’ (uma aspa simples)

    A partir da versão 9.1 do PostgreSQL o parâmetro standard_conforming_strings passou a vir ligado por padrão. Isso significa que o comportamento histórico de aceitar escapes no estilo C dentro de uma cadeia de caracteres. Para contornar isso, o Postgres (e outros SGDBs também) criaram uma sintaxe alternativa para aceita-los. Basta preceder a aspa simples que abre a cadeia de caracteres por um E maiúsculo ou não:

    teste=# SELECT E' foo \n bar';
     ?column?
    ----------
      foo    +
      bar
    (1 row)
    
    teste=# SELECT E'Joana d\'Arc';
      ?column?
    -------------
     Joana d'Arc
    (1 row)
    

    No primeiro exemplo, utilizamos o \n para para quebrar a linha. No segundo utilizamos para escapar a aspa simples. Veja que o escape no estilo C é mantido dessa forma mais não é padrão SQL. Não estimulamos o seu uso. Se você realmente precisa inserir caracteres não imprimíveis como uma tabulação ou uma quebra de linha, você pode fazer isso usando o padrão SQL com uma função desta forma:

    teste=# SELECT ' foo ' || CHR(10) || ' bar';
     ?column?
    ----------
      foo    +
      bar
    (1 row)
    

    Caracteres unicode

    Aqui as coisas começam a complicar um pouco mais. Você pode querer inserir caracteres que não estão na tabela ASCII padrão, mas caracteres em outros alfabetos. Se você utiliza UTF8 como codificação de caracteres (você deveria fazer isso sempre, sério), então você pode utilizar esta notação para inserir caracteres específicos que seu teclado ABNT não possui. Você deve preceder a aspa simples com um sinal de U&:

    teste=# SELECT   U&'\0441\043B\043E\043D';
     ?column?
    ----------
     слон
    (1 row)
    

    Existe um operador que utiliza o caractere & no PostgreSQL. Por este motivo, quando quiser trabalhar com caracteres UTF8 utilize o U&’ sem espaços, quando quiser utilizar o operador & utilize sempre com espaços antes de depois dele, de forma evitar qualquer ambiguidade.

    Cadeias de caracteres binárias

    Existem 2 tipos de cadeias de caracteres binárias no PostgreSQL: BIT (com tamanho fixo, assim como em CHAR) e BIT VARYING. Como se pode imaginar, eles só aceitam os caracteres 0 e 1. Este tipo é muito utilizado em mapas de bits, uma técnica muito comum em programação em C. Para utilizar cadeias binárias, você pode também utilizar caracteres hexadecimais, onde cada caractere hexadecimal equivale exatamente a 4 caracteres binários. Para isso você utiliza o B como prefixo quando for utilizar números binários e o X quando for utilizar números hexadecimais:

    teste=# SELECT B'1010', X'A';
     ?column? | ?column?
    ----------+----------
     1010     | 1010
    (1 row)
    

    Coerção de tipos de dados

    Todos os outros tipos de dados trabalham em geral com constantes entre aspas simples, em diferentes formatos. Se você estiver realizando uma operação de INSERT por exemplo, pode ser que o PostgreSQL consiga converter implicitamente para o tipo correto durante a operação. Mas em outras situações isso nem sempre acontece. Existem muitos detalhes dependendo de cada tipo de dado envolvido. Vamos deixar estes detalhes para outro artigo!

    Existem 3 formas de definir com qual tipo de dado estamos trabalhando:

    type ‘string’
    ‘string’::type
    CAST ( ‘string’ AS type )

    • O primeiro é estilo é o mais utilizado e deve ser preferido no dia-a-dia;
    • O segundo estilo só existe no PostgreSQL e é mantido por razões históricas, portanto você deveria evitar o mesmo;
    • O último utiliza a função CAST. Funciona bem e é padrão SQL, mas é um pouco mais verborrágico. Acredito que a primeira opção é mais enxuta.

    Vejamos alguns exemplos utilizando os 3:

    teste=# SELECT NUMERIC(4,2) '42';
     numeric
    ---------
       42.00
    (1 row)
    
    teste=# SELECT '42'::NUMERIC(4,2);
     numeric
    ---------
       42.00
    (1 row)
    
    teste=# SELECT CAST('42' AS NUMERIC(4,2));
     numeric
    ---------
       42.00

    Note que quando utilizamos o 42 no primeiro exemplo, ele foi considerado um INTEGER e não um NUMERIC. Outro detalhe é que aqui utilizamos sim um número entre aspas simples. Esta regra vai valer para praticamente qualquer tipo de dado. Mas claro, sempre existem exceções…

    Operadores

    operadores são funções que utilizam símbolos especiais e tem significados específicos dependendo do tipo de dado envolvidos junto aos operadores. Em geral estamos acostumados com operadores que utilizam um caractere só, como +, -, * e /. Mas o PostgreSQL possui uma gama enorme de operadores que podem utilizar um ou mais dos seguintes caracteres:

    + – * / < > = ~ ! @ # % ^ & | ` ?

    • Observação: Existem algumas regras para evitar confusão como comentários como — e /*

    Os operadores são uma parte muito importante do PostgreSQL e um dos seus grandes trunfos. Isso porque estes operadores são indexáveis. Então você pode utilizar um tipo de dados geométrico e fazer uma consulta onde na cláusula WHERE você utiliza o operador ?-| para saber se outra figura geométrica é perpendicular ao seu polígono. E utilizar um índice para acelerar esta consulta!

    Caracteres especiais

    Existe por fim uma lista de caracteres especiais utilizados em situações específicas:

    • $ –  O dólar é utilizado para numerar parâmetros de uma função de acordo com a ordem que aparece na chamada da função e na notação de cadeia de caracteres com o $$ como explicamos há pouco;
    • () – Os parenteses são utilizados para agrupar parâmetros de uma função, agrupar expressões e para reforçar a precedência de operadores;
    • [] – Os colchetes são utilizados em matrizes (arrays);
    • , – As vírgulas servem para separar elementos sintáticos como variáveis, identificadores e constantes;
    • ; – O ponto e vírgula fecha comandos SQL;
    • : – Os dois pontos são utilizados em matrizes (arrays) para separar pedaços do mesmo. Também podem ser utilizados em substituição de variáveis como prefixo antes do nome da variável;
    • * – O Asterisco pode substituir um conjunto de identificadores ou utilizado em funções de agregação;
    • . – O ponto separa a parte inteira da decimal em números e também separa nome de esquemas de tabelas e colunas.

    Próximos passos

    Nos próximos artigos vamos brincar um pouco com vários tipos de dados e funções e ver como utilizar de maneira correta. Prepare-se!

     

  • Lançada a grade do PGConf Brasil 2018

    Em novembro eu postei aqui e aqui sobre o PGConf Brasil 2018. Eu prometi para mim mesmo que não ficaria postando trocentas vezes sobre o evento. Mas é um momento muito especial e eu estou orgulhoso em divulgar uma grade realmente excepcional.

    Sobre seleção das palestras

    Foi uma maratona… recebemos um número recorde de propostas de palestras na nossa chamada de trabalhos: mais de 80! Algo que eu havia notado no PGBR2017 se confirmou logo de cara: o nível do pessoal subiu. Em 2007 quando começamos, o nível ainda era mais iniciante. Agora vemos mais palestras com nível elevado e um público mais experiente também. Em 2018 continuaremos tendo boas palestras de nível básico, mas a maioria tem nível intermediário.

    Tivemos 5 pessoas na banca avaliadora. Apenas uma pessoa da Timbira, que não fui eu. E mais, a decisão da banca avaliadora foi soberana. Parece que não, mas isso faz toda a diferença quando pensamos na qualidade do evento que estamos organizando. A ideia é que mesmo o evento sendo organizado pela Timbira, nós seguimos as recomendações da comunidade internacional e fomos reconhecidos como “evento da comunidade“. Isso significa que a Timbira (como sempre) não vai ganhar um tostão com a organização do evento e não teremos um evento comercial para vender a Timbira. Nosso compromisso é com os profissionais que utilizam (ou estão pensando em utilizar) o PostgreSQL no Brasil. Simples assim.

    Sobre as salas

    Tivemos que abrir mais uma sala para acomodar as palestras. Decidimos trazer o maior número de palestras relevantes possível, mesmo sem saber quantos inscritos teremos. A foto deste post foi tirada em 2011 no mesmo hotel em que o evento vai acontecer e deu nisso: salas lotadas. Achamos que em 2018 não vai ser muito diferente e reservamos TODAS as salas do hotel. Então vai ficar assim:

    • Sala de exposições “Josh Berkus” para o credenciamento, patrocinadores, coffee breack e happy hour;
    • Sala “Marc G. Fournier” com capacidade para 230 lugares;
    • Sala “Thomas G. Lockhart” com capacidade para 120 lugares;
    • Sala “Vadim B. Mikheev” com capacidade para 120 lugares;
    • Sala “Jan Wieck” para os Hacker Talks, Hackathon e Couch Clinic com capacidade para 20 lugares;
    • Sala VIP para palestrantes com capacidade de 20 lugares.

    O nome das salas é uma homenagem aos nossos “Hackers Emeritus“: desenvolvedores que tiveram contribuições excepcionais no PostgreSQL e que não estão mais ativos.

    Sobre os palestrantes

    Temos ao todo 44 palestrantes confirmados. Temas bastante variados, mas com muito mais foco em DevOps, nuvem e desenvolvimento.

    • 36 palestrantes nacionais
      • SP
        • Arlindo do Carmo e Silva Neto
        • Carlos Smaniotto
        • Danielle Monteiro
        • Everaldo Canuto
        • Fabiano Menegidio
        • Fábio Telles Rodriguez
        • Felipe Oliveira
        • Fernando Ike
        • Juliano Atanazio
        • Leonardo Ferreira Leite
        • Matheus Oliveira
        • Renan Ranelli
        • Waldir Pimentel
        • Yago Nobre dos Santos
      • Brasília
        • Davy Alvarenga Machado
        • Gerdan dos Santos
        • Glauco Torres
        • Marcone Viana Peres
        • Raul Diego de Queiroz Oliveira
      • SC
        • Dickson S. Guedes
        • Erivelton Vichroski
        • Lucas Eduardo Viecelli
        • Marcelo Kruger
      • RS
        • Álvaro Melo
        • Fabrízio de Royes Mello
        • Rodrigo Crespi
        • Sebastian Webber
      • TO
        • Euler Taveira
        • Raphael Araújo e Silva
      • PR
        • Rafael Thofehrn Castro
        • William Ivanski
      •  RJ
        • Lucio Chiessi
      • ES
        • Gustavo Sperandio
      • RO
        • Luis Fernando Bueno
      • CE
        • Narcelio de Sá Pereira Filho
      • AM
        • Roberto Mello
    • 8 Palestrantes internacionais
      • EUA
        • Ivan Novick
        • Joe Conway
        • Stephen Frost
      • Argentina
        • Martín Marqués
      •  Chile
        • Alvaro Herrera
      • Cuba
        • Gilberto Castillo
      •  Equador
        • Jaime Casanova
      • Itália
        • Rubens Souza

    Agora, se você quer ver a grade e conhecer todas as palestras, só tem um jeito, vai lá no site do evento!

    Muito mais que palestras

    Teremos palestras de 30, 60 e 120 minutos. Mas teremos algumas coisas que nem todos estão acostumados a ver:

    • Hacker Talks: Palestras com foco em apoiar pessoas que queiram desenvolver novas funcionalidades no PostgreSQL;
    • Hackathon: Pessoas desenvolvendo novas funcionalidades relacionadas ao PostgreSQL ao vivo;
    • Couch Clinic: Nossos palestrantes estarão dando consultoria de graça para quem quiser, bastará se inscrever;
    • Lightning Talks: Uma sessão com 12 palestras de 5 minutos. Qualquer um pode palestrar e se inscrever durante o evento, só não pode estourar os 5 minutos;
    • Happy Hour: Preparamos na noite do primeiro dia do evento um Happy Hour com todos participantes do evento. Uma ótima oportunidade para melhorar o seu networking.

     

     

    E agora???

    Bom, agora não tem muito jeito, só fazendo a sua inscrição mesmo. Lembrando que o valor atual das inscrições só vão até junho. As opções de compra antecipada de camisetas e canecas também acabam em junho. Então vai lá e faça sua inscrição agora!

  • Extreme Go Horse DBA

    Todos já conhecem o DBA Chuck Norris, aquele mega phoda que tudo pode no CPD. Aquele que está acima de Deus. Mas os tempos mudaram, estamos na era DevOps e migrando tudo para as nuvens. Então a moda agora é DBA XGH. Veja aqui uma compilação das melhores técnicas XGH para PostgrSQL:

    • Se você é um ninja do Linux, SEMPRE compile o PostgreSQL e explore todas as opções possíveis de compilação. Se possível utilize também o –whithout-readline e –without-zlib, assim você consegue tirar até a última gota de desempenho do seu banco de dados;
    • Para não ter problemas na hora de compilar, instale todos os pacotes da sua distribuição Linux, você garante uma instalação limpa, sem problemas com dependências e pronta para rodar qualquer parada;
    • Compre o maior disco possí
    • Na hora de particionar os discos, adote a filosofia KISS (Keep It Simple, Stupid): Deixe o particionador automático ocupar o disco inteiro com apenas uma partição. Você elimina assim as complexidades desnecessárias, aumenta o desempenho e não desperdiça espaço em disco;
    • Se você tiver bases grandes com alguns TB, aí é melhor você comprar um único disco grande como 8TB e dividir o disco em várias partições menores de 100GB colocando um tablespace em cada um, distribuindo assim o I/O no disco;
    • Se você tem vários discos e quer obter a melhor performance, utilize sempre um único RAID 0 e continue dividindo tudo em partições de 100GB;
    • Ao criar seu banco de dados sempre utilize como codificação de caractere padrão o SQL_ASCII. Você elimina a necessidade de conversões, ganha espaço em disco e desempenho, além de trabalhar com um padrão que qualquer linguagem de programação entende;
    • Use e abuse da flexibilidade do VARCHAR. Com ele você tem uma compatibilidade direta com a web que entende apenas texto. Com o VARCHAR você consegue armazenar texto, RG, CPF, CEP, telefones, Json, XML, o que você quiser.
    • Se precisar de campos flexíveis, você também pode utilizar o VARCHAR marcando as posições para distribuir vários campos de tamanho fixo dentro de uma cadeia de caracteres. Essa é uma técnica campeã utilizada já no tempo do Cobol e funciona até hoje;
    • Evite erros na sua aplicação removendo qualquer tipo de restrição do tipo PRIMARY KEY, FOREIGN KEY, UNIQUE e NOT NULL. Suas consultas vão rodar mais rápido e não vão mais dar erro na execução;
    • DevOps exige agilidade, utilize o esquema padrão e o usuário padrão para tudo. Assim você não tem que armazenar varias senhas ou se preocupar com vários esquemas.
    • Você ainda pode criar um acesso universal colocando a seguinte linha no seu pg_hba.conf: “host    all    all    0.0.0.0/0    trust”;
    • Uma ideia para facilitar o suporte remoto é colocar um IP público no servidor de banco de dados. Você vai conseguir acessar seu servidor sem rodeios, de forma muito mais ágil;
    • Não mexa no postgresql.conf, deixe os valores padrão que são otimizados para funcionar no seu hardware e na sua aplicação já na instalação;
    • Em nenhuma hipótese ative a gravação dos logs do banco de dados, isso vai ocupar mais espaço em disco e gerar mais I/O deixando seu banco de dados mais lento;
    • A melhor ferramenta de backup é sempre o PGAdmin. Em poucos clicks você resolve tudo, ponto final;
    • O jeito mais prático de matar uma sessão no banco de dados é com o comando kill -9, não falha nunca;
    • O melhor jeito de não ter dor de cabeça com os desenvolvedores é criar um super usuário próprio para eles na produção. Esta metodologia ágil é conhecida como OLD: On Line Development;
    • Se você tem uma tabela crítica para o seu sistema, crie um índice para cada campo da sua tabela e garanta que seu sistema sempre fará consultas indexadas;
    • Se você tem muitas tabelas, você deve criar uma view juntando a maioria das tabelas mais utilizadas e a partir dela criar todas as outras consultas do seu sistema;
    • A melhor forma de evitar problemas com o autovacuum é desligar ele para todas as tabelas. Assim você garante que não vai ter processos pesados de vacuum rodando no seu horário de pico;
    • Se você tem uma aplicação que é “database centric” e trabalha com um único banco monolítico, migre para a nuvem o quanto antes. Quanto maior o seu banco de dados, maior será a economia que você terá migrando diretamente para a nuvem. Sucesso garantido;
    • A melhor versão do PostgreSQL que já fizeram é a 8.2. Tudo que veio depois é besteira. Está funcionando, não é mesmo? Não mexa!
    • Repita comigo: SQL simples roda rápido, SQL com subconsultas é lento. Se você tem que atualizar 10 milhões de registros, a melhor forma é fazer 10 milhões de UPDATEs simples no banco de dados, claro;
    • Se você tem uma consulta complexa para fazer, crie uma função e divida a operação em diversas etapas e loops, simplificando a lógica e a execução da mesma;
    • A documentação do PostgreSQL é longa, prolixa e complexa. Já ouviu falar no Google?

     

    Você utiliza XGH na sua empresa também? Ajude os demais colegas e conte nos comentários as técnicas de alto rendimento que você desenvolveu e recomenda para todos!

     

     

     

     

     

    OBS: Eu sei…. o Go Horse Process não chega a ser algo novo. Mas sabe como é DBA: tá sempre atrasado no hype.

  • Chaves Artificiais no PostgreSQL: desempenho

    Falamos sobre a criação de chaves artificiais, sobre como usar UUID ou sequencias, inclusive sobre a nova sintaxe adotada no PostgreSQL 10 utilizando as chamadas IDENTITY COLUMNs. Uma reação comum que eu vi por aí é achar que é mais simples usar UUID sempre, uma vez que você não tem risco de colisão e pode usar em sistemas distribuídos. Bom, a história não é bem assim. Existe um custo adicional ao se escolher UUIDs ou mesmo gerar hashs enormes e armazenar no banco de dados. Vou aqui explorar algumas possibilidades, entre utilizar uma SEQUENCE armazenada num campo INTEGER e índices do tipo BTREE e HASH (que melhorou muito no PostgreSQL 10). Depois vamos utilizar campos do tipo UUID, VARCHAR e CHAR, também com índices BTREE e HASH.

    Primeiro vamos criar nossas tabelas:

    CREATE TABLE seq_btree (id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY);
    CREATE TABLE seq_hash (id integer  GENERATED BY DEFAULT AS IDENTITY);
    CREATE INDEX ON seq_hash USING hash (id);
    CREATE TABLE uuid_btree (id uuid PRIMARY KEY);
    CREATE TABLE uuid_hash (id uuid );
    CREATE INDEX ON uuid_hash USING hash (id);
    CREATE TABLE uuid_char_btree (id char(36) PRIMARY KEY);
    CREATE TABLE uuid_char_hash (id varchar);
    CREATE INDEX ON uuid_char_hash USING hash (id);
    CREATE TABLE uuid_varchar_btree (id varchar PRIMARY KEY);
    CREATE TABLE uuid_varchar_hash (id varchar);
    CREATE INDEX ON uuid_varchar_hash USING hash (id);

    Notem que não existem índices UNIQUE do tipo HASH, logo ele não pode ser utilizado em uma PRIMARY KEY. Neste caso adicionamos o índice após a criação da tabela.

    INSERT

    Agora vamos inserir um milhão de registros em cada uma das tabelas utilizando a função nextval() nas sequencias e a função uuid_generate_v4() para gerar o UUID:

    INSERT INTO seq_btree SELECT nextval('seq_btree_id_seq') FROM generate_series(1::integer,1000000::integer);
    
    INSERT INTO  uuid_btree SELECT uuid_generate_v4() FROM generate_series(1,1000000);

    Tomei alguns cuidados antes de fazer a carga:

    • Aumentei o valor do WORK_MEM;
    • Rodei um CHECKPOINT antes de cada teste;
    • Realizei todos os teste num mesmo ambiente controlado, sem nada rodando em paralelo.

    Vejamos o tempo levado para a carga de cada tabela:

    seq_btree           Time: 11152.751 ms (00:11.153)
    seq_hash            Time: 11692.158 ms (00:11.692)
    uuid_btree          Time: 58822.429 ms (00:58.822)
    uuid_hash           Time: 55405.673 ms (00:55.406)
    uuid_char_btree     Time: 77064.257 ms (01:17.064)
    uuid_char_hash      Time: 57885.347 ms (00:57.885)
    uuid_varchar_btree  Time: 75555.282 ms (01:15.555)
    uuid_varchar_hash   Time: 57805.156 ms (00:57.805)
    

    Aqui vemos uma clara vantagem em se utilizar sequências! Elas são muito mais rápidas durante o INSERT. Depois vemos que os dados armazenados em UUID são mais rápidos que os armazenados em CHAR ou VARCHAR. Note também que nas tabelas que utilizam UUID, o índice HASH foi mais veloz que o índice BTREE. Para números sequenciais isso se inverte.

    Tamanho

    Além disso, vemos que o tamanho das tabelas muda, uma vez que uma sequencia ocupa menos espaço:

    seq_btree 35 MB
    seq_hash 35 MB
    uuid_btree 42 MB
    uuid_hash 42 MB
    uuid_char_btree 65 MB
    uuid_char_hash 65 MB
    uuid_varchar_btree 65 MB
    uuid_varchar_hash 65 MB
    

    Vejam que utilizar UUID é mais eficiente do que armazenar este tipo de dado em CHAR ou VARCHAR, daí o melhor desempenho na carga.

    Agora vejamos como fica o tamanho dos índices:

    seq_btree_pkey           seq_btree          21 MB
    seq_hash_id_idx          seq_hash           37 MB
    uuid_btree_pkey          uuid_btree         39 MB
    uuid_hash_id_idx         uuid_hash          36 MB
    uuid_char_btree_pkey     uuid_char_btree    73 MB
    uuid_char_hash_id_idx    uuid_char_hash     37 MB
    uuid_varchar_btree_pkey  uuid_varchar_btree 74 MB
    uuid_varchar_hash_id_idx uuid_varchar_hash  37 MB

    Aqui nós vemos como o índice do tipo HASH tem vantagens em dados aleatórios sobre o BTREE que é melhor em dados sequenciais.

    SELECT

    Outro ponto de vista importante é referente às estatísticas coletadas pelo ANALYZE. Olhando a tabela pg_stats, vemos algumas coisas interessantes:

         tablename      | avg_width | correlation
    --------------------+-----------+--------------
     seq_btree          |         4 |            1
     seq_hash           |         4 |            1
     uuid_btree         |        16 |   0.00269653
     uuid_hash          |        16 |   0.00022602
     uuid_char_btree    |        37 |   0.00133542
     uuid_char_hash     |        37 |   0.00420481
     uuid_varchar_btree |        37 |   0.0120523
     uuid_varchar_hash  |        37 |   0.0016304

    A primeira coisa que vemos é que o tamanho (avg_width) do campo é muito menor no caso de um INTEGER e muito pior no caso do VARCHAR e CHAR. depois notamos que a correlação (relação entre a distribuição dos dados e a ordem em que eles estão armazenados no disco) é favorável ao uso de sequencias, particularmente quando você quiser trazer vários registros baseados na ordenação.

    Vejamos como fica o desempenho das nossas chaves artificiais em operações de leitura. Primeiro vamos pegar um registro apenas de cada tabela e medir o tempo:

    SELECT * FROM seq_btree WHERE id = 100001;
    
    seq_btree           Time: 0.390 ms
    seq_hash            Time: 0.366 ms
    uuid_btree          Time: 0.407 ms
    uuid_hash           Time: 0.396 ms
    uuid_char_btree     Time: 0.494 ms
    uuid_char_hash      Time: 0.391 ms
    uuid_varchar_btree  Time: 0.440 ms
    uuid_varchar_hash   Time: 0.414 ms

    Vemos que a diferença aqui é pequena, com leve vantagem para índices do tipo HASH e para o uso de sequências. Agora vamos utilizar uma ordenação para trazer os dados:

    SELECT * FROM seq_btree ORDER BY id LIMIT 1 OFFSET 100000;
    
    seq_btree           Time: 26.792 ms
    seq_hash            Time: 217.644 ms
    uuid_btree          Time: 100.570 ms
    uuid_hash           Time: 507.426 ms
    uuid_char_btree     Time: 114.143 ms
    uuid_char_hash      Time: 3753.417 ms
    uuid_varchar_btree  Time: 109.001 ms
    uuid_varchar_hash   Time: 3815.933 ms

    Aqui a correlação faz muita diferença e os índices do tipo HASH são uma péssima escolha. Então se você tem o hábito de ordenar os dados baseado na sua chave artificial, as sequências tem uma vantagem enorme, e os índices HASH uma boa desvantagem.

    Conclusões

    Espero que tenha ficado claro que o uso de UUID tem custo sim e deve ser avaliado com cuidado. Então vejamos algumas considerações finais:

    • Não fiz testes com UPDATE e DELETE aqui, pois considerei que não relevantes para no uso de chaves artificiais.
    • No geral ele ocupa mais espaço (ainda mais se você fizer a besteira de utilizar CHAR ou VARCHAR)
    • UUID e é cerca de 5x mais lento em operações de INSERT
    • UUID e sequências tem desempenho semelhante em consultas simples do tipo “id = xxxx’, mas em consultas complexas as sequences são mais eficientes devido a correlação entre a sequência e o armazenamento físico;
    • Índices do tipo HASH são ligeiramente mais eficientes com UUID, mas não valem à pena quando se utiliza sequences. No entanto, vale lembrar que índices do tipo hash não podem ser utilizados como PK ou UNIQUE.

    Observação

    Você também pode utilizar o tipo de dados binário bytea para armazenar números hexadecimais com a mesma eficiência que o campo do tipo UUID, gravando dois números hexadecimais por byte, enquanto o CHAR e VARCHAR utilizam pelo menos um byte por caractere. A diferença está na validação e formatação de entrada e saída que fica mais simples no UUID.

  • Chaves artificiais no PostgreSQL

    Criar chaves artificiais virou um vício com o advento das ferramentas de ORM. Mesmo antes delas, as chaves artificiais já existiam e temos bons motivos (e maus motivos também) para adota-las. Existem várias formas de criar uma chave artificial. No PostgreSQL temos duas bastante utilizadas. Uma utiliza as sequências, para gerar números sequenciais. Note que não há garantia de que a sequência não ganhe buracos no caminho. Outra utiliza números aleatórios que nunca se repetem, o famoso UUID. O uso de sequências é bastante conhecido e difundido, mas o UUID é útil em sistemas distribuídos onde você pode gerar um número para sua chave artificial em diversos nós distintos. Se você utilizar uma sequencia comum, você não poderá garantir que dois nós não utilizem o mesmo número, perdendo assim a unicidade da sua chave. Existem sequências distribuídas também, até a versão 10 do PostgreSQL ela não foi implementada ainda. O Oracle utiliza sequencias distribuías no Oracle RAC onde cada nó guarda no cache números diferentes para os próximos valores da sequência. No MySQL, é comum criar sequências com um salto entre cada número, onde cada nó possui um valor inicial diferente, assim com 3 nós por exemplo, o nó um teria números sequenciais como 1, 4, 7, 10… o nó 2 teria números 2, 5, 8, 11… e o nó 3 teria números 3, 6, 9, 12…

    UUID

    UUID é um acrônimo de “Universally unique identifier” ou identificador único universal. O PostgreSQL tem suporte nativo para este tipo de dado e funções para gerar localmente estes números, sem precisar da aplicação para isso. Mas estas funções não são nativas no core. Por sorte estão num módulo do contrib (sempre recomendo instalar todos os módulos do contrib e depois criar as extensões conforme a necessidade). Vejamos um exemplo simples de sua utilização:

    postgres=# CREATE TABLE uuid_teste (id uuid, nome varchar);
    CREATE TABLE
    
    postgres=# \d uuid_teste
    Tabela "public.uuid_teste"
    Coluna | Tipo | Modificadores
    -------+-------------------+---------------
    id     | uuid              |
    nome   | character varying |
    
    postgres=# CREATE EXTENSION "uuid-ossp";
    CREATE EXTENSION
    
    postgres=# INSERT INTO uuid_teste VALUES (uuid_generate_v4(), 'Fábio');
    INSERT 0 1
    postgres=# INSERT INTO uuid_teste VALUES (uuid_generate_v4(), 'Paulo');
    INSERT 0 1
    postgres=# INSERT INTO uuid_teste VALUES (uuid_generate_v4(), 'Maria');
    INSERT 0 1
    
    postgres=# SELECT * FROM uuid_teste ;
    id | nome
    --------------------------------------+-------
    8feca1d8-8461-4df1-8951-7a3b0abfcd56  | Fábio
    68bd6900-8c5f-4852-a124-b881dcf2edf5  | Paulo
    179fc357-17c4-449f-8d7c-03a1e922d6f1  | Maria
    (3 registros)

    Note que não existe um objeto como uma SEQUENCE aqui, apenas o tipo de dados uuid e a função uuid_generate_v4() para gerar os números aleatórios (que nunca se repetem). Você também pode automatizar um pouco as coisas e usar a função uuid_generate_v4() como valor padrão para nossa coluna id:

     

    postgres=# ALTER TABLE uuid_teste ALTER COLUMN id SET DEFAULT uuid_generate_v4();
    ALTER TABLE
    
    postgres=# \d uuid_teste
    Tabela "public.uuid_teste"
    Coluna  | Tipo              | Modificadores
    --------+-------------------+------------------------------------
    id      | uuid              | valor padrão de uuid_generate_v4()
    nome    | character varying |
    
    postgres=# INSERT INTO uuid_teste (id, nome) VALUES (DEFAULT, 'Pedro');
    INSERT 0 1
    postgres=# INSERT INTO uuid_teste (id, nome) VALUES (DEFAULT, 'Ana');
    INSERT 0 1
    
    postgres=# INSERT INTO uuid_teste (nome) VALUES ('José');
    INSERT 0 1
    postgres=# INSERT INTO uuid_teste (nome) VALUES ('Joana');
    INSERT 0 1
    
    postgres=# SELECT * FROM uuid_teste ;
    id | nome
    --------------------------------------+-------
    8feca1d8-8461-4df1-8951-7a3b0abfcd56 | Fábio
    68bd6900-8c5f-4852-a124-b881dcf2edf5 | Paulo
    179fc357-17c4-449f-8d7c-03a1e922d6f1 | Maria
    51f88f73-9cc1-4fdb-be4a-bd001d7bc934 | Pedro
    05e96034-369f-4f42-b458-3ba872d55fc6 | Ana
    f9807337-fab0-4913-990b-8cc2b168bb04 | José
    d6ac1694-91de-44d1-8087-6bed2207b228 | Joana
    (7 registros)

     

    Note que utilizei duas sintaxes ligeiramente diferentes para obter o mesmo resultado no INSERT.

    Por fim, existem diversos algorítimos diferentes para gerar o número aleatório do UUID, o pacote uuid-ossp tem 5 diferentes funções para gera-lo. Leia a documentação deste módulo e escolha a mais apropriada antes de começar a utilizar o UUID.

    SEQUENCE

    As sequencias existem como um objeto independente no banco de dados. Ao cria-las você pode definir algumas propriedades particulares, como número inicial e final, saltos, etc:

    postgres=# \h CREATE SEQUENCE
    Comando: CREATE SEQUENCE
    Descrição: define um novo gerador de sequência
    Sintaxe:
    CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] nome [ INCREMENT [ BY ] incremento ]
    [ MINVALUE valor_mínimo | NO MINVALUE ] [ MAXVALUE valor_máximo | NO MAXVALUE ]
    [ START [ WITH ] início ] [ CACHE cache ] [ [ NO ] CYCLE ]
    [ OWNED BY { nome_tabela.nome_coluna | NONE } ]

    A partir da versão 10, ficou mais fácil acompanhar a vida da

    test=# CREATE SEQUENCE s;
    CREATE SEQUENCE
    
    test=# SELECT nextval('s');
    nextval
    ---------
    2
    (1 row)
    
    test=# SELECT nextval('s');
    nextval
    ---------
    3
    (1 row)
    
    test=# SELECT * FROM pg_sequences;
    schemaname | sequencename | sequenceowner | data_type | start_value | min_value | max_value           | increment_by | cycle | cache_size | last_value
    -----------+--------------+---------------+-----------+-------------+-----------+---------------------+--------------+-------+------------+------------
    public     | s            | postgres      | bigint    | 1           | 1         | 9223372036854775807 | 1            | f     | 1          | 3
    (1 row)

    A view pg_sequences é nova no PostgreSQL 10 e ajuda bastante a verificar o status da sua sequencia. Note que o valor da coluna last_value, reflete aqui o último valor gerado.

    Algumas aplicações utilizam a função nextval exatamente da forma como mostrada e só depois realizam um INSERT em uma tabela. Mas assim como no caso do UUID, você pode fazer isso de forma mais direta:

    test=# CREATE table seq_teste(id bigint, nome varchar);
    CREATE TABLE
    
    test=# \d seq_teste
    Table "public.seq_teste"
    Column | Type               | Collation | Nullable | Default
    -------+--------------------+-----------+----------+---------
    id     | bigint             |           |          |
    nome   | character varying  |           |          |
    
    test=# INSERT INTO seq_teste (id, nome) VALUES (nextval('s'), 'Fábio');
    INSERT 0 1
    test=# INSERT INTO seq_teste (id, nome) VALUES (nextval('s'), 'Ana');
    INSERT 0 1
    test=# SELECT * FROM seq_teste ;
    id  | nome
    ----+-------
    4   | Fábio
    5   | Ana
    (2 rows)
    

    Você pode também ter uma segunda tabela utilizando a mesma sequência:

    test=# CREATE TABLE seq_teste2 (id bigint, idade smallint);
    LOG: statement: CREATE TABLE seq_teste2 (id bigint, idade smallint);
    CREATE TABLE
    test=# INSERT INTO seq_teste2 (id, idade) VALUES (nextval('s'), 18);
    INSERT 0 1
    test=# INSERT INTO seq_teste2 (id, idade) VALUES (nextval('s'), 36);
    INSERT 0 1
    
    test=# SELECT * FROM seq_teste2;
    id | idade
    ----+-------
    6 | 18
    7 | 36
    (2 rows)

    Muitas pessoas preferem pegar o próximo número da sequência antes do INSERT por um simples motivo: elas vão utilizar esta numeração em outros lugares depois. Da forma que fizemos, para saber qual foi o número gerado pela nossa sequência ‘s’, temos que fazer um SELECT depois do INSERT. é claro que existe um jeito mais prático de fazer isso, utilizando a cláusula RETURNING existente nos comandos de DML INSERT, UPDATE e DELETE:

    test=# INSERT INTO seq_teste (id, nome) VALUES (nextval('s'), 'Hugo') RETURNING id;
    id
    ----
    8
    (1 row)
    
    INSERT 0 1
    test=# INSERT INTO seq_teste (id, nome) VALUES (nextval('s'), 'Priscila') RETURNING id;
    id
    ----
    9
    (1 row)
    
    INSERT 0 1

    Vejam que o seu INSERT assume um comportamento parecido com um SELECT, uma vez que ele retorna um valor para você.

    Você também pode colocar o valor da sua sequência como valor padrão para a sua coluna na tabela, assim como fizemos com o UUID:

    test=# ALTER TABLE seq_teste ALTER COLUMN id SET DEFAULT nextval('s');
    LOG: statement: ALTER TABLE seq_teste ALTER COLUMN id SET DEFAULT nextval('s');
    ALTER TABLE
    
    test=# \d seq_teste
    Table "public.seq_teste"
    Column | Type              | Collation | Nullable | Default
    -------+-------------------+-----------+----------+------------------------
    id     | bigint            |           |          | nextval('s'::regclass)
    nome   | character varying |           |          |
    
    test=# INSERT INTO seq_teste (nome) VALUES ('Patrícia') RETURNING id;
    id
    ----
    10
    (1 row)
    
    INSERT 0 1
    test=# INSERT INTO seq_teste (nome) VALUES ('Denis') RETURNING id;
    id
    ----
    11
    (1 row)
    
    INSERT 0 1

    Um detalhe extra sobre a sequência é que ela pode ter um “dono”. Você pode definir que um determinado campo de uma tabela seja o dono desta sequência. A única vantagem de fazer isso é que se em algum momento a sua coluna (ou a tabela inteira, claro) for excluída, a sequência será excluída junto:

    test=# ALTER SEQUENCE s OWNED BY seq_teste.id;
    LOG: statement: ALTER SEQUENCE s OWNED BY seq_teste.id;
    ALTER SEQUENCE
    
    test=# \d s
    Sequence "public.s"
    Type | Start | Minimum | Maximum | Increment | Cycles? | Cache
    --------+-------+---------+---------------------+-----------+---------+-------
    bigint | 1 | 1 | 9223372036854775807 | 1 | no | 1
    Owned by: public.seq_teste.id
    
    test=# DROP TABLE seq_teste;
    LOG: statement: DROP TABLE seq_teste;
    DROP TABLE
    test=# \d s
    Did not find any relation named "s".

    De fato, estas 3 operações são muito comuns:

    • Criar uma sequência
    • Definir a sequência como valor padrão de uma coluna de uma tabela
    • Definir a sequência como pertencente à coluna desta tabela

    Para facilitar a vida, o Postgres possui os tipos de dados serial, bigserial e smallserial que criam respectivamente campos do tipo integer, bigint e smallint com uma sequence associada utilizando estes 3 passos implicitamente:

    test=# CREATE TABLE seq_teste (id bigserial, nome varchar);
    LOG: statement: CREATE TABLE seq_teste (id bigserial, nome varchar);
    CREATE TABLE
    test=# \d seq_teste
    Table "public.seq_teste"
    Column | Type | Collation | Nullable | Default
    --------+-------------------+-----------+----------+---------------------------------------
    id | bigint | | not null | nextval('seq_teste_id_seq'::regclass)
    nome | character varying | | |

     

    Note que estes tipos de dados são apenas atalhos, e não tipos de dados reais. Os tipos de dados na tabela continuam sendo do tipo integer, bigint e smallint. Muitas pessoas que utilizam o serial não percebem as 3 operações criadas implicitamente e sequer imaginam que existe uma sequência sendo utilizada nos bastidores.

    Colisão

    O grande problema com as sequências é que embora você possa utilizar uma única sequência para várias tabelas, uma chave artificial só pode utilizar valores gerados por uma única sequência. Se você resolver inserir dados literais na sua tabela, o valor da sequência não vai ser incrementado e você terá uma colisão:

    test=# ALTER TABLE seq_teste ADD PRIMARY KEY (id);
    LOG: statement: ALTER TABLE seq_teste ADD PRIMARY KEY (id);
    ALTER TABLE
    test=# INSERT INTO seq_teste (nome) VALUES ('Fábio');
    INSERT 0 1
    test=# INSERT INTO seq_teste (nome) VALUES ('Paulo');
    INSERT 0 1
    test=# INSERT INTO seq_teste (nome) VALUES ('Cícero');
    INSERT 0 1
    test=# SELECT * FROM seq_teste;
     id | nome
    ----+--------
     1  | Fábio
     2  | Paulo
     3  | Cícero
    (3 rows)
    
    test=# INSERT INTO seq_teste (id, nome) VALUES (4, 'Paula');
    INSERT 0 1
    test=# INSERT INTO seq_teste (id, nome) VALUES (5, 'Márcia');
    INSERT 0 1
    test=# INSERT INTO seq_teste (id, nome) VALUES (6, 'Joana');
    INSERT 0 1
    
    test=# SELECT * FROM seq_teste;
     id | nome
    ----+--------
     1  | Fábio
     2  | Paulo
     3  | Cícero
     4  | Paula
     5  | Márcia
     6  | Joana
    (6 rows)
    test=# INSERT INTO seq_teste (nome) VALUES ('Joaquim');
    ERROR: duplicate key value violates unique constraint "seq_teste_pkey"
    DETAIL: Key (id)=(4) already exists.

    Ou seja, depois de inserir os IDs 4, 5 e 6 manualmente, a sequência continuou no valor 4. Ao tentar inserir utilizando a sequencia novamente ela duplicou a chave primária e deu erro. Para concertar isso, vamos ter que alterar o valor do próximo número da sequência, utilizando a função setval:

    test=# SELECT setval('seq_teste_id_seq',6);
    setval
    --------
    6
    (1 row)
    
    test=# INSERT INTO seq_teste (nome) VALUES ('Joaquim') RETURNING id;
    id
    ----
    7
    (1 row)

     

    Um detalhe aqui: o nome da sequência agora é seq_teste_id_seq, que é o nome gerado internamente pelo Postgres quando você utilizou o serial na criação da tabela.

    Além do problema da possível colisão, existem alguns detalhes adicionais que podem complicar a vida de quem usa sequências para alimentar chaves artificiais:

    • Se você criar uma tabela com o CREATE TABLE … LIKE, a nova tabela vai apontar para a mesma sequência da tabela original e não criar uma nova;
    • Quando você remove o valor padrão que referencia uma sequência, a sequência em si não é removida da base;
    • Você tem permissões (GRANT, REVOKE) adicionais para a sequência, além das permissões na tabela;
    • Se você quiser apagar uma sequência utilizada como valor padrão de uma tabela, você precisa utilizar o DROP SEQUENCE … CASCADE;

    Para mais detalhes sobre estes problemas e a comparação com o uso das novas IDENTITY COLUMNS veja o artigo do Sr. Peter Eisentraut sobre o assunto.

    IDENTITY COLUMNS

    O padrão SQL:2003 criou um padrão novo para lidar com sequências em chaves artificiais. O postgreSQL passou a implementar este padrão no PostgreSQL 10, embora continue suportando o uso de campos do tipo serial, mantendo a compatibilidade com o legado. No lugar da cláusula DEFAULT de uma coluna, você pode utilizar a seguinte sintaxe:

    GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ]

    Desta forma você tem que escolher entre as opções ALWAYS ou BY DEFAULT e pode adicionar opcionalmente algumas opções para a sequência que será criada implicitamente. A diferença entre o ALWAYS e o BY DEFAULT é justamente a possibilidade de colisão durante o INSERT. Se você utilizar o BY DEFAULT, terá um comportamento mais parecido com o tipo de dados serial do Postgres, ou seja, qualquer INSERT pode escolher valores fora da sequência:

    test=# CREATE TABLE teste_by_default (id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, nome varchar);
    LOG: statement: CREATE TABLE teste_by_default (id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, nome varchar);
    CREATE TABLE
    test=# \d teste_by_default
     Table "public.teste_by_default"
     Column | Type              | Collation | Nullable | Default
    --------+-------------------+-----------+----------+----------------------------------
     id     | integer           |           | not null | generated by default as identity
     nome   | character varying |           |          |
    Indexes:
     "teste_by_default_pkey" PRIMARY KEY, btree (id)
    test=# \d teste_by_default_id_seq
     Sequence "public.teste_by_default_id_seq"
     Type    | Start | Minimum | Maximum    | Increment | Cycles? | Cache
    ---------+-------+---------+------------+-----------+---------+-------
     integer | 1     | 1       | 2147483647 | 1         | no      | 1
    Sequence for identity column: public.teste_by_default.id
    test=# INSERT INTO teste_by_default (nome) VALUES ('Fábio');
    INSERT 0 1
    test=# INSERT INTO teste_by_default (nome) VALUES ('Paulo');
    INSERT 0 1
    test=# INSERT INTO teste_by_default (nome) VALUES ('João');
    INSERT 0 1
    test=# INSERT INTO teste_by_default (id, nome) VALUES (4, 'Pedro');
    INSERT 0 1
    test=# INSERT INTO teste_by_default (id, nome) VALUES (5, 'José');
    INSERT 0 1
    test=# INSERT INTO teste_by_default (nome) VALUES ( 'Rita');
    ERROR: duplicate key value violates unique constraint "teste_by_default_pkey"
    DETAIL: Key (id)=(4) already exists.

    Aqui vemos que o valor padrão da coluna id da tabela é referenciado como “generated by default as identity” e não aparece o nome da sequência. Já na sequência aparece a referência “Sequence for identity column: public.teste_by_default.id”

    Agora veremos que utilizando a opção ALWAYS, não é possível fazer a mesma coisa:

    test=# CREATE TABLE teste_always (id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, nome varchar);
    LOG: statement: CREATE TABLE teste_always (id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, nome varchar);
    CREATE TABLE
    test=# \d teste_always
    Table "public.teste_always"
    Column | Type              | Collation | Nullable | Default
    -------+-------------------+-----------+----------+------------------------------
    id     | integer           |           | not null | generated always as identity
    nome   | character varying |           |          |
    Indexes:
    "teste_always_pkey" PRIMARY KEY, btree (id)
    
    test=# \d teste_always_id_seq
    Sequence "public.teste_always_id_seq"
    Type     | Start | Minimum | Maximum | Increment | Cycles? | Cache
    ---------+-------+---------+------------+-----------+---------+-------
    integer  | 1     | 1 | 2147483647 | 1 | no | 1
    Sequence for identity column: public.teste_always.id
    
    test=# INSERT INTO teste_always (nome) VALUES ('Fábio');
    INSERT 0 1
    test=# INSERT INTO teste_always (nome) VALUES ('Paulo');
    INSERT 0 1
    test=# INSERT INTO teste_always (nome) VALUES ('João');
    INSERT 0 1
    test=# INSERT INTO teste_always (id, nome) VALUES (4,'Pedro');
    ERROR: cannot insert into column "id"
    DETAIL: Column "id" is an identity column defined as GENERATED ALWAYS.
    HINT: Use OVERRIDING SYSTEM VALUE to override.

    Agora o valor padrão da coluna aparece como “generated always as identity” e quando tentamos atribuir um valor arbitrário para a coluna id recebemos um erro. Mas logo abaixo vem uma dica de como podemos burlar isso, usando o OVERRIDING SYSTEM VALUE.

    OVERRIDING VALUE

    Existem duas opções para esta cláusula OVERRIDING SYSTEM VALUE e OVERRRIDING USER VALUE:

    test=# INSERT INTO teste_always (id, nome) OVERRIDING SYSTEM VALUE VALUES (4, 'Pedro');
    INSERT 0 1
    test=# INSERT INTO teste_always (id, nome) OVERRIDING SYSTEM VALUE VALUES (5, 'José');
    INSERT 0 1
    test=# INSERT INTO teste_always (nome) VALUES ('Rita');
    ERROR: duplicate key value violates unique constraint "teste_always_pkey"
    DETAIL: Key (id)=(4) already exists.

    Ou seja, você pode sim utilizar um valor arbitrário seu, mas se o fizer, tem que tomar cuidado com o que está fazendo! O padrão SQL exige que você coloque a cláusula OVERRIDING SYSTEM VALUE, como um alerta para que você tenha certeza do que está fazendo.

    Existe também a cláusula OVERRIDING USER VALUE, que pode ser utilizada numa situação onde você quer ignorar os valores arbitrários e utilizar apenas os valores da sequencia:

    test=# INSERT INTO teste_by_default (id, nome) VALUES (42, 'Ana');
    INSERT 0 1
    test=# INSERT INTO teste_by_default (id, nome) VALUES (36, 'Paula');
    INSERT 0 1
    test=# INSERT INTO teste_by_default (id, nome) VALUES (111, 'Priscila');
    INSERT 0 1
    test=# SELECT * FROM teste_by_default;
    id  | nome
    ----+----------
    1   | Fábio
    2   | Paulo
    3   | João
    4   | Pedro
    5   | José
    42  | Ana
    36  | Paula
    111 | Priscila
    (8 rows)
    
    test=# CREATE TABLE teste_by_default2 (id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, nome varchar);
    LOG: statement: CREATE TABLE teste_by_default2 (id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, nome varchar);
    CREATE TABLE
    test=# INSERT INTO teste_by_default2 (id, nome) OVERRIDING USER VALUE SELECT * FROM teste_by_default;
    INSERT 0 8
    
    test=# INSERT INTO teste_by_default2 (id, nome) OVERRIDING USER VALUE VALUES (4, 'Maria');
    INSERT 0 1
    test=# INSERT INTO teste_by_default2 (id, nome) OVERRIDING USER VALUE VALUES (6, 'Luiza');
    INSERT 0 1
    test=# SELECT * FROM teste_by_default2;
    id | nome
    ---+----------
    1  | Fábio
    2  | Paulo
    3  | João
    4  | Pedro
    5  | José
    6  | Ana
    7  | Paula
    8  | Priscila
    9  | Maria
    10 | Luiza
    (10 rows)

    Veja que toda a sequência da tabela teste_by_default2 ignora os valores que vieram da tabela teste_by_default ou mesmo os inseridos com valores arbitrários.

    Um detalhe: você só pode utilizar esta opção em colunas definidas com o GENERATED BY DEFAULT, em colunas GENERATED ALWAYS você obtém o seguinte erro:

    test=# INSERT INTO teste_always (id, nome) OVERRIDING USER VALUE VALUES (6, 'Luiza');
    ERROR: cannot insert into column "id"
    DETAIL: Column "id" is an identity column defined as GENERATED ALWAYS.
    HINT: Use OVERRIDING SYSTEM VALUE to override.

    Por último, você tem a opção de passar algumas opções para a sequência que será gerada implicitamente:

    test=# CREATE TABLE teste_seq_opts (id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (INCREMENT BY 2 START WITH 100), nome varchar);
    LOG: statement: CREATE TABLE teste_seq_opts (id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (INCREMENT BY 2 START WITH 100), nome varchar);
    CREATE TABLE
    test=# \d teste_seq_opts;
    Table "public.teste_seq_opts"
    Column | Type              | Collation | Nullable | Default
    -------+-------------------+-----------+----------+------------------------------
    id     | integer           |           | not null | generated always as identity
    nome   | character varying |           |          |
    Indexes:
    "teste_seq_opts_pkey" PRIMARY KEY, btree (id)
    
    test=# \d teste_seq_opts_id_seq
    Sequence "public.teste_seq_opts_id_seq"
    Type    | Start | Minimum | Maximum    | Increment | Cycles? | Cache
    --------+-------+---------+------------+-----------+---------+-------
    integer | 100   | 1       | 2147483647 | 2         | no      | 1
    Sequence for identity column: public.teste_seq_opts.id
    
    test=# INSERT INTO teste_seq_opts (nome) VALUES ('Fábio');
    INSERT 0 1
    test=# INSERT INTO teste_seq_opts (nome) VALUES ('Telles');
    INSERT 0 1
    test=# INSERT INTO teste_seq_opts (nome) VALUES ('Rodriguez');
    INSERT 0 1
    test=# SELECT * FROM teste_seq_opts;
    id  | nome
    ----+-----------
    100 | Fábio
    102 | Telles
    104 | Rodriguez
    (3 rows)

    Você não pode especificar o nome da sequencia que será criada. Seja utilizando o serial ou o IDENTITY, o nome da sequência sempre terá o formato <nome_da_tabela>_<nome da coluna>_seq.

     

    Bom, recomendo que você faça alguns testes com chaves artificiais e se já estiver utilizando o PostgreSQL 10, passe a adotar o IDENTITY COLUMN ao invés do serial. Além deste formato ser mais robusto e apresentar menos problemas, ele também é padrão SQL, o que ajuda na portabilidade do seu código. Qualquer dúvida, deixe um comentário aqui.

    Continuação…

    Publiquei outro artigo com ênfase no desempenho de chaves artificiais utilizando sequencias e UUID, confira!

  • Um pouco mais sobre o PGConf Brasil 2018

    O PGConf Brasil 2018 já está todo planejado e estamos preparando os últimos detalhes para colocar o bloco na rua:

    • O evento será nos dias 3 e 4 de agosto de 2018;
    • O local é o mesmo do PGBR2011: Hotel Century Paulista em São Paulo, muito bem localizado entre a estação Brigadeiro do metrô (na Av. Paulista) e o Parque Ibirapuera;
    • Para quem vier de fora de São Paulo, conseguimos preços especiais para quem vier no evento, com quartos para uma, duas e três pessoas;
    • Já começamos a convidar os primeiros palestrantes internacionais;
    • O Media Kit (A.K.A. Plano de Captação) já está pronto e já estamos procurando patrocinadores para o evento (se você quiser patrocinar, deixe um comentário aqui que entraremos em contato!);
    • A chamada de trabalhos e as inscrições devem começar oficialmente nesta 6ª feira, 1/12/17;
    • As inscrições antecipadas também já vão começar no dia 1/12/17. Para quem se inscrever antes, estamos preparando um brinde especial e exclusivo para aqueles que forem nossos “early birds”. As inscrições serão feitas no Eventize, com direito a pagamento com boleto, cartão de crédito, parcelamento etc e tal, tudo on-line;
    • Também colocamos como opções nas inscrições adquirir a caneca e camiseta oficial do evento por um preço promocional: R$ 50,00 pelo kit;
    • Outra opção será participar do almoço VIP junto com os palestrantes no próprio restaurante dentro do hotel do evento, com comida e bebida (não alcoólica) à vontade, por R$ 100. Esta opção tem quantidade limitada devido à capacidade de atendimento do restaurante do hotel;
    • O espaço físico já está reservado:
      • Uma sala principal com 230 lugares
      • Uma sala com 120 lugares
      • Deixaremos de prontidão uma sala extra de 120 lugares, caso as inscrições ultrapassem 300 pessoas;
      • Uma sala VIP para palestrantes e Couch Clinic com 12 lugares;
      • Salão para credenciamento, de exposições de stands de patrocinadores e coffee breack.
    • Na grade deixamos espaço para:
      • Palestra de pelo menos 2 Key notes internacionais de 90 minutos;
      • Pelo menos 2 tutoriais de 120 minutos;
      • 18 palestras de 60 minutos;
      • 12 lightning talks de 5 minutos;
      • 1 FishBowl;
      • 4 Coffe Breaks;
      • Abertura e encerramento oficial;

    Até o dia 1/12 estarei publicando detalhes sobre a chamada de trabalhos e as inscrições, até breve!

  • PGConf Brasil 2018, lançado!

    O primeiro evento de Software Livre que eu organizei foi em 2003, no ABCD paulista. Depois vieram em 2004, 2005 e 2006 as 3 edições do Fórum Regional de Software Livre do ABCD.

    Em 2005 e 2006 juntamos uma turma de pessoas interessadas em PostgreSQL nos eventos de software livre como o FISL, CONISLI. Começou com uma palestra aqui e ali. Logo ficou claro que estava na hora de fazer nosso próprio evento. Em 2007 montamos nosso primeiro stand no FISL e no final do ano organizamos o PGCon Brasil 2007 em São Paulo. Nesta primeira edição contamos com a “Tempo Real Eventos” que organizou toda a infra do evento.

    Em 2008 e 2009 fomos até a UNICAMP em Campinas / SP e fizemos dois grandes eventos, com as próprias pernas. Em 2011 o evento muda de PGCon para PGBR, devido a uma solicitação do organizador do PGCon no Canadá. Fizemos o PGBR 2011 em São Paulo / SP, a primeira feita num hotel. Este foi o último grande evento em que eu encabecei a organização.

    Em 2013 o Luis Fernando Bueno foi o responsável pela organização do PGBR 2013 em Porto Velho / RO. Em 2015 o Fabrízio de Royes Mello assumiu a organização do PGBR 2015 em Porto Alegre / RS e em 2017 o Sebastian Webber foi o Kahuna do PGBR 2017.

    Para que fosse possível organizar os eventos de forma independente nós precisávamos de um CNPJ e uma conta bancária para poder receber patrocínios, contratar fornecedores e receber o valor das inscrições. Para isso contamos com o apoio da ASL que organiza o FISL. A comunidade brasileira de PostgreSQL em si nunca teve uma entidade legalmente constituída. Nunca houve um presidente ou uma autoridade que pudesse falar em nome da comunidade. Houveram apenas pessoas que toparam o desafio de fazer acontecer.

    A comunidade mudou muito de 2016 para cá. Hoje não existe mais aquela empolgação de que o Software Livre iria mudar o mundo. Por outro lado, conhecemos muita gente bacana, disposta a ajudar outras pessoas, trouxemos palestrantes dos EUA, Japão, Argentina, Suécia, Rússia, Equador, Bolívia, Espanha, Chile, Inglaterra e devo estar esquecendo de mais alguns. Isso nos ajuda a arejar um bocado a cabeça. Fizemos grandes amigos, aliás, alguns deles viraram colegas de trabalho.

    Depois do sucesso do PGBR 2017, estava claro que o modelo atual não seria o mais adequado para nós. Os tempos mudaram. Então resolvemos assumir a organização do evento como Timbira, sem a ASL. Isso diminui um bocado a burocracia da organização e nos dá um pouco mais de flexibilidade. Fazer as coisas de um jeito mais eficiente é fundamental para que a organização do evento não tome todo o nosso tempo. Por outro lado, para que tudo dê certo (no final sempre dá… mas nem todos veem o esforço para isso virar por trás das cortinas) resolvemos começar agora.

    Vamos utilizar o nome PGConf, como a maioria das conferências sobre PostgreSQL tem feito nos outros países. O site do evento já está no ar e uma página no Facebook também. Aproveitamos ainda para lançar uma pesquisa para ver quais palestrantes internacionais as pessoas gostariam de ver no evento. Então anote aí:

    Até o final de novembro deveremos iniciando a captação de recursos, abrindo a chamada de trabalhos e as inscrições antecipadas para o evento, então logo teremos novidades.

     

  • Um pouco sobre a história dos bancos de dados – Parte III

     

    Se não está na Internet, então não existe!

    A nova era do NoSQL

    O crescimento da Internet mudou tudo na informática. Aplicações relacionadas às redes sociais como Twitter e Facebook trouxeram novos desafios e o crescente uso de smartphones criou uma explosão de dados. Enquanto os bancos de dados relacionais estavam orgulhosos dos seus Datawarehouses, com seus relatórios complexos, OLAP, DataMining, VLDB, ETLs e por aí vai, surge a era do Big Data. Novos desafios numa escala sem precedentes surgem. Os bancos de dados relacionais nasceram com o conceito de ACID arraigado em suas premissas. Porém imagine que você administra o banco de dados de uma rede social como o Twitter. O que é mais importante para você, a disponibilidade  do serviço com bom desempenho ou a consistência dos dados? Neste cenário perder a informação de alguns tweets não é tão importante quanto manter o serviço no ar. Assim surge o teorema CAP, onde uma nova geração de bancos de dados abre mão da consistência de dados em nome da escalabilidade horizontal, um ponto fraco nos bancos de dados relacionais.Outro ponto interessante entre os bancos de dados NoSQL é a predominância esmagadora de soluções livres, enquanto entre os banco de dados relacionais, as versões proprietárias ainda dominam. Existem vários tipos de bancos de dados NoSQL (conhecidos como “No SQL” e depois como “Not Only SQL”): chave-valor, orientados a documentos, orientados a grafos, orientados a eventos, temporais, XML, etc. Comentarei aqui apenas os 3 mais utilizados atualmente.

    Bancos de dados Chave-Valor

    Foram criados dezenas de tipos de bancos de dados chave-valor, a maioria com persistência apenas em memória. Bancos de dados chave-valor tem um desempenho absurdo, pois não dependem da persistência em discos e nem controles de transações. Também podem se espalhar por dezenas de servidores de forma transparente. São muito eficientes para problemas de baixa complexidade. O Memcached, criado em 2003 foi o primeiro a se tornar popular e foi muito utilizado como cache de sites na internet. Atualmente o Redis é banco de dados do tipo chave-valor mais popular do mercado. Lançado em 2009, ele é também o 9º banco de dados mais popular no placar geral.

    Bancos de dados orientados a documentos

    Nos anos 2000, o XML ganha ampla aceitação no mercado e muitos bancos de dados implementam extensões para armazenar e manipular dados em XML. Em 2006 surge o uma especificação no padrão SQL:2006 para o armazenamento de XML. Novamente tivemos também bancos de dados especializados em manipular XML e até implementações do XQuery que se tornou uma recomendação do W3C em 2007.

    Lançado em 2009, o MongoDB é o banco de dados orientado a documentos mais bem sucedido hoje. É o 5º mais popular entre todos os bancos de dados, logo depois do PostgreSQL.  Ele armazena dados no formato JSON, formato que ganhou o mercado nos anos 2010 na internet. Em 2016, o PostgreSQL lança a primeira implementação em bancos de dados relacionais eficiente para armazenar JSON. Outros bancos de dados também se tornaram capazes de armazenar dados no formato JSON, sendo lançado uma padronização no SQL:2016 onde o Oracle é o banco de dados mais aderente ao padrão ISO.

    Bancos de dados orientados a grafos

    Este tipo de banco de dados, resolve problemas que são particularmente chatos em bases relacionais: consultas hierárquicas. A estrutura de grafos permite modelar os dados em termos de relacionamentos mais naturais e flexíveis, resolvendo problemas complexos de forma muito mais simples em comparação com os bancos relacionais. Nesta categoria, o Neo4j é o mais popular, figurando na posição 21 no ranking geral.

    A geração DevOps e a nuvem

    A computação em nuvem ganhou o mundo e muitos bancos de dados que ficavam trancafiados nos CPDs passaram a flutuar por aí. Primeiro vieram as VMs, e por fim os serviços de provisionamento na nuvem com o PaaS, IaaS, SaaS, Pizza as a Service e por aí vai. Na nuvem, as coisas são voláteis. Uma das coisas mais complicadas para os bancos de dados tradicionais é garantir um bom desempenho em disco, o que é um problema complexo quando os discos são compartilhados na nuvem. Além disso, alguns bancos de dados tem uma relação muito íntima com o hardware. Na nuvem, este acoplamento entre hardware e software não faz muito sentido e você tem que trabalhar de outra forma para conseguir aproveitar as vantagens da nuvem sem drenar todo o seu orçamento.

    Uma das soluções para utilizar melhor a nuvem é ter um provisionamento ágil, quebrar a sua aplicação enorme em vários pedaços menores, cada uma com seu próprio banco de dados. É o que se chama de microserviços. Gerenciar uma estrutura onde temos vários deploys acontecendo na produção diariamente, exige uma nova forma de trabalho, e é aí que surge a cultura DevOps, na qual a automação, a integração entre desenvolvimento, DBAs e sysadmins vira uma constante.

    Outra característica comum nos dias de hoje é a presença de bancos de dados de vários tipos ao lado do tradicional banco de dados relacional. O banco de dados central e monolítico perdeu espaço para opções mais simples e especializadas para cada situação.

    Conclusões

    O trabalho de pesquisar sobre a história dos bancos de dados nos traz algumas informações interessantes:

    • Ser melhor não significa ser maior. Algumas vezes tecnologias melhores fazem menos sucesso pelas condições em que são lançadas. O QUEL poderia ser melhor que o SQL, mas o poder da IBM em criar um padrão se mostrou forte, assim como o lançamento do IBM-PC no começo da década de 80 que criou o padrão dos microcomputadores para o mercado;
    • Seguir um padrão realmente importa. O padrão SQL teve um peso muito importante no mercado de bancos de dados. Mesmo numa era NoSQL, o padrão SQL continua sendo muito relevante, mesmo depois de quase 50 anos de existência. Os bancos de dados orientados a objetos não tinham um padrão e uma teoria bem definida. Havia um frisson no seu lançamento de que os bancos de dados relacionais iriam desaparecer com o sucesso dos bancos de dados orientados à objeto;
    • Novas demandas requerem novas soluções. O MySQL foi o banco de dados que surfou nesta onda e se espalhou por toda a internet, mesmo sendo muito inferior a maioria dos concorrentes. Mas era simples, barato e leve. O NoSQL atende demandas específicas onde os bancos de dados relacionais não são tão eficientes e garantiram seu espaço no mercado;
    • Grandes empresas podem cair sim. A IBM tem mais de um século, criou um número enorme de novas tecnologias e foi uma das maiores empresas do planeta. Seus funcionários ganharam 5 prêmios Nobel. Foi a maior detentora de patentes do planeta e hoje não é mais tão grande assim. Grandes empresas tem grande capacidade de investimento em pesquisa, mas são péssimas em se adaptar às novas tendências.

    Sobre o futuro…

    Brincar de futurologia é sempre um perigo. Dizem que se a frase “quem não conhece a história está condenado a repeti-la” fosse verdade, ninguém casaria duas vezes! Outro ponto que devemos observar é que o mercado de banco de dados se move lentamente. DBAs são pessoas conservadoras e resistentes à mudanças. Mas me arrisco a alguns palpites aqui, que acho bastante válidos:

    • A nuvem não vai dominar o mundo, mas vai continuar crescendo e provavelmente vai engolir mais da metade do mercado. Existem muitas aplicações de grande porte que não vão se adaptar bem à nuvem, por questões de desempenho, por questões legais, segurança, custo, etc. Mas aos poucos, a maior parte das novas aplicações nascerão na nuvem. Quem não se adaptar vai dançar;
    • A maioria dos bancos de dados rodarão em Linux. O lançamento do SQL Server para Linux não foi um mero acidente, é uma tendência de mercado clara. Além disso, parece que nos últimos testes o SQL Server já roda mais rápido em Linux do que em Windows.
    • Assim como o Linux dominou o mercado de sistemas operacionais, particularmente na nuvem, os bancos de dados livres dominarão o mercado, pelo menos nas aplicações mais comuns. Os bancos de dados livres já tem a características suficientemente boas para lidar com a maior parte dos problemas comuns. Os bancos de dados proprietários  se tornarão soluções de nicho, como são hoje os supercomputadores e sistemas operacionais mais exóticos. Em quase 5 anos, os bancos de dados livres pularam de 35,5% para 46,2% do ranking do DB Engines;
    • Cada vez mais a flexibilidade vai se tornar um fator decisivo ao escolher um banco de dados. A capacidade de estender as capacidades e de conversar nativamente com outros bancos de dados e serviços é fundamental para o sucesso e evolução dos projetos que começam hoje;
    • Os bancos de dados relacionais continuarão dominando o mercado e incorporando algumas características de outros bancos de dados NoSQL. Outras soluções não relacionais continuarão surgindo, mas entre as centenas de soluções que surgiram recentemente deverão sumir em favor de algumas poucas opções mais populares. Apesar do rápido crescimento dos bancos de dados NoSQL, os bancos de dados relacionais ainda respondem por quase 80% do ranking do DB Engines.
plugins premium WordPress