PostgreSQL Prático/DDL/Criação e Exclusão de Bancos, Esquemas, Tabelas, Views, Constraints, etc

DDL é o conjunto de comandos SQL responsáveis pela definição dos dados, ou seja, pela criação de bancos, esquemas, tabelas, campos, tipos de dados, constraints, etc.

3.1 - Criação e exclusão de bancos, esquemas, tabelas, views, etc

Obs.: Nomes de objetos e campos não podem usar hífen (-). Alternativamente usar sublinhado (_).

campo-1 Inválido

campo_1 Válido

Nomes de Identificadores

Utiliza-se por convenção as palavras chaves do SQL em maiúsculas e os identificadores dos objetos que criamos em minúsculas.

Identificadores digitados em maiúsculas serão gravados em minúsculas, a não ser que venham entre aspas “”.

Revisões da Linguagem SQL

SQL – 1989

SQL – 1992

SQL – 1999

SQL – 2003

Divisões da SQL

DML – Linguagem de Manipulação de Dados

DDL – Linguagem de Definição de Dados

DCL – Linguagem de Controle de Dados (autorização de dados e licença de usuários para controlar quem tem acesso aos dados).

DQL – Linguagem de Consulta de Dados (Tem apenas um comando: SELECT).


Criar Banco

banco=# \h create database

Comando: CREATE DATABASE

Descrição: cria um novo banco de dados

Sintaxe:

CREATE DATABASE nome

   [ [ WITH ] [ OWNER [=] dono_bd ]
          [ TEMPLATE [=] modelo ]
          [ ENCODING [=] codificação ]
          [ TABLESPACE [=] tablespace ] ]
          [ CONNECTION LIMIT [=] limite_con ] ]


CREATE DATABASE nomebanco;

Excluindo Um Banco

DROP DATABASE nomebanco;

Listar os bancos existentes:

\l - - No psql

psql -l (no prompt)

SELECT datname FROM pg_database;


Quando se cria um novo banco de dados sem indicar o modelo, o que de fato estamos fazendo é clonar o banco de dados template1.

Criar um banco para outro usuário:


CREATE DATABASE nomebanco OWNER nomeuser;

createdb -O nomeusuario nomebanco

Obs.: requer ser superusuário para poder criar banco para outro usuário.

Criar Tabela

postgres=# \h create table

Comando: CREATE TABLE

Descrição: define uma nova tabela

Sintaxe:

CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE nome_tabela ( [

   { nome_coluna tipo_dado [ DEFAULT expressão_padrão ] [ restrição_coluna [ ... ] ]
   | restrição_tabela
   | LIKE tabela_pai [ { INCLUDING | EXCLUDING } DEFAULTS ] }
   [, ... ]

] )

[ INHERITS ( tabela_pai [, ... ] ) ]

[ WITH OIDS | WITHOUT OIDS ]

[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]

[ TABLESPACE tablespace ]


onde restrição_coluna é:

[ CONSTRAINT nome_restrição ]

