Migrar utilizadores e esquemas da base de dados Oracle® para o Cloud SQL para PostgreSQL

Este documento faz parte de uma série que fornece informações e orientações importantes relacionadas com o planeamento e a execução de migrações de bases de dados Oracle® 11g/12c para o Cloud SQL para PostgreSQL versão 12. Este documento aborda as diferenças básicas entre a base de dados Oracle® e o Cloud SQL para PostgreSQL no que diz respeito à criação de utilizadores, esquemas, tabelas, índices e vistas.

Além da parte de configuração introdutória, a série inclui as seguintes partes:

Diferenças de terminologia entre o Oracle e o Cloud SQL para PostgreSQL

O Oracle e o Cloud SQL para PostgreSQL têm arquiteturas e terminologia diferentes para instâncias, bases de dados, utilizadores e esquemas. Para ver um resumo destas diferenças, consulte a parte de terminologia desta série.

Exportar configurações do Oracle

Um dos primeiros passos ao planear uma migração para o Cloud SQL for PostgreSQL é rever as definições de parâmetros existentes na base de dados Oracle de origem. As definições relativas à atribuição de memória, ao conjunto de carateres e aos parâmetros de armazenamento são particularmente úteis porque podem informar a configuração e o dimensionamento iniciais do ambiente de destino do Cloud SQL para PostgreSQL. Existem vários métodos para extrair as definições dos parâmetros do Oracle. Seguem-se algumas comuns:

  • Os relatórios do repositório de carga de trabalho automático (AWR) contêm dados de atribuição de recursos (CPU, RAM), configuração de parâmetros de instância e sessões ativas máximas.
  • DBA_HIST, V$OSSTAT e V$LICENSE para ver detalhes de utilização da CPU.
  • V$PARAMETER para ver os parâmetros de configuração da base de dados.
  • V$NLS_PARAMETERS para ver os parâmetros de idioma da base de dados.
  • DBA_DATA_FILES para calcular o tamanho do armazenamento da base de dados.
  • O Oracle SPFILE para configurações de instâncias da base de dados.
  • Ferramentas de agendamento de tarefas (por exemplo, crontab) para identificar cópias de segurança de rotina ou janelas de manutenção que devem ser tidas em consideração.

Importar e configurar utilizadores no Cloud SQL para PostgreSQL

A um nível elevado, cada esquema do Oracle deve ser criado como o seu próprio esquema no PostgreSQL. Numa base de dados Oracle, user é sinónimo de schema. Isto significa que é criado um esquema quando cria um utilizador. Existe sempre uma relação individual entre os utilizadores e os esquemas. No PostgreSQL, os utilizadores e os esquemas são criados separadamente. Um utilizador pode ser criado sem criar um esquema correspondente. Para manter a mesma estrutura de utilizador ou esquema do Oracle no PostgreSQL, pode criar um esquema para cada utilizador.

A tabela seguinte ilustra exemplos de conversão:

Tipo de ação Tipo de base de dados Comparação de comandos
Crie o utilizador e o esquema Oracle CREATE USER username IDENTIFIED BY password;
PostgreSQL O utilizador e o esquema são conceitos distintos no PostgreSQL e, por isso, requerem duas declarações CREATE separadas

CREATE USER username WITH PASSWORD 'password';
CREATE SCHEMA schema_name;
Atribuir funções Oracle GRANT CONNECT TO username;
PostgreSQL GRANT pg_monitor TO username;
Conceder privilégios Oracle GRANT SELECT, INSERT, UPDATE ON HR.EMPLOYEES TO username;
PostgreSQL GRANT SELECT, INSERT, UPDATE ON HR.EMPLOYEES TO username;
Revogar privilégios Oracle REVOKE UPDATE ON HR.EMPLOYEES FROM username;
PostgreSQL REVOKE UPDATE ON HR.EMPLOYEES FROM username;
Conceda DBA/superutilizador Oracle GRANT DBA TO username;
PostgreSQL GRANT cloudsqlsuperuser TO username;
Remover utilizador Oracle DROP USER username CASCADE;
PostgreSQL O utilizador e o esquema são conceitos distintos no PostgreSQL e, por isso, requerem duas declarações DROP separadas

DROP USER username;
DROP SCHEMA schema_name CASCADE;
Metadados dos utilizadores Oracle DBA_USERS
PostgreSQL pg_catalog.pg_user
Metadados de autorizações Oracle DBA_SYS_PRIVS
DBA_ROLE_PRIVS
SESSION_PRIVS
PostgreSQL pg_catalog.pg_roles
String de ligação da CLI Oracle sqlplus username/password@host/tns_alias
Sqlplus username/password@host:IP/sid
PostgreSQL Sem pedido de palavra-passe:

PGPASSWORD=password psql -h hostname -U username -d database_name

Com pedido de palavra-passe:

psql -h hostname -U username -W -d database_name

Utilizadores de bases de dados Oracle 12c:

Existem dois tipos de utilizadores no Oracle 12c: utilizadores comuns e utilizadores locais. Os utilizadores comuns são criados no CDB raiz, incluindo PDBs. São identificados pelo prefixo C## no respetivo nome de utilizador. Os utilizadores locais são criados apenas numa PDB específica. Podem ser criados diferentes utilizadores da base de dados com nomes de utilizador idênticos em várias PDBs. Quando migrar do Oracle 12c para o PostgreSQL, modifique os utilizadores e as autorizações para se adequarem à arquitetura do PostgreSQL. Seguem-se dois exemplos comuns para ilustrar estas diferenças:

# Oracle local user
SQL> ALTER SESSION SET CONTAINER=pdb;
SQL> CREATE USER username IDENTIFIED BY password QUOTA 50M ON USERS;

# PostgreSQL user for a single database and schema
postgres=> CREATE USER username WITH PASSWORD 'password';
postgres=> GRANT CONNECT TO DATABASE database_name TO username;
postgres=> GRANT USAGE ON SCHEMA schema_name TO username;
postgres=> -- Optionally, grant object privileges in the schema
postgres=> GRANT ALL ON ALL TABLES IN SCHEMA schema_name TO username;
postgres=> GRANT ALL ON ALL SEQUENCES IN SCHEMA schema_name TO username;
postgres=> GRANT ALL ON ALL FUNCTIONS IN SCHEMA schema_name TO username;
postgres=> GRANT ALL ON ALL PROCEDURES IN SCHEMA schema_name TO username;
postgres=> GRANT ALL ON ALL ROUTINES IN SCHEMA schema_name TO username;

