Consultoria e Treinamento em Bancos de Dados Relacionais - PDF
Download "Consultoria e Treinamento em Bancos de Dados Relacionais"
Sophia Carlos Aleixo
1 Tabela de conteúdos CREATE TABLE AS...1 CREATE DOMAIN...1 Funções de dados...2 Funções de data e hora...2 Funções de agregação...3 Expressões condicionais...4 CASE...4 COALESCE...4 NULLIF...4 Controle de concorrência e Transações LOCK...5 CREATE FUNCTION...5 Funções SQL...6 Funções nas linguagens procedurais...7 PL/pgSQL - Linguagem procedural SQL...7 Estrutura da linguagem PL/pgSQL...7 Declarações...8 Aliases para parâmetros de função...8 Instruções básicas...9 Estruturas de controle...9 CREATE TRIGGER...13 CREATE TABLE AS CREATE [ [ GLOBAL LOCAL ] { TEMPORARY TEMP } ] TABLE nome_da_tabela [ (nome_da_coluna [,...] ) ] AS consulta O comando CREATE TABLE AS cria uma tabela e a carrega com dados computados pelo comando SELECT, ou por um comando EXECUTE que executa um comando SELECT preparado. As colunas da tabela possuem os nomes e tipos de dado associados às colunas da saída do comando SELECT (exceto que é possível mudar os nomes das colunas fornecendo uma lista explícita de novos nomes de colunas). CREATE DOMAIN CREATE DOMAIN nome [AS] tipo_de_dado [ DEFAULT expressão ] [ restrição [... ] ] onde restrição é: [ CONSTRAINT nome_da_restrição ] { NOT NULL NULL CHECK (expressão) } O comando CREATE DOMAIN cria um domínio de dados. O usuário que cria o domínio se 1 de 14
2 torna o seu dono. Exemplo: CREATE DOMAIN cod_nacao char(2) NOT NULL; CREATE TABLE tbl_nacao (id integer, nacao cod_nacao); Funções de dados Funções de data e hora Função Tipo retornado Descrição Exemplo Resultado age(timestamp) interval Subtrai de hoje age(timestamp ' ') age(timestamp, timestamp) interval Subtrai os argumentos current_date date Data de hoje current_time time with time zone current_timestamp timestamp with time zone date_part(text, timestamp) date_part(text, interval) date_trunc(text, timestamp) extract(campo timestamp) extract(campo interval) from from double precision double precision Hora do dia Data e hora Retorna subcampo (equivale extract) Retorna subcampo ao timestamp Trunca na precisão especificada double precision double precision Retorna subcampo Retorna subcampo isfinite(timestamp) boolean Testa carimbo de hora finito (diferente de infinito) 43 years 8 mons 3 days 43 years 9 mons 27 days 20 date_part('month', interval '2 years 3 months') :00:00 age(' ', timestamp ' ') date_part('hour', timestamp ' :38:40') date_trunc('hour', timestamp ' :38:40') extract(hour from timestamp ' :38:40') 20 extract(month from interval '2 years 3 months') 3 isfinite(timestamp ' :28:30') True 2 de 14
3 Função Tipo retornado Descrição Exemplo Resultado isfinite(interval) boolean Testa intervalo finito now() timestamp with time zone Data e hora corrente (equivale ao current_timestam p) timeofday() text Data e hora corrente isfinite(interval '4 hours') true Funções de agregação Função Tipo do argumento Tipo retornado Descrição avg(expressão) min(expressão) smallint, integer, bigint, real, double precision, numeric ou interval qualquer tipo de dado numérico, cadeia de caracteres, data ou hora numeric para qualquer argumento de tipo inteiro, double precision para argumento de tipo ponto flutuante, caso contrário o mesmo tipo de dado do argumento o mesmo tipo de dado do argumento a média (média aritmética) de todos os valores de entrada valor mínimo da expressão entre todos os valores de entrada count(expressão) qualquer um bigint número de valores de entrada para os quais o valor da expressão não é nulo max(expressão) qualquer tipo de dado numérico, cadeia de caracteres, data ou hora o mesmo tipo de dado do argumento valor máximo da expressão entre todos os valores de entrada 3 de 14
4 Função Tipo do argumento Tipo retornado Descrição sum(expressão) smallint, integer, bigint, real, double precision, numeric ou interval bigint para argumentos smallint ou integer, numeric para argumentos bigint, double precision para argumentos de ponto flutuante, caso contrário o mesmo tipo de dado do argumento somatório da expressão para todos os valores de entrada Expressões condicionais CASE A expressão CASE do SQL é uma expressão condicional genérica, semelhante às declarações if/else de outras linguagens: CASE WHEN condição THEN resultado [WHEN...] [ELSE resultado] END COALESCE COALESCE(valor [,...]) A função COALESCE retorna o primeiro de seus argumentos que não for nulo. Só retorna nulo quando todos os seus argumentos são nulos. Geralmente é útil para substituir o valor padrão para mostrar valores nulos quando os dados são usados para exibição. Por exemplo: SELECT COALESCE(descrição, descrição_curta, '(nenhuma)')... Como a expressão CASE, a função COALESCE não processa argumentos que não são necessários para determinar o resultado; ou seja, os argumentos à direita do primeiro argumento que não for nulo não são avaliados. NULLIF NULLIF(valor1, valor2) A função NULLIF retorna o valor nulo se, e somente se, valor1 e valor2 forem iguais. Senão, retorna valor1. Pode ser utilizado para realizar a operação inversa do exemplo para COALESCE mostrado acima: SELECT NULLIF(valor, '(nenhuma)')... 4 de 14
5 Controle de concorrência e Transações [ WORK TRANSACTION ] O comando inicia um bloco de transação, ou seja, todos os comandos após o são executados em uma única transação, até ser encontrado um COMMIT ou ROLLBACK explícito. Por padrão (sem o ), o PostgreSQL executa as transações no modo de autoefetivação (autocommit), ou seja, cada comando é executado em sua própria transação e uma efetivação (commit) é realizada implicitamente no fim do comando, se a execução for bemsucedida, senão a transação é desfeita (rollback). LOCK LOCK [ TABLE ] nome [,...] [ IN modo_de_bloqueio MODE ] onde modo_de_bloqueio é um entre: ACCESS SHARE ROW SHARE ROW EXCLUSIVE SHARE UPDATE EXCLUSIVE SHARE SHARE ROW EXCLUSIVE EXCLUSIVE ACCESS EXCLUSIVE O comando LOCK TABLE obtém um bloqueio no nível de tabela aguardando, quando necessário, pela liberação de qualquer bloqueio conflitante. Uma vez obtido, o bloqueio é mantido pelo restante da transação corrente (Não existe o comando UNLOCK TABLE; os bloqueios são sempre liberados no final da transação). CREATE FUNCTION CREATE [ OR REPLACE ] FUNCTION nome ( [ tipo_do_argumento [,...] ] ) RETURNS tipo_retornado { LANGUAGE nome_da_linguagem IMMUTABLE STABLE VOLATILE CALLED ON NULL INPUT RETURNS NULL ON NULL INPUT STRICT [EXTERNAL] SECURITY INVOKER [EXTERNAL] SECURITY DEFINER AS 'definição' AS 'arquivo_objeto', 'símbolo_de_vínculação' }... [ WITH ( atributo [,...] ) ] O comando CREATE FUNCTION cria uma função. O comando CREATE OR REPLACE FUNCTION cria uma função, ou substitui uma função existente. Exemplos: Somar dois números inteiros CREATE FUNCTION soma(integer, integer) RETURNS integer AS 'select $1 + $2;' LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT; Somar dias a uma data A seguir está mostrada uma função sobrecarregada para somar dias a uma data. Pode ser utilizada com os tipos de dado date, timestamp e timestamp with time zone. CREATE OR REPLACE FUNCTION soma_dias(date,integer) RETURNS date AS ' 5 de 14
6 DECLARE nova_data date; nova_data := $1 + $2; RETURN nova_data; END; ' LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION soma_dias(timestamp,integer) RETURNS timestamp AS ' DECLARE nova_data timestamp; hora interval; hora := $1 - CAST($1 AS DATE); nova_data := CAST($1 AS DATE)+ $2; RETURN nova_data + hora; END; ' LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION soma_dias(timestamp with time zone,integer) RETURNS timestamp with time zone AS ' DECLARE nova_data timestamp with time zone; hora interval; hora := $1 - CAST($1 AS DATE); nova_data := CAST($1 AS DATE)+ $2; RETURN nova_data + hora; END; ' LANGUAGE plpgsql; Funções SQL As funções SQL executam uma lista arbitrária de declarações SQL, retornando o resultado da última consulta da lista. Se acontecer da última consulta não retornar nenhuma linha, é retornado o valor nulo. Abaixo está mostrada uma função mais útil, que pode ser utilizada para debitar de uma conta corrente no banco, retornando o saldo atualizado: CREATE TABLE conta_corrente { numero_da_conta serial, saldo numeric }; CREATE FUNCTION debitar (integer, numeric) RETURNS numeric AS ' UPDATE conta_corrente SET saldo = saldo - $2 WHERE numero_da_conta = $1; 6 de 14
7 SELECT saldo FROM conta_corrente WHERE numero_da_conta = $1; ' LANGUAGE SQL; CREATE TABLE emp ( nome text, salario integer, idade integer, id serial ); CREATE FUNCTION dobrar_salario(emp) RETURNS integer AS ' SELECT $1.salario * 2 AS salario; ' LANGUAGE SQL; SELECT nome, dobrar_salario(emp) AS sonho FROM emp WHERE emp.id = 1; Também é possível construir uma função que retorna um tipo composto. Abaixo está mostrado como exemplo uma função que retorna uma única linha da tabela emp: CREATE FUNCTION ver_empregado() RETURNS emp AS ' SELECT nome, salario, idade, id from emp where id = $1; ' LANGUAGE SQL; Abaixo está mostrado um exemplo da extração de um atributo de um tipo linha: SELECT ( ver_empregado()).nome; Todas as funções SQL podem ser utilizadas na cláusula FROM da consulta, mas esta situação é particularmente útil no caso das funções que retornam tipos compostos. Se a função for definida como retornando um tipo base, a função de tabela produz uma tabela de uma coluna. Se a função for definida como retornando um tipo composto, a função de tabela produz uma coluna para cada atributo do tipo composto. Abaixo segue um exemplo: SELECT * FROM novo_empregado(1) AS t1; Quando uma função SQL é declarada como retornando SETOF algum_tipo, a consulta SELECT no final da função é executada até o fim, e cada linha produzida é retornada como um elemento do conjunto resultado. CREATE or replace FUNCTION ver_empregado2(int) RETURNS SETOF emp AS ' SELECT * FROM emp WHERE id = $1; ' LANGUAGE SQL; SELECT * FROM ver_empregado2(1) AS t1; Funções nas linguagens procedurais As linguagens procedurais não estão construídas dentro do servidor PostgreSQL; são oferecidas como módulos carregáveis. PL/pgSQL - Linguagem procedural SQL Estrutura da linguagem PL/pgSQL A linguagem PL/pgSQL é estruturada em blocos. O texto completo da definição da função 7 de 14
8 deve ser um bloco. Um bloco é definido como: [ <<rótulo>> ] [ DECLARE declarações ] END; Todas as declarações e dentro do bloco são terminadas por ponto-e-vírgula. Todas as palavras chave e identificadores podem ser escritos misturando letras maiúsculas e minúsculas. As letras dos identificadores são convertidas implicitamente em minúsculas a menos que estejam entre aspas. Existem dois tipos de comentários no PL/pgSQL. O hífen duplo (--) começa um comentário que se estende até o final da linha. O /* começa um bloco de comentário que se estende até a próxima ocorrência de */. Os blocos de comentários não podem ser aninhados, mas comentários de hífen duplo podem estar contidos em blocos de comentários, e os hífens duplos escondem os delimitadores de bloco de comentário /* e */. Declarações Todas as variáveis utilizadas em um bloco devem ser declaradas na seção de declarações do bloco (A única exceção é a variável de laço do FOR interagindo sobre um intervalo de valores inteiros, que é automaticamente declarada como do tipo inteiro). As variáveis da linguagem PL/pgSQL podem possuir qualquer tipo de dado da linguagem SQL, tais como integer, varchar e char. Abaixo seguem alguns exemplos de declaração de variáveis: id_usuario integer; quantidade numeric(5); url varchar; minha_linha nome_da_tabela%rowtype; meu_campo nome_da_tabela.nome_da_coluna%type; uma_linha RECORD; A sintaxe geral para declaração de variáveis é: nome [ CONSTANT ] tipo [ NOT NULL ] [ { DEFAULT := } expressão ]; Aliases para parâmetros de função nome ALIAS FOR $n; Os parâmetros passados para as funções recebem como nome os identificadores $1, $2, etc. Opcionalmente, podem ser declarados aliases para os nomes dos parâmetros $n para melhorar a legibilidade do código. Tanto o aliás quanto o identificador numérico podem ser utilizados para fazer referência ao valor do parâmetro. Alguns exemplos: CREATE FUNCTION taxa_de_venda(real) RETURNS real AS ' DECLARE subtotal ALIAS FOR $1; RETURN subtotal * 0.06; END; ' LANGUAGE plpgsql; 8 de 14
9 Instruções básicas 1. Atribuições A atribuição de um valor a uma variável, ou a um campo de linha ou de registro, é escrita da seguinte maneira: identificador := expressão; 2. SELECT INTO O resultado de um comando SELECT que retorna várias colunas (mas apenas uma linha) pode ser atribuído a uma variável tipo-registro, a uma variável tipo-linha, ou a uma lista de variáveis escalares. É feito através de SELECT INTO destino expressões_de_seleção FROM...;... DECLARE registro_usuario RECORD; nome_completo varchar; SELECT INTO registro_usuario * FROM usuarios WHERE id=1; IF registro_usuario.pagina IS NULL THEN -- não há idade cadastrada RETURN 'Sem página'; END; Estruturas de controle 1. RETURN RETURN expressão; RETURN com uma expressão termina a função e retorna o valor da expressão para quem chama. 2. Condicionais As IF permitem executar os comandos com base em certas condições. O PL/pgSQL possui quatro formas de IF: IF... THEN IF... THEN... ELSE IF... THEN... ELSE IF IF... THEN... ELSIF... THEN... ELSE IF-THEN IF expressão_booleana THEN As IF-THEN são a forma mais simples de IF. As entre o THEN e o END IF são executadas se a condição for verdade. Senão, são saltadas. Exemplo: IF v_id_usuario <> 0 THEN 9 de 14
10 UPDATE usuarios SET = v_ WHERE id_usuario = v_id_usuario; IF-THEN-ELSE IF expressão_booleana THEN ELSE As IF-THEN-ELSE ampliam o IF-THEN permitindo especificar um conjunto alternativo de a ser executado se a condição for avaliada como falso. Exemplos: IF id_pais IS NULL OR id_pais = '''' THEN RETURN nome_completo; ELSE RETURN hp_true_filename(id_pais) ''/'' nome_completo; IF v_contador > 0 THEN INSERT INTO contador_usuarios (contador) VALUES (v_contador); RETURN ''t''; ELSE RETURN ''f''; IF-THEN-ELSE IF As IF podem ser aninhadas, como no seguinte exemplo: IF linha_demo.sexo = ''m'' THEN sexo_extenso := ''masculino''; ELSE IF linha_demo.sexo = ''f'' THEN sexo_extenso := ''feminino''; Na verdade, quando esta forma é utilizada uma instrução IF está sendo aninhada dentro da parte ELSE da instrução IF externa. Portanto, é necessária uma instrução END IF para cada IF aninhado, mais um para o IF-ELSE pai. Embora funcione, se torna entediante quando existem muitas alternativas a serem verificadas. Por isso existe a próxima forma. IF-THEN-ELSIF-ELSE IF expressão_booleana THEN [ ELSIF expressão_booleana THEN [ ELSIF expressão_booleana THEN...]] [ ELSE 10 de 14
11 ] A instrução IF-THEN-ELSIF-ELSE fornece um método mais conveniente para verificar muitas alternativas em uma instrução. Formalmente é equivalente aos comandos IF-THEN- ELSE-IF-THEN aninhados, mas somente necessita de um END IF. Abaixo está um exemplo: IF number = 0 THEN result := ''zero''; ELSIF number > 0 THEN result := ''positivo''; ELSIF number < 0 THEN result := ''negativo''; ELSE -- a única outra possibilidade é que o número seja nulo result := ''nulo''; 3. Loop LOOP A instrução LOOP define um laço incondicional, repetido indefinidamente até ser terminado por uma instrução EXIT ou RETURN. Nos laços aninhados pode ser utilizado um rótulo opcional na instrução EXIT, para especificar o nível de aninhamento que deve ser terminado. LOOP -- algum processamento IF contador > 0 THEN EXIT; -- sair do laço LOOP -- algum processamento EXIT WHEN contador > 0; -- o mesmo do exemplo acima 4. WHILE WHILE expressão LOOP A instrução WHILE repete a seqüência de enquanto a expressão for avaliada como verdade. A condição é verificada logo antes de cada entrada no corpo do laço. Por exemplo: 11 de 14
12 WHILE quantia_devida > 0 AND saldo > 0 LOOP -- algum processamento WHILE NOT expressão_booleana LOOP -- algum processamento 5. FOR (variação inteira) FOR nome IN [ REVERSE ] expressão.. expressão LOOP Esta forma do FOR cria um laço que interage num intervalo de valores inteiros. A variável nome é definida automaticamente como do tipo integer, e somente existe dentro do laço. As expressões que fornecem os limites inferior e superior do intervalo são avaliadas somente uma vez, ao entrar no laço. Normalmente o passo da interação é 1, mas quando REVERSE é especificado se torna -1. Alguns exemplos de laços FOR inteiros: FOR i IN LOOP -- algum processamento RAISE NOTICE ''i is %'', i; FOR i IN REVERSE LOOP -- algum processamento Se o limite inferior for maior do que o limite superior (ou menor do que, no caso do REVERSE), o corpo do laço não é executado nenhuma vez. Nenhum erro é gerado. 6. Laços através dos resultados da consulta Utilizando um tipo diferente de laço FOR, é possível interagir através dos resultados de uma consulta e manipular os dados. A sintaxe é: FOR registro_ou_linha IN comando_select LOOP -- aqui Cada linha do resultado da consulta (um comando SELECT) é atribuída, sucessivamente, a variável tipo-registro ou tipo-linha, e o corpo do laço é executado para cada linha. CREATE TRIGGER CREATE TRIGGER nome { BEFORE AFTER } { evento [ OR... ] } ON tabela [ FOR [ EACH ] { ROW STATEMENT } ] 12 de 14
13 EXECUTE PROCEDURE nome_da_função ( argumentos ) O comando CREATE TRIGGER cria um gatilho. O gatilho fica associado à tabela especificada e executa a função especificada nome_da_função quando determinados eventos ocorrem. O gatilho pode ser especificado para disparar antes de tentar realizar a operação na linha (antes das restrições serem verificadas e o comando INSERT, UPDATE ou DELETE ser tentado), ou após a operação estar completa (após as restrições serem verificadas e o INSERT, UPDATE ou DELETE ter completado). Se o gatilho for disparado antes do evento, o gatilho pode evitar a operação para a linha corrente, ou modificar a linha sendo inserida (para as operações de INSERT e UPDATE somente). Se o gatilho for disparado após o evento, todas as mudanças, incluindo a última inserção, atualização ou exclusão, são "visíveis" para o gatilho. Se o gatilho estiver marcado como FOR EACH ROW então é chamado uma vez para cada linha modificada pela operação. Por exemplo, um comando DELETE afetando 10 linhas faz com que todos os gatilhos ON DELETE da relação de destino sejam chamados 10 vezes, uma vez para cada linha excluída. Por outro lado, um gatilho marcado como FOR EACH STATEMENT somente executa uma vez para uma determinada operação, a despeito de quantas linhas sejam modificadas; em particular, uma operação que não modifica nenhuma linha ainda assim resulta na execução de todos os gatilhos FOR EACH STATEMENT aplicáveis. CREATE TABLE emp ( nome_emp text, salario integer, ultima_data timestamp, ultimo_usuario text ); CREATE FUNCTION emp_carimbo() RETURNS trigger AS ' -- Verificar se o nome do empregado foi fornecido IF NEW.nome_emp IS NULL THEN RAISE EXCEPTION ''O nome do empregado não pode ser nulo''; IF NEW.salario IS NULL THEN RAISE EXCEPTION ''% não pode ter um salário nulo'', NEW.nome_emp; -- Quem é que paga para trabalhar? IF NEW.salario < 0 THEN RAISE EXCEPTION ''% não pode ter um salário negativo'', NEW.nome_emp; -- Registrar que alterou o pagamento e quando NEW.ultima_data := ''now''; NEW.ultimo_usuario := current_user; RETURN NEW; END; ' LANGUAGE plpgsql; 13 de 14
14 CREATE TRIGGER emp_carimbo BEFORE INSERT OR UPDATE ON emp FOR EACH ROW EXECUTE PROCEDURE emp_carimbo(); 14 de 14