{ NOT NULL |

 NULL |
 UNIQUE [ USING INDEX TABLESPACE tablespace ] |
 PRIMARY KEY [ USING INDEX TABLESPACE tablespace ] |
 CHECK (expressão) |
 REFERENCES tabela_ref [ ( coluna_ref ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
   [ ON DELETE ação ] [ ON UPDATE ação ] }

[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]


e restrição_tabela é:

[ CONSTRAINT nome_restrição ]

{ UNIQUE ( nome_coluna [, ... ] ) [ USING INDEX TABLESPACE tablespace ] |

 PRIMARY KEY ( nome_coluna [, ... ] ) [ USING INDEX TABLESPACE tablespace ] |
 CHECK ( expressão ) |
 FOREIGN KEY ( nome_coluna [, ... ] ) REFERENCES tabela_ref [ ( coluna_ref [, ... ] ) ]
   [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE ação ] [ ON UPDATE ação ] }

[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]


Obs.: Atenção: nesta versão (8.1.3) WITH OID é opcional. As tabelas são criadas sem OID.


\d – visualizar tabelas e outros objetos

\d nometabela – visualizar estrutura da tabela


CREATE TABLE tabela (

campo1 integer,

campo2 text

);


Excluindo Tabela

DROP TABLE primeira_tabela;


Valor Default (padrão) Para Campos

Ao definir um valor default para um campo, ao ser cadastrado o registro e este campo não for informado, o valor default é assumido. Caso não seja declarado explicitamente um valor default, o valor nulo (NULL) será o valor default.


CREATE TABLE produtos (

  produto_no integer,
  descricao text,
  preco numeric DEFAULT 9.99

);


Constraints (Restrições)

CHECK

Ao criar uma tabela podemos prever que o banco exija que o valor de um campo satisfaça uma expressão


CREATE TABLE produtos (

  produto_no integer,
  descricao text,
  preco numeric CHECK (preco > 0)

);


Dando nome à restrição check. Isso ajuda a tornar mais amigáveis as mensagens de erro e a poder referenciar de uma consulta.

CREATE TABLE produtos (

  produto_no integer,
  descricao text,
  preco numeric CONSTRAINT preco_positivo CHECK (preco > 0)

);


CREATE TABLE produtos (

  produto_no integer,
  descricao text,
  desconto numeric CHECK (desconto > 0 AND desconto < 0.10),
  preco numeric CONSTRAINT preco_positivo CHECK (preco > 0),
  check (preco > desconto)

);


Constraint NOT NULL

Obrigar o preenchimento de um campo. Ideal para campos importantes que não devem ficar sem preenchimento. Mas devemos ter em mente que até um espaço em branco atende a esta restrição.


CREATE TABLE produtos (

  cod_prod   integer NOT NULL CHECK (cod_prod > 0),
  nome       text    NOT NULL,
  preco      numeric NOT NULL

);

Obs importante: nulos não são checados. UNIQUE não aceita valores repetidos, mas aceita vários nulos (já que estes não são checados). Cuidado com NULLs.


Unique Constraint

Obrigar valores exclusivos para cada campo em todos os registros

CREATE TABLE produtos (

  cod_prod   integer UNIQUE,
  nome       text,
  preco      numeric

);


CREATE TABLE produtos (

  cod_prod   integer,
  nome       text,
  preco      numeric,
  UNIQUE (cod_prod)

);


CREATE TABLE exemplo (

  a integer,
b integer,
c integer,
UNIQUE (a, c)

);

CREATE TABLE produtos (

  cod_prod   integer CONSTRAINT unq_cod_prod UNIQUE,
nome text,
preco numeric

);

Evitando duplicação com nulos:
create table teste(
id serial not null,
parent integer null,
component integer not null
);

postgres=# create unique index naoduplic on teste using btree (component) where (parent is null);
Chaves Primárias (Primary Key)
A chave primária de uma tabela é formada internamente pela combinação das constraints UNIQUE e NOT NULL. Uma tabela pode ter no máximo uma chave primária. A teoria de bancos de dados relacional dita que toda tabela deve ter uma chave primária. O PostgreSQL não obriga que uma tabela tenha chave primária, mas é recomendável, a não ser que esteja criando uma tabela para importar de outra que contenha registros duplicados para tratamento futuro ou algo parecido ou apenas para testes.
CREATE TABLE produtos (

  cod_prod   integer UNIQUE NOT NULL,
nome text,
preco numeric

);

CREATE TABLE produtos (

  cod_prod   integer PRIMARY KEY,
nome text,
preco numeric

);

Composta (formada por mais de um campo)

CREATE TABLE exemplo (

  a integer,
b integer,
c integer,
PRIMARY KEY (a, c)

);

Chave Estrangeira (Foreign Key)
Criadas com o objetivo de relacionar duas tabelas, mantendo a integridade referencial entre ambas. Especifica que o valor da coluna (ou grupo de colunas) deve corresponder a algum valor existente em um registro da outra tabela. Normalmente queremos que na tabela estrangeira existam somente registros que tenham um registro relacionado na tabela principal. Como também controla a remoção de registros na tabela principal que tenha registros relacionados na estrangeira.
Tabela principal

CREATE TABLE produtos (

  cod_prod   integer PRIMARY KEY,
nome text,
preco numeric

);

Tabela referenciada
CREATE TABLE pedidos (

  cod_pedido  integer PRIMARY KEY,
cod_prod integer,
quantidade integer,
CONSTRAINT pedidos_fk FOREIGN KEY (cod_prod) REFERENCES produtos (cod_prod)

);

CREATE TABLE t0 (

a integer PRIMARY KEY,
b integer,
c integer,
FOREIGN KEY (b, c) REFERENCES outra_tabela - - a coluna de destino será a PK

);

CREATE TABLE t1 (

a integer PRIMARY KEY,
b integer,
c integer,
FOREIGN KEY (b, c) REFERENCES outra_tabela (c1, c2)

);

Obs.: O número de colunas e tipo na restrição devem ser semelhantes ao número e tipo das colunas referenciadas.

Simulando ENUM no PostgreSQL

Para simular a constraint enum do MySQL, podemos usar a constraint check.
Dica do site "PostgreSQL & PHP Tutorials".

CREATE TABLE pessoa(
codigo int null primary key,
cor_favorita varchar(255) not null,
check (cor_favorita IN ('vermelha', 'verde', 'azul'))
);

INSERT INTO pessoa (codigo, cor_favorita) values (1, 'vermelha'); -- OK
INSERT INTO pessoa (codigo, cor_favorita) values (1, 'amarela'); -- Erro, amarelo não consta

Herança
Podemos criar uma tabela que herda todos os campos de outra tabela existente.

CREATE TABLE cidades (

  nome        text,
populacao float,
altitude int -- (em pés)

);

CREATE TABLE capitais (

  estado      char(2)

) INHERITS (cidades);
capitais assim passa a ter também todos os campos da tabela cidades.

Segundo uma entrevista (vide DBFree Magazine No. 2) com a equipe de desenvolvimento do PostgreSQL, evite utilizar herança de tabelas.

Esquemas (Schema)
\dn – visualizar esquemas
Um banco de dados pode conter vários esquemas e dentro de cada um desses podemos criar várias tabelas. Ao invés de criar vários bancos de dados, criamos um e criamos esquemas dentro desse. Isso permite uma maior flexibilidade, pois uma única conexão ao banco permite acessar todos os esquemas e suas tabelas. Portanto devemos planejar bem para saber quantos bancos precisaremos, quantos esquemas em cada banco e quantas tabelas em cada esquema.
Cada banco ao ser criado traz um esquema public, que é onde ficam todas as tabelas, caso não seja criado outro esquema. Este esquema public não é padrão ANSI. Caso se pretenda ao portável devemos excluir este esquema public e criar outros. Por default todos os usuários criados tem privilégio CREATE e USAGE para o esquema public.
Criando Um Esquema
CREATE SCHEMA nomeesquema;

Excluindo Um Esquema
DROP SCHEMA nomeesquema;

Aqui, quando o esquema tem tabelas em seu interior, não é possível apagar dessa forma, temos que utilizar:

DROP SCHEMA nomeesquema CASCADE;
Que apaga o esquema e todas as suas tabelas, portanto muito cuidado.

Obs.: O padrão SQL exige que se especifique RESTRICT (default no PostgreSQL) OU CASCADE, mas nenhum SGBD segue esta recomendação.

Obs.: é recomendado ser explícito quanto aos campos a serem retornados, ao invés de usar * para todos, entrar com os nomes de todos os campos. Assim fica mais claro. Além do mais a consulta terá um melhor desempenho.


Acessando Tabelas Em Esquemas

SELECT * FROM nomeesquema.nometabela;


Privilégios Em Esquemas

\dp – visualizar permissões


REVOKE CREATE ON SCHEMA public FROM PUBLIC; - - Remove o privilégio CREATE de todos os usuários.

Obtendo Informações sobre os Esquemas:

\dn

\df current_schema*


SELECT current_schema();

SELECT current_schemas(true);

SELECT current_schemas(false);

Visões (views)

\dp – visualizar views e outros objetos

Que são VIEWS?

São uma maneira simples de executar e exibir dados selecionados de consultas complexas em bancos. Em que elas são úteis? Elas economizam grande quantidade de digitação e esforço e apresentam somente os dados que desejamos.

Criando Uma View

CREATE VIEW recent_shipments

       AS SELECT count(*) AS num_shipped, max(ship_date), title
          FROM shipments
          JOIN editions USING (isbn)
          NATURAL JOIN books AS b (book_id)
          GROUP BY b.title
          ORDER BY num_shipped DESC;


Usando Uma View

SELECT * FROM recent_shipments;

SELECT * FROM recent_shipments

       ORDER BY max DESC
       LIMIT 3;


Destruindo Uma View

DROP VIEW nomeview;


Criar as Tabelas que servirão de Base

CREATE TABLE client (

clientid SERIAL NOT NULL PRIMARY KEY,

clientname VARCHAR(255)

);


CREATE TABLE clientcontact (

contactid SERIAL NOT NULL PRIMARY KEY,

clientid int CONSTRAINT client_contact_check REFERENCES client(clientid),

name VARCHAR(255),

phone VARCHAR(255),

fax VARCHAR(255),

emailaddress VARCHAR(255)

);


CREATE VIEW client_contact_list AS

SELECT client.clientid, clientname, name, emailaddress FROM client, clientcontact

WHERE client.clientid = clientcontact.clientid;

Estando no psql e digitando \d podemos visualizar também as views.


O nome da visão deve ser distinto do nome de qualquer outra visão, tabela, seqüência ou índice no mesmo esquema. A visão não é materializada fisicamente. Em vez disso, a consulta é executada toda vez que a visão é referenciada em uma consulta. Fazer livre uso de visões é um aspecto chave de um bom projeto de banco de dados SQL.


As visões podem ser utilizadas em praticamente todos os lugares onde uma tabela real pode ser utilizada. Construir visões baseadas em visões não é raro. Atualmente, as visões são somente para leitura: o sistema não permite inserção, atualização ou exclusão em uma visão. É possível obter o efeito de uma visão atualizável criando regras que reescrevem as inserções, etc. na visão como ações apropriadas em outras tabelas. Para obter informações adicionais consulte o comando CREATE RULE.

CREATE VIEW vista AS SELECT 'Hello World';

é ruim por dois motivos: o nome padrão da coluna é ?column?, e o tipo de dado padrão da coluna é unknown. Se for desejado um literal cadeia de caracteres no resultado da visão deve ser utilizado algo como CREATE VIEW vista AS SELECT text 'Hello World' AS hello;

Veja capítulo 4 do Livro "Pratical PostgreSQL"

Supondo que uma consulta seja de particular interesse para uma aplicação, mas que não se deseja digitar esta consulta toda vez que for necessária, então é possível criar uma view baseada na consulta, atribuindo um nome a esta consulta pelo qual será possível referenciá-la como se fosse uma tabela comum.


CREATE VIEW minha_view AS

  SELECT cidade, temp_min, temp_max, prcp, data, localizacao
      FROM clima, cidades
      WHERE cidade = nome;

SELECT * FROM minha_visao;


Fazer livre uso de visões é um aspecto chave de um bom projeto de banco de dados SQL. As visões permitem encapsular, atrás de interfaces que não mudam, os detalhes da estrutura das tabelas, que podem mudar na medida em que as aplicações evoluem.


As visões podem ser utilizadas em praticamente todos os lugares onde uma tabela real pode ser utilizada. Construir visões baseadas em visões não é raro.


RULES

O comando CREATE RULE cria uma regra aplicada à tabela ou visão especificada.


Uma regra faz com que comandos adicionais sejam executados quando um determinado comando é executado em uma determinada tabela.


É importante perceber que a regra é, na realidade, um mecanismo de transformação de comando, ou uma macro de comando.


É possível criar a ilusão de uma visão atualizável definindo regras ON INSERT, ON UPDATE e ON DELETE, ou qualquer subconjunto destas que seja suficiente para as finalidades desejadas, para substituir as ações de atualização na visão por atualizações apropriadas em outras tabelas.


Existe algo a ser lembrado quando se tenta utilizar regras condicionais para atualização de visões: é obrigatório haver uma regra incondicional INSTEAD para cada ação que se deseja permitir na visão. Se a regra for condicional, ou não for INSTEAD, então o sistema continuará a rejeitar as tentativas de realizar a ação de atualização, porque acha que poderá acabar tentando realizar a ação sobre a tabela fictícia da visão em alguns casos.


banco=# \h create rule

Comando: CREATE RULE

Descrição: define uma nova regra de reescrita

Sintaxe:

CREATE [ OR REPLACE ] RULE nome AS ON evento

   TO tabela [ WHERE condição ]
   DO [ ALSO | INSTEAD ] { NOTHING | comando | ( comando ; comando ... ) }


O comando CREATE RULE cria uma regra aplicada à tabela ou visão especificada.

evento

Evento é um entre SELECT, INSERT, UPDATE e DELETE.


condição

Qualquer expressão condicional SQL (retornando boolean). A expressão condicional não pode fazer referência a nenhuma tabela, exceto NEW e OLD, e não pode conter funções de agregação.


INSTEAD

INSTEAD indica que os comandos devem ser executados em vez dos (instead of) comandos originais.

ALSO

ALSO indica que os comandos devem ser executados adicionalmente aos comandos originais. Se não for especificado nem ALSO nem INSTEAD, ALSO é o padrão.


comando

O comando ou comandos que compõem a ação da regra. Os comandos válidos são SELECT, INSERT, UPDATE, DELETE e NOTIFY.


Dentro da condição e do comando, os nomes especiais de tabela NEW e OLD podem ser usados para fazer referência aos valores na tabela referenciada. O NEW é válido nas regras ON INSERT e ON UPDATE, para fazer referência à nova linha sendo inserida ou atualizada. O OLD é válido nas regras ON UPDATE e ON DELETE, para fazer referência à linha existente sendo atualizada ou excluída.


Obs.: É necessário possuir o privilégio RULE na tabela para poder definir uma regra para a mesma.

Exemplos:

CREATE RULE me_notifique AS ON UPDATE TO datas DO ALSO NOTIFY datas;


CREATE RULE r1 AS ON INSERT TO TBL1 DO

(INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);


CREATE RULE "_RETURN" AS ON SELECT TO minha_visão DO INSTEAD

SELECT * FROM minha_tabela; -- Ao invés de selecionar da visão seleciona da tabela.

Banco de dados modelo intocado

Existe um modelo de banco de dados que sempre se preserva original, que é o template0. O template template1 pode incorporar objetos e acaba algumas vezes ficando inviável seu uso como modelo. Quando isso acontece podemos substitui-lo com uma cópia do template0.


Criando banco de dados baseado em outro modelo

CREATE DATABSASE nomebanco TEMPLATE template0;

createdb -T template0 nomebanco


Recriando o template1

\c testes

postgres=# UPDATE pg_database SET datistemplate=false WHERE datname='template1';

testes=# DROP DATABASE template1;

testes=# CREATE DATABASE template1 TEMPLATE template0 ENCODING 'latin1';

testes=# \c template1

template1=# VACUUM FULL FREEZE;

template1=# VACUUM FULL;

template1=# UPDATE pg_database SET datistemplate=true WHERE datname='template1';

Agora temos um template1 original e limpo.