# Oracle common user
SQL> CREATE USER c##username IDENTIFIED BY password CONTAINER=ALL;

# PostgreSQL user with permissions for all database (use the local user script above and repeat it for each database and schema)

Gerir utilizadores através da Google Cloud consola

Para ver os utilizadores configurados atuais do Cloud SQL para PostgreSQL, aceda à página seguinte na Google Cloud consola:

Google Cloud > Storage > SQL > Instância > Utilizadores

Captura de ecrã da página Utilizadores.

Importar definições de tabelas e vistas

O Oracle e o PostgreSQL diferem em termos de sensibilidade a maiúsculas e minúsculas. Os nomes do Oracle não são sensíveis a maiúsculas e minúsculas. Os nomes do PostgreSQL não são sensíveis a maiúsculas e minúsculas, exceto quando estão entre aspas duplas. Muitas ferramentas de exportação de esquemas e geração de SQL para o Oracle, como o DBMS_METADATA.GET_DDL, adicionam automaticamente aspas duplas aos nomes dos objetos. Estas aspas podem causar todo o tipo de problemas após a migração. Recomendamos que remova todas as aspas que envolvem os nomes dos objetos das declarações de linguagem de definição de dados (DDL) antes de criar os objetos no PostgreSQL.

Sintaxe de criação de tabelas

Quando converte tabelas do Oracle para tipos de dados do PostgreSQL, o primeiro passo é extrair as declarações de criação de tabelas do Oracle da base de dados de origem. A seguinte consulta de exemplo extrai o DDL da tabela de localizações do esquema de RH:

SQL> SELECT DBMS_METADATA.GET_DDL('TABLE', 'LOCATIONS') FROM DUAL;

CREATE TABLE "HR"."LOCATIONS"
   (  "LOCATION_ID" NUMBER(4,0),
  "STREET_ADDRESS" VARCHAR2(40),
  "POSTAL_CODE" VARCHAR2(12),
  "CITY" VARCHAR2(30) CONSTRAINT "LOC_CITY_NN" NOT NULL ENABLE,
  "STATE_PROVINCE" VARCHAR2(25),
  "COUNTRY_ID" CHAR(2),
  CONSTRAINT "LOC_ID_PK" PRIMARY KEY ("LOCATION_ID")
...
      CONSTRAINT "LOC_C_ID_FK" FOREIGN KEY ("COUNTRY_ID")
          REFERENCES "HR"."COUNTRIES" ("COUNTRY_ID") ENABLE

O resultado completo inclui elementos de armazenamento, índices e informações do espaço de tabelas, que foram omitidos porque estes elementos adicionais não são suportados pela declaração CREATE TABLE do PostgreSQL.

Depois de extrair o DDL, remova as aspas que envolvem os nomes e faça a conversão da tabela de acordo com os tipos de dados do Oracle para o PostgreSQL tabela de conversão. Verifique o tipo de dados de cada coluna para ver se pode ser convertido tal como está ou, se não for suportado, escolha um tipo de dados diferente de acordo com a tabela de conversão. Por exemplo, o seguinte é o DDL convertido para a tabela de localizações.

CREATE TABLE HR.LOCATIONS (
  LOCATION_ID NUMERIC(4,0),
  STREET_ADDRESS VARCHAR(40),
  POSTAL_CODE VARCHAR(12),
  CITY VARCHAR(30) CONSTRAINT LOC_CITY_NN NOT NULL,
  STATE_PROVINCE VARCHAR(25),
  COUNTRY_ID CHAR(2),
  CONSTRAINT LOC_ID_PK PRIMARY KEY (LOCATION_ID),
  CONSTRAINT LOC_C_ID_FK FOREIGN KEY (COUNTRY_ID)
REFERENCES HR.COUNTRIES (COUNTRY_ID)
)

Create Table As Select (CTAS)

A declaração CREATE TABLE AS SELECT (CTAS) é usada para criar uma nova tabela com base numa tabela existente. Tenha em atenção que apenas os nomes das colunas e os tipos de dados das colunas são copiados, enquanto as restrições e os índices não são. O PostgreSQL suporta a norma SQL ANSI para a funcionalidade CTAS e é compatível com a declaração CTAS da Oracle.

Colunas invisíveis do Oracle 12c

O PostgreSQL não suporta colunas invisíveis. Para uma solução alternativa, crie uma vista que contenha apenas as colunas visíveis.

Restrições de tabelas

O Oracle oferece seis tipos de restrições de tabelas que podem ser definidas na criação de tabelas ou após a criação de tabelas através do comando ALTER TABLE. Os tipos de restrições da Oracle são PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK, NOT NULL e REF. Além disso, a Oracle permite que o utilizador controle o estado de uma restrição através das seguintes opções:

  • INITIALLY IMMEDIATE: verifica a restrição no final de cada declaração SQL subsequente (o estado predefinido).
  • DEFERRABLE/NOT DEFERRABLE: permite a utilização da cláusula SET CONSTRAINT em transações subsequentes até ser enviada uma declaração COMMIT
  • INITIALLY DEFERRED: verifica a restrição no final das transações subsequentes.
  • VALIDATE/NO VALIDATE: verifica (ou não verifica deliberadamente) as linhas novas ou modificadas quanto a erros. Estes parâmetros dependem de a restrição ser ENABLED ou DISABLED.
  • ENABLED/DISABLED: especifica se a restrição deve ser aplicada após a criação (ENABLED por predefinição)

O PostgreSQL também suporta seis tipos de restrições de tabelas: PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK, NOT NULL e EXCLUDE. No entanto, existem algumas diferenças notáveis entre os tipos de restrições do Oracle e do PostgreSQL, incluindo o seguinte:

  • O PostgreSQL não suporta a restrição REF da Oracle.
  • O PostgreSQL não cria automaticamente um índice nas colunas de referência para uma restrição de chave externa. É necessária uma declaração CREATE INDEX separada nas colunas de referência se for necessário um índice.
  • O PostgreSQL não suporta a cláusula ON DELETE SET NULL da Oracle. Esta cláusula indica ao Oracle que defina todos os valores dependentes nas tabelas secundárias como NULL quando o registo na tabela principal é eliminado.
  • As restrições em VIEWS não são suportadas, exceto CHECK OPTION.
  • O PostgreSQL não suporta a desativação de restrições. O PostgreSQL suporta a opção NOT VALID quando é adicionada uma nova chave externa ou restrição de verificação através de uma declaração ALTER TABLE. Esta opção indica ao PostgreSQL para ignorar as verificações de integridade referencial nos registos existentes na tabela secundária.

A tabela seguinte resume as principais diferenças entre os tipos de restrições do Oracle e do PostgreSQL:

Tipo de restrição da Oracle Suporte do Cloud SQL para PostgreSQL Equivalente do Cloud SQL para PostgreSQL
PRIMARY KEY Sim PRIMARY KEY
FOREIGN KEY Sim Usa a mesma sintaxe ANSI SQL que o Oracle.

Usa a cláusula ON DELETE para processar casos de eliminações de registos principais FOREIGN KEY. O PostgreSQL oferece três opções para processar casos em que os dados são eliminados da tabela principal e uma tabela secundária é referenciada por uma restrição FOREIGN KEY:

  • ON DELETE CASCADE
  • ON DELETE RESTRICT
  • ON DELETE NO ACTION

O PostgreSQL não suporta a cláusula ON DELETE SET NULL da Oracle.

Usa a cláusula ON UPDATE para processar casos de atualizações de registos principais FOREIGN KEY.
O PostgreSQL oferece três opções para processar FOREIGN KEY eventos de atualização de restrições:

  • ON UPDATE CASCADE
  • ON UPDATE RESTRICT
  • ON UPDATE NO ACTION

O PostgreSQL não cria automaticamente um índice nas colunas de referência para uma restrição de chave externa.
UNIQUE Sim Cria um índice UNIQUE por predefinição.
CHECK Sim CHECK
NOT NULL Sim NOT NULL
REF Não Não suportado.
DEFERRABLE/NOT DEFERRABLE Sim DEFERRABLE/NOT DEFERRABLE
INITIALLY IMMEDIATE Sim INITIALLY IMMEDIATE
INITIALLY DEFERRED Sim INITIALLY DEFERRED
VALIDATE/NO VALIDATE Não Não suportado.
ENABLE/DISABLE Não Ativada por predefinição. Use a opção NOT VALID quando uma nova chave estrangeira ou restrição de verificação for adicionada à tabela através de uma declaração ALTER TABLE para ignorar as verificações de integridade referencial em registos existentes.
Restrição em VIEWs Não Não suportado, exceto o VIEW WITH CHECK OPTION.
Metadados de restrições Oracle DBA_CONSTRAINTS
PostgreSQL INFORMATION_SCHEMA.TABLE_CONSTRAINTS

Colunas virtuais e geradas

As colunas virtuais da Oracle baseiam-se nos resultados dos cálculos de outras colunas. Aparecem como colunas normais, mas os respetivos valores são derivados de um cálculo em tempo real pelo motor da base de dados Oracle e não são armazenados na base de dados. As colunas virtuais podem ser usadas com restrições, índices, partição de tabelas e chaves externas, mas não podem ser manipuladas através de operações de linguagem de manipulação de dados (DML).

As colunas geradas do PostgreSQL são comparáveis às colunas virtuais do Oracle em termos de funcionalidade. No entanto, ao contrário do Oracle, as colunas geradas no PostgreSQL são armazenadas e tem de especificar um tipo de dados para cada coluna gerada, o que significa que ocupam armazenamento como se fossem colunas normais.

Exemplo de uma coluna virtual no Oracle:

SQL> CREATE TABLE PRODUCTS (
        PRODUCT_ID     INT PRIMARY KEY,
        PRODUCT_TYPE   VARCHAR2(100) NOT NULL,
        PRODUCT_PRICE  NUMBER(6,2) NOT NULL,
        PRICE_WITH_TAX AS (ROUND(PRODUCT_PRICE * 1.01, 2))
);

SQL> INSERT INTO PRODUCTS(PRODUCT_ID, PRODUCT_TYPE, PRODUCT_PRICE)
     VALUES(1, 'A', 99.99);

SQL> SELECT * FROM PRODUCTS;
PRODUCT_ID PRODUCT_TYPE         PRODUCT_PRICE PRICE_WITH_TAX
---------- -------------------- ------------- --------------
         1 A                            99.99         100.99

Exemplo equivalente no PostgreSQL:

postgres=> CREATE TABLE PRODUCTS (
postgres(>         PRODUCT_ID     INT PRIMARY KEY,
postgres(>         PRODUCT_TYPE   VARCHAR(100) NOT NULL,
postgres(>         PRODUCT_PRICE  NUMERIC(6,2) NOT NULL,
postgres(>         PRICE_WITH_TAX NUMERIC GENERATED ALWAYS AS (ROUND(PRODUCT_PRICE * 1.01, 2)) STORED
postgres(> );

postgres=> INSERT INTO PRODUCTS(PRODUCT_ID, PRODUCT_TYPE, PRODUCT_PRICE) VALUES(1, 'A', 99.99);

postgres=> SELECT * FROM PRODUCTS;
 product_id | product_type | product_price | price_with_tax
------------+--------------+---------------+----------------
          1 | A            |         99.99 |         100.99
(1 row)

Índices de tabelas

A Oracle e o PostgreSQL oferecem uma variedade de algoritmos de indexação e tipos de índices que podem ser usados para uma variedade de aplicações. Segue-se uma lista dos algoritmos de indexação disponíveis no PostgreSQL:

Algoritmo de índice Descrição
Árvore B
  • Tipo de índice predefinido para o PostgreSQL, usado para acelerar as consultas de igualdade e intervalo
  • Suporta todos os tipos de dados primitivos e pode ser usado para obter valores NULL
  • Por predefinição, os valores de índice são ordenados por ordem ascendente, mas também podem ser configurados por ordem descendente
Hash
  • Usado para acelerar as pesquisas de igualdade
  • Mais eficiente do que o índice de árvore B, mas limitado apenas ao processamento de pesquisas de igualdade
GIN
  • Índices de árvores invertidas
  • Mais eficiente do que o índice de árvore B quando lida com colunas que contêm vários valores de componentes, como matrizes e texto
GiST
  • Não é um único tipo de índice, mas sim uma infraestrutura para definir índices que podem suportar mais operadores de comparação do que um índice B-tree normal
  • Útil para dados geométricos quando é necessário otimizar as pesquisas de "vizinho mais próximo"
SP-GiST
  • Semelhante ao GiST, o SP-GiST é uma infraestrutura para estratégias de indexação definidas pelo utilizador
  • Permite uma vasta gama de estruturas de dados não equilibradas diferentes, como quadtrees
  • Não disponível no Cloud SQL para PostgreSQL
BRIN
  • Índices de intervalos de bloqueio
  • Armazena resumos de intervalos de blocos físicos de uma tabela
  • Para colunas com uma ordem de ordenação linear
  • Útil para a pesquisa de intervalos em tabelas enormes

A tabela seguinte compara os tipos de índice entre o Oracle e o PostgreSQL:

Índice Oracle Descrição Suportado pelo PostgreSQL Equivalente do PostgreSQL
Índice de mapa de bits Armazena um mapa de bits para cada chave de índice, mais adequado para fornecer uma obtenção de dados rápida para cargas de trabalho OLAP Não N/A
Índice de árvore B O tipo de índice mais comum, adequado para uma variedade de cargas de trabalho e que pode ser configurado na ordenação ASC|DESC. Sim Índice de árvore B
Índice composto Criadas em mais de duas colunas para melhorar o desempenho da obtenção de dados. A ordem das colunas no índice determina o caminho de acesso. Sim Índices de várias colunas
É possível especificar até 32 colunas ao criar um índice de várias colunas.
Índice baseado em funções Armazena o resultado de uma função aplicada aos valores de uma coluna de tabela. Sim Índices em expressões
Índice único Um índice de árvore B que aplica uma restrição UNIQUE aos valores indexados por coluna. Sim Índice único
Índice de domínios de aplicações Adequado para indexar dados não relacionais, como dados de áudio/vídeo, dados LOB e outros tipos não textuais. Não N/A
Índice invisível Funcionalidade da Oracle que lhe permite gerir, manter e testar índices sem afetar a tomada de decisões do otimizador. Não Como solução alternativa, pode criar um índice adicional numa réplica de leitura para fins de teste sem afetar a atividade em curso.
Tabela organizada por índice Um tipo de índice que controla a forma como os dados são armazenados ao nível da tabela e do índice. Não O PostgreSQL não suporta tabelas organizadas por índice. A declaração CLUSTER indica ao PostgreSQL que organize o armazenamento de tabelas de acordo com um índice especificado. Tem uma finalidade semelhante à tabela organizada por índice da Oracle. No entanto, a aglomeração é uma operação única e o PostgreSQL não mantém a estrutura da tabela nas atualizações subsequentes. É necessário um agrupamento manual e periódico.
Índice local e global Usado para indexar tabelas particionadas numa base de dados Oracle. Cada índice é definido como LOCAL ou GLOBAL. Não Os índices de trabalho das partições do PostgreSQL têm a mesma funcionalidade que os índices locais do Oracle (ou seja, o índice é definido ao nível da partição, o nível global não é suportado).
Índices parciais para tabelas particionadas (Oracle 12c) Cria um índice num subconjunto das partições de uma tabela. Suporta LOCAL e GLOBAL. Sim A partição no PostgreSQL funciona anexando tabelas secundárias a uma tabela principal. Só é possível criar índices num subconjunto de tabelas secundárias.
CREATE/DROP INDEX Comando usado para a criação e a eliminação de índices. Sim O PostgreSQL suporta o comando CREATE INDEX. Também suporta ALTER TABLE tableName ADD INDEX indexName columnName
ALTER INDEX ... REBUILD Reconstrói o índice, o que pode causar um bloqueio exclusivo na tabela indexada. Requer uma sintaxe diferente O PostgreSQL suporta recriações de índices através da declaração REINDEX. A tabela está bloqueada para escritas durante esta operação e só são permitidas leituras.
ALTER INDEX ... REBUILD ONLINE Reconstrói um índice sem criar um bloqueio exclusivo na tabela. Requer uma sintaxe diferente O PostgreSQL suporta recriações de índices simultâneas através da declaração REINDEX TABLE CONCURRENTLY. Neste modo, o PostgreSQL tenta reconstruir os índices usando o bloqueio mínimo com a desvantagem de potencialmente demorar mais tempo e recursos para concluir a reconstrução.
Compressão de índice Uma funcionalidade para reduzir o tamanho físico do índice. Não N/A
Atribua
índice a um espaço de tabelas
Cria um tablespace de índice que pode ser armazenado num disco separado dos dados da tabela para reduzir os estrangulamentos de E/S do disco. Não Embora o PostgreSQL permita a criação de um índice no espaço de tabelas definido pelo utilizador, não pode criar espaços de tabelas no Cloud SQL para PostgreSQL, e o índice tem de ser criado no espaço de tabelas predefinido.
Indexa metadados (tabelas/vistas) Oracle DBA_INDEXES
DBA_PART_INDEXES
DBA_IND_COLUMNS
PostgreSQL pg_catalog.pg_index
pg_catalog.pg_attribute
pg_catalog.pg_class

Considerações sobre a conversão de índices

Na maioria dos casos, os índices Oracle podem ser simplesmente convertidos em índices B-tree do PostgreSQL, porque este tipo de índice é o tipo de índice mais usado. Tal como numa base de dados Oracle, é criado automaticamente um índice nos PRIMARY KEYcampos de uma tabela. Da mesma forma, é criado automaticamente um índice UNIQUE nos campos que têm uma restrição UNIQUE. Além disso, os índices secundários são criados através da declaração CREATE INDEX padrão.

O exemplo seguinte ilustra como uma tabela Oracle com vários campos indexados pode ser convertida para PostgreSQL:

SQL> CREATE TABLE ORA_IDX_TO_PG (
        col1 INT PRIMARY KEY,
        col2 VARCHAR2(60),
        col3 DATE,
        col4 CLOB,
        col5 VARCHAR2(20)
      );

-- Single-field index
SQL> CREATE INDEX idx_col2 ON ora_idx_to_pg(col2);

-- Composite index
SQL> CREATE INDEX idx_cols3_2 ON ora_idx_to_pg(col3 DESC, col2);

-- Unique index
SQL> CREATE UNIQUE INDEX idx_col3_uni ON ora_idx_to_pg(col3);

-- Function-based index
SQL> CREATE INDEX idx_func_col3 ON
        ora_idx_to_pg(EXTRACT(MONTH FROM col3));

-- CLOB index
SQL> CREATE INDEX idx_col4 ON
       ora_idx_to_pg(col4) INDEXTYPE IS CTXSYS.CONTEXT;

-- Invisible index
SQL> CREATE INDEX idx_col5_inv ON
        ora_idx_to_pg(col5) INVISIBLE;

-- Drop index
SQL> DROP INDEX idx_col5_inv;

postgres=> CREATE TABLE ORA_IDX_TO_PG (
postgres(> col1 INT PRIMARY KEY,
postgres(> col2 VARCHAR(60),
postgres(> col3 DATE,
postgres(> col4 TEXT,
postgres(> col5 VARCHAR(20)
postgres(> );

-- Single index (supported)
postgres=> CREATE INDEX idx_col2 ON ora_idx_to_pg(col2);

-- Composite index (supported)
postgres=> CREATE INDEX idx_cols3_2 ON ora_idx_to_pg(col3 DESC, col2);

-- Unique index (supported)
postgres=> CREATE UNIQUE INDEX idx_col3_uni ON ora_idx_to_pg(COL3);

-- Function-based index (supported)
postgres=> CREATE INDEX idx_func_col3 ON
postgres->         ora_idx_to_pg(EXTRACT(MONTH FROM col3));

-- CLOB (Supported, but requires different syntax. See Full Text Search for details)
postgres=> CREATE INDEX idx_col4 ON ora_idx_to_pg
postgres->         USING GIN (to_tsvector('english', col4));

-- Invisible index (not supported)
-- Optional - create the index as a B-tree index
postgres=> CREATE INDEX idx_col5 ON ora_idx_to_pg(col5);

-- Drop index
postgres=> DROP INDEX idx_col2;

SQL> SELECT ui.table_name,
            ui.index_name,
            ui.index_type,
            ic.column_name
     FROM user_indexes ui JOIN user_ind_columns ic
     ON ui.index_name = ic.index_name
     WHERE ui.table_name = 'ORA_IDX_TO_PG'
     ORDER BY 4;

postgres=> select distinct
postgres->     t.relname as table_name,
postgres->     i.relname as index_name,
postgres-> pg_get_indexdef(ix.indexrelid) index_definition
postgres-> from
postgres->     pg_class t,
postgres->     pg_class i,
postgres->     pg_index ix
postgres-> where
postgres->     t.oid = ix.indrelid
postgres->     and i.oid = ix.indexrelid
postgres->     and t.relname = 'ora_idx_to_pg'
postgres-> order by
postgres->     t.relname,
postgres->     i.relname;

-- OR Use psql \d command:
postgres=> \d ora_idx_to_pg
                  Table "public.ora_idx_to_pg"
 Column |         Type          | Collation | Nullable | Default
--------+-----------------------+-----------+----------+---------
 col1   | integer               |           | not null |
 col2   | character varying(60) |           |          |
 col3   | date                  |           |          |
 col4   | text                  |           |          |
 col5   | character varying(20) |           |          |
Indexes:
    "ora_idx_to_pg_pkey" PRIMARY KEY, btree (col1)
    "idx_col2" btree (col2)
    "idx_col4" gin (to_tsvector('english'::regconfig, col4))
    "idx_col5" btree (col5)
    "idx_cols3_2" btree (col3 DESC, col2)
    "idx_func_col3" btree (date_part('month'::text, col3))

postgres=>

Particionamento de tabelas

O Oracle e o PostgreSQL oferecem capacidades de partição para dividir tabelas grandes. Isto é feito segmentando fisicamente uma tabela em partes mais pequenas, em que cada parte contém um subconjunto horizontal das linhas. A tabela particionada é denominada tabela principal e as respetivas linhas são armazenadas fisicamente nas partições. Embora nem todos os tipos de partições da Oracle sejam suportados no PostgreSQL, o PostgreSQL suporta os mais comuns.

As secções seguintes descrevem os tipos de partições suportados pelo PostgreSQL, ilustrando cada um com um exemplo de como criar as partições que correspondem a esse tipo.

Partição RANGE

Este tipo de partição atribui linhas a partições com base nos valores das colunas que se encontram dentro de um determinado intervalo. Cada partição contém linhas para as quais o valor da expressão de particionamento se encontra num determinado intervalo. É importante ter em atenção que os intervalos não se sobrepõem entre partições.

Exemplo

CREATE TABLE employees (
 empid     INT,
 fname     VARCHAR(30),
 lname     VARCHAR(30),
 hired     DATE,
 separated DATE,
 job_code  INT,
 store_id  INT)
 PARTITION BY RANGE (store_id);

CREATE TABLE employees_p0 PARTITION OF employees
 FOR VALUES FROM (MINVALUE) TO (6);
CREATE TABLE employees_p1 PARTITION OF employees
 FOR VALUES FROM (6) TO (11);
CREATE TABLE employees_p2 PARTITION OF employees
 FOR VALUES FROM (11) TO (16);
CREATE TABLE employees_p3 PARTITION OF employees
 FOR VALUES FROM (16) TO (21);

Criação de partições LIST

Semelhante à partição RANGE, a partição LIST atribui linhas a partições com base nos valores das colunas que se enquadram num conjunto predefinido de valores. Os valores-chave que aparecem em cada partição são explicitamente indicados para partições de LIST.

Exemplo

CREATE TABLE employees (
 empid     INT,
 fname     VARCHAR(30),
 lname     VARCHAR(30),
 hired     DATE,
 separated DATE,
 job_code  INT,
 store_id  INT)
 PARTITION BY LIST (store_id);

CREATE TABLE employees_pNorth PARTITION OF employees
 FOR VALUES IN (3,5,6);
CREATE TABLE employees_pEast PARTITION OF employees
 FOR VALUES IN (1,2,10);
CREATE TABLE employees_pWest PARTITION OF employees
 FOR VALUES IN (4,12,13);
CREATE TABLE employees_pCnrl PARTITION OF employees
 FOR VALUES IN (7,8,15);

Partição HASH

A partição HASH é mais adequada quando o objetivo é alcançar uma distribuição uniforme de dados entre todas as partições. Um valor de coluna (ou uma expressão baseada num valor de coluna a ser calculado com hash) e o valor da linha são atribuídos à partição que corresponde a esse valor de hash. Os valores hash têm de ser atribuídos exclusivamente a partições, e todos os valores inseridos têm de ser mapeados exatamente para uma partição.

Exemplo

CREATE TABLE employees (
 empid     INT,
 fname     VARCHAR(30),
 lname     VARCHAR(30),
 hired     DATE,
 separated DATE,
 job_code  INT,
 store_id  INT)
 PARTITION BY HASH (date_part('year', hired));

CREATE TABLE employees_p0 PARTITION OF employees
 FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE employees_p1 PARTITION OF employees
 FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE employees_p2 PARTITION OF employees
 FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE employees_p3 PARTITION OF employees
 FOR VALUES WITH (MODULUS 4, REMAINDER 3);

Partição em vários níveis

A partição de vários níveis é um método de criação de uma hierarquia de partições para uma única tabela. Cada partição é ainda dividida num número de partições diferentes. O número de subpartições pode variar de uma partição para outra.

Exemplo

CREATE TABLE sales (
 Saleid    INT,
 sale_date DATE,
 cust_code VARCHAR(15),
 income    DECIMAL(8,2))
PARTITION BY RANGE(date_part('year', sale_date));

CREATE TABLE sales_2019 PARTITION OF sales
 FOR VALUES FROM (2019) TO (2020)
 PARTITION BY RANGE(date_part('month', sale_date));

CREATE TABLE sales_2019_q1 PARTITION OF sales_2019
 FOR VALUES FROM (1) TO (4);
CREATE TABLE sales_2019_q2 PARTITION OF sales_2019
 FOR VALUES FROM (4) TO (7);
CREATE TABLE sales_2019_q3 PARTITION OF sales_2019
 FOR VALUES FROM (7) TO (10);
CREATE TABLE sales_2019_q4 PARTITION OF sales_2019
 FOR VALUES FROM (10) TO (13);

CREATE TABLE sales_2020 PARTITION OF sales
 FOR VALUES FROM (2020) TO (2021)
 PARTITION BY RANGE(date_part('month', sale_date));

CREATE TABLE sales_2020_q1 PARTITION OF sales_2020
 FOR VALUES FROM (1) TO (4);
CREATE TABLE sales_2020_q2 PARTITION OF sales_2020
 FOR VALUES FROM (4) TO (7);
CREATE TABLE sales_2020_h2 PARTITION OF sales_2020
 FOR VALUES FROM (7) TO (13);

Anexar ou desanexar partições

No PostgreSQL, as partições podem ser adicionadas ou removidas da tabela principal. Uma partição separada pode ser novamente anexada à mesma tabela mais tarde. Além disso, podem ser especificadas novas condições de partição quando voltar a anexar a partição, o que permite ajustar os limites da partição.

Exemplo

CREATE TABLE employees (
 empid     INT,
 fname     VARCHAR(30),
 lname     VARCHAR(30),
 hired     DATE,
 separated DATE,
 job_code  INT,
 store_id  INT)
 PARTITION BY RANGE (date_part('year', hired));

CREATE TABLE employees_p0 PARTITION OF employees
 FOR VALUES FROM (2010) TO (2015);
CREATE TABLE employees_p1 PARTITION OF employees
 FOR VALUES FROM (2015) TO (2020);

-- changing partition boundaries
BEGIN TRANSACTION;
ALTER TABLE employees DETACH PARTITION employees_p1;
ALTER TABLE employees ATTACH PARTITION employees_p1 FOR VALUES FROM (2015) TO (2022);
COMMIT TRANSACTION;

A tabela seguinte descreve onde os tipos de partições do Oracle e do Cloud SQL for PostgreSQL são equivalentes e onde se recomenda uma conversão:

Tipo de partição Oracle Suportado pelo PostgreSQL Implementação do PostgreSQL
RANGE partições Sim PARTITION BY RANGE
LIST partições Sim PARTITION BY LIST
HASH partições Sim PARTITION BY HASH
SUB-PARTITIONING Sim Partição em vários níveis
Partições de intervalo Não Não suportado
Consultor de partições Não Não suportado
Partição de preferências Não Não suportado
Particionamento baseado em colunas virtuais Não Como solução alternativa, considere a partição com a expressão da coluna virtual diretamente:

CREATE TABLE users (
id INT,
username VARCHAR(20),
first_letter VARCHAR(1)
GENERATED ALWAYS AS
(
UPPER(SUBSTR(TRIM(username), 1, 1))
) STORED
)
PARTITION BY LIST (UPPER(SUBSTR(TRIM(username), 1, 1)));

Partição automática de listas Não Não suportado
Divida
partições
Não Para uma solução alternativa, considere desanexar ou anexar partições de tabelas para ajustar os limites das partições
Partições de troca Sim DETACH / ATTACH PARTITION
Partição de vários tipos (partição composta) Sim Partição em vários níveis
Metadados de partições Oracle DBA_TAB_PARTITIONS
DBA_TAB_SUBPARTITIONS
PostgreSQL pg_catalog.pg_class
pg_catalog.pg_partitioned_table

O exemplo seguinte é uma comparação lado a lado da criação de partições de tabelas em ambas as plataformas. Tenha em atenção que o PostgreSQL não suporta a referência a um tablespace na cláusula PARTITIONS do comando CREATE TABLE.

Implementação da Oracle

CREATE TABLE employees (
empid NUMBER,
fname VARCHAR2(30),
lname VARCHAR2(30),
hired DATE,
separated DATE,
job_code NUMBER,
store_id NUMBER)
PARTITION BY LIST (store_id) (
PARTITION employees_pNorth VALUES (3,5,6) TABLESPACE users,
PARTITION employees_pEast VALUES (1,2,10) TABLESPACE users,
PARTITION employees_pWest VALUES (4,12,13) TABLESPACE users,
PARTITION employees_pCnrl VALUES (7,8,15) TABLESPACE users
);

Implementação do PostgreSQL

CREATE TABLE employees (
empid INT,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE,
separated DATE,
job_code INT,
store_id INT)
PARTITION BY LIST (store_id);

CREATE TABLE employees_pNorth PARTITION OF employees
FOR VALUES IN (3,5,6);
CREATE TABLE employees_pEast PARTITION OF employees
FOR VALUES IN (1,2,10);
CREATE TABLE employees_pWest PARTITION OF employees
FOR VALUES IN (4,12,13);
CREATE TABLE employees_pCnrl PARTITION OF employees
FOR VALUES IN (7,8,15);

Tabelas temporárias

Numa base de dados Oracle, as tabelas temporárias são denominadas GLOBAL TEMPORARY TABLES, enquanto no PostgreSQL são simplesmente conhecidas como tabelas temporárias. A funcionalidade básica de uma tabela temporária é idêntica em ambas as plataformas. No entanto, existem algumas diferenças notáveis:

  • O Oracle armazena a estrutura da tabela temporária para utilização repetida, mesmo após o reinício da base de dados, enquanto o PostgreSQL armazena a tabela temporária apenas durante uma sessão.
  • Uma tabela temporária numa base de dados Oracle pode ser acedida por diferentes utilizadores com as autorizações adequadas. Por outro lado, só é possível aceder a uma tabela temporária no PostgreSQL durante a sessão em que foi criada, a menos que a tabela temporária seja referenciada com nomes qualificados pelo esquema.
  • Numa base de dados Oracle, existe uma distinção entre tabelas GLOBAL e LOCAL temporárias que especificam se o conteúdo da tabela é global ou específico da sessão. No PostgreSQL, as palavras-chave GLOBAL e LOCAL são suportadas por motivos de compatibilidade, mas não têm qualquer efeito na visibilidade dos dados.
  • Se a cláusula ON COMMIT for omitida ao criar uma tabela temporária, o comportamento predefinido na base de dados Oracle é ON COMMIT DELETE ROWS, o que significa que o Oracle trunca a tabela temporária após cada confirmação. Por outro lado, no PostgreSQL, o comportamento predefinido é preservar as linhas na tabela temporária após cada confirmação.

A tabela seguinte realça as diferenças nas tabelas temporárias entre o Oracle e o Cloud SQL para PostgreSQL.

Funcionalidade de tabela temporária Implementação da Oracle Implementação do PostgreSQL
Sintaxe CREATE GLOBAL TEMPORARY TABLE CREATE TEMPORARY TABLE
Acessibilidade Acessível a partir de várias sessões Acessível apenas a partir da sessão do criador, a menos que seja referenciado com nomes qualificados pelo esquema
Apoio técnico do índice Sim Sim
Suporte de chaves externas Sim Sim
Preserve DDL Sim Não
Ação predefinida ON COMMIT Os registos são eliminados Os registos são preservados
ON COMMIT PRESERVE ROWS Sim Sim
ON COMMIT DELETE ROWS Sim Sim
ON COMMIT DROP Não Sim
ALTER TABLE apoio técnico Sim Sim
A recolher estatísticas DBMS_STATS.GATHER_TABLE_STATS ANALYZE
Oracle 12c GLOBAL_TEMP_

TABLE_STATS
DBMS_STATS.SET_TABLE_PREFS ANALYZE

Colunas não usadas

A funcionalidade da Oracle de marcar colunas específicas como UNUSED é frequentemente usada para remover colunas de tabelas sem remover fisicamente os dados das colunas. Isto destina-se a evitar as potenciais cargas elevadas que ocorrem quando são eliminadas colunas de tabelas grandes.

No PostgreSQL, a eliminação de uma coluna grande não remove os dados da coluna do armazenamento físico e, por isso, é uma operação rápida, mesmo em tabelas grandes. Não é necessário marcar uma coluna como UNUSED, como numa base de dados Oracle. O espaço ocupado pela coluna eliminada é recuperado por novas declarações DML ou durante uma operação VACUUM subsequente.

Tabelas só de leitura

As tabelas só de leitura são uma funcionalidade da Oracle que marca as tabelas como só de leitura através do comando ALTER TABLE. No Oracle 12c R2, esta funcionalidade também está disponível para tabelas com partições e subpartições. O PostgreSQL não oferece uma funcionalidade equivalente, mas existem duas soluções alternativas possíveis:

  • Conceda autorização de SELECT em tabelas para utilizadores específicos. Tenha em atenção que isto não impede o proprietário da tabela de realizar operações de DML nas respetivas tabelas.
  • Crie uma réplica de leitura do Cloud SQL para PostgreSQL e direcione os utilizadores para as tabelas de réplicas que são tabelas só de leitura. Esta solução requer a adição de uma instância de réplica de leitura a uma instância existente do Cloud SQL para PostgreSQL.
  • Crie um acionador da base de dados que gere exceções em declarações DML, por exemplo:

    -- Define trigger function
    CREATE OR REPLACE FUNCTION raise_readonly_exception() RETURNS TRIGGER AS $$
    BEGIN
      RAISE EXCEPTION 'Table is readonly!';
      RETURN NULL;
    END;
    $$ LANGUAGE 'plpgsql';
    
    -- Fire trigger when DML statements is executed on read only table
    CREATE TRIGGER myTable_readonly_trigger
    BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE ON myTable FOR EACH STATEMENT
    EXECUTE PROCEDURE raise_readonly_exception();
    
    -- Testing the trigger
    postgres=> INSERT INTO myTable (id) VALUES (1);
    ERROR:  Table is readonly!
    CONTEXT:  PL/pgSQL function raise_readonly_exception() line 3 at RAISE
    postgres=>
    

Conjuntos de carateres

O Oracle e o PostgreSQL suportam uma grande variedade de conjuntos de carateres, ordenações e Unicode, incluindo suporte para idiomas de byte único e multibyte. Além disso, as bases de dados PostgreSQL que residem na mesma instância podem ser configuradas com conjuntos de carateres distintos. Consulte a lista de conjuntos de carateres suportados no PostgreSQL.

Na base de dados Oracle, os conjuntos de carateres são especificados ao nível da base de dados (Oracle 12g R1 ou anterior) ou ao nível da base de dados conectável (Oracle 12g R2 ou posterior). No PostgreSQL, é especificado um conjunto de carateres predefinido quando uma nova instância do Cloud SQL para PostgreSQL é criada. Cada base de dados criada nessa instância pode ser criada com um conjunto de carateres diferente. A ordem de ordenação e a classificação de carateres podem ser especificadas por coluna da tabela.

Exemplo

-- Create a database using UTF-8 character set and ja_JP.UTF collation
postgres=> CREATE DATABASE jpdb WITH ENCODING 'UTF8' LC_COLLATE='ja_JP.UTF8' LC_CTYPE='ja_JP.UTF8' TEMPLATE=template0;

-- Query the character set and collation settings of all databases
postgres=> SELECT datname AS DATABASE_NAME, datcollate AS LC_COLLATE, datctype AS LC_CTYPE from pg_database;
 database_name | lc_collate |  lc_ctype
---------------+------------+------------
 cloudsqladmin | en_US.UTF8 | en_US.UTF8
 template0     | en_US.UTF8 | en_US.UTF8
 template1     | en_US.UTF8 | en_US.UTF8
 postgres      | en_US.UTF8 | en_US.UTF8
 jpdb          | ja_JP.UTF8 | ja_JP.UTF8
(5 rows)

-- Alternatively, use psql \l command to query the database settings
postgres=> \l
                                                List of databases
     Name      |       Owner       | Encoding |  Collate   |   Ctype    |            Access privileges
---------------+-------------------+----------+------------+------------+-----------------------------------------
 cloudsqladmin | cloudsqladmin     | UTF8     | en_US.UTF8 | en_US.UTF8 |
 postgres      | cloudsqlsuperuser | UTF8     | en_US.UTF8 | en_US.UTF8 | =Tc/cloudsqlsuperuser                  +
               |                   |          |            |            | cloudsqlsuperuser=CTc/cloudsqlsuperuser+
               |                   |          |            |            | testuser=CTc/cloudsqlsuperuser
 template0     | cloudsqladmin     | UTF8     | en_US.UTF8 | en_US.UTF8 | =c/cloudsqladmin                       +
               |                   |          |            |            | cloudsqladmin=CTc/cloudsqladmin
 template1     | cloudsqlsuperuser | UTF8     | en_US.UTF8 | en_US.UTF8 | =c/cloudsqlsuperuser                   +
               |                   |          |            |            | cloudsqlsuperuser=CTc/cloudsqlsuperuser
-- Specifying column level collation
postgres=> CREATE TABLE test1 (
postgres(>     a text COLLATE "de_DE",
postgres(>     b text COLLATE "es_ES"
postgres(> );

Visualizações

O PostgreSQL suporta visualizações simples e complexas. Para as opções de criação de vistas, existem algumas diferenças entre o Oracle e o PostgreSQL. A tabela seguinte realça estas diferenças.

Funcionalidade de visualização da Oracle Descrição Suporte do Cloud SQL para PostgreSQL Considerações sobre as conversões
FORCE Crie uma vista sem verificar se as tabelas/vistas de origem existem. Não Não está disponível nenhuma opção equivalente.
CREATE OR REPLACE Crie uma vista inexistente ou substitua uma vista existente. Sim O PostgreSQL suporta o comando CREATE OR REPLACE para vistas.
WITH CHECK OPTION Especifica o nível de aplicação quando são realizadas operações DML na vista. Sim A predefinição é CASCADED, o que faz com que as visualizações referenciadas também sejam avaliadas.

A palavra-chave LOCAL faz com que apenas a vista atual seja avaliada.
WITH READ-ONLY Permite apenas operações de leitura na vista. As operações DML são proibidas. Não Uma solução alternativa é conceder privilégios SELECT na vista a todos os utilizadores.
VISIBLE | INVISIBLE (Oracle 12c) Especifica se uma coluna baseada na vista está visível ou invisível para o utilizador. Não Crie o ficheiro VIEW apenas com as colunas necessárias.

O exemplo de conversão seguinte demonstra a conversão do Oracle para o Cloud SQL PostgreSQL para visualizações.

-- Create view to retrieve employees from department 100 using the WITH CHECK -- OPTION option
SQL> CREATE OR REPLACE FORCE VIEW vw_emp_dept100
AS
SELECT EMPLOYEE_ID,
       FIRST_NAME,
       LAST_NAME,
       SALARY,
       DEPARTMENT_ID
FROM EMPLOYEES
WHERE DEPARTMENT_ID=100
WITH CHECK OPTION;

-- Perform an UPDATE operation on the VIEW
SQL> UPDATE vw_emp_dept100
     SET salary=salary+1000;

postgres=> CREATE OR REPLACE VIEW vw_emp_dept100
postgres-> AS
postgres-> SELECT EMPLOYEE_ID,
postgres->        FIRST_NAME,
postgres->        LAST_NAME,
postgres->        SALARY,
postgres->        DEPARTMENT_ID
postgres-> FROM EMPLOYEES
postgres-> WHERE DEPARTMENT_ID=100
postgres-> WITH CHECK OPTION;

-- Perform an UPDATE operation on the VIEW
postgres=> UPDATE vw_emp_dept100
postgres-> SET salary=salary+1000;

-- Update one employee department id to 60
postgres=> UPDATE vw_emp_dept100
postgres-> SET DEPARTMENT_ID=60
postgres-> WHERE EMPLOYEE_ID=110;

ERROR:  new row violates check option for view "vw_emp_dept100"
DETAIL:  Failing row contains (110, John, Chen, JCHEN, 515.124.4269, 1997-09-28, FI_ACCOUNT, 9200.00, null, 108, 60).

Gestão de acessos à visualização de propriedade:

Os proprietários de uma vista têm de ter privilégios nas tabelas base para criar a vista. O utilizador de uma visualização de propriedade precisa das autorizações SELECT adequadas na visualização de propriedade. Também precisam das autorizações INSERT, UPDATE e DELETE adequadas na vista quando realizam operações DML através da vista. Em qualquer dos casos, os utilizadores não precisam de autorizações nas tabelas subjacentes.

O que se segue?

  • Explore mais sobre as contas de utilizador do PostgreSQL.
  • Explore arquiteturas de referência, diagramas e práticas recomendadas sobre o Google Cloud. Consulte o nosso Centro de arquitetura na nuvem.