Sistemas de Base de Dados



Documentos relacionados
Comandos de Manipulação

Regras de Integridade. Profa. Késsia Marchi

PostgreSQL Performance

Tarefa Orientada 19 Triggers

BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar

Consistem num conjunto de apontadores para instâncias especificas de cada relação.

Linguagem SQL Sub-linguagem DDL

Tarefa Orientada 12 Junção Externa, Auto-Junção e União

Tarefa Orientada 16 Vistas

1. Domínio dos Atributos

Programação SQL. Introdução

Banco de Dados I. Aula 12 - Prof. Bruno Moreno 04/10/2011

Roteiro 9 - SQL Básico: chave estrangeira, operadores de comparação e operadores booleanos

Tarefa Orientada 15 Manipulação de dados

APOSTILA BANCO DE DADOS INTRODUÇÃO A LINGUAGEM SQL

Comandos DDL. id_modulo = id_m odulo

SQL comando SELECT. SELECT [DISTINCT] <campos> FROM <tabela> [condição] [ ; ] Paulo Damico - MDK Informática Ltda.

Introdução à Banco de Dados. Nathalia Sautchuk Patrício

Tarefa Orientada 14 Subconsultas

Triggers em PostgreSQL. Linguagem de Programação de Banco de Dados. Triggers em PostgreSQL. Triggers em PostgreSQL

Junções e Índices em Tabelas

SQL (Structured Query Language)

Faculdade Pitágoras 16/08/2011. Curso Superior de Tecnologia: Banco de Dados Sistemas para Internet

Banco de Dados. Prof. Antonio

Programação SQL. Manipulação de Dados. DML Data Manipulation Language

SQL Linguagem de Definição de Dados. Laboratório de Bases de Dados Profa. Dra. Cristina Dutra de Aguiar Ciferri

Faculdade Pitágoras. Curso Superior de Tecnologia: Banco de Dados. Disciplina: Banco de Dados Prof.: Fernando Hadad Zaidan SQL

Hugo Pedro Proença, 2007

NOME SEXO CPF NASCIMENTO SALARIO

SQL Linguagem de Manipulação de Dados. Banco de Dados Profa. Dra. Cristina Dutra de Aguiar Ciferri

Banco de Dados Profa. Dra. Cristina Dutra de Aguiar Ciferri. Banco de Dados Processamento e Otimização de Consultas

BANCO DE DADOS. info 3º ano. Prof. Diemesleno Souza Carvalho

Bases de Dados. Lab 1: Introdução ao ambiente

Bases de Dados 2005/2006. Aula 5

SQL Structured Query Language

SQL TGD/JMB 1. Projecto de Bases de Dados. Linguagem SQL

Transações Seguras em Bancos de Dados (MySQL)

Structured Query Language (SQL) Ambiente Simplificado de um SGBD

UNIVERSIDADE FEDERAL DE SANTA MARIA - UFSM COLÉGIO AGRÍCOLA DE FREDERICO WESTPHALEN BANCO DE DADOS II

O que são Bancos de Dados?

A linguagem SQL

SQL Linguagem de Definição de Dados. Banco de Dados Profa. Dra. Cristina Dutra de Aguiar Ciferri

SQL - Criação de Tabelas

Triggers. um trigger permite que uma determinada sequência de comandos SQL seja accionada quando um determinado evento ocorre.

Prof. Daniela Barreiro Claro

Linguagem de Consulta Estruturada SQL- DML

Integridade dos Dados

Banco de Dados I Módulo V: Indexação em Banco de Dados. (Aulas 4) Clodis Boscarioli

trigger insert, delete, update

PHP INTEGRAÇÃO COM MYSQL PARTE 1

Tabela de Símbolos. Análise Semântica A Tabela de Símbolos. Principais Operações. Estrutura da Tabela de Símbolos. Declarações 11/6/2008

Administração de Banco de Dados

Triggers e Regras. Fernando Lobo. Base de Dados, Universidade do Algarve

Princípio dos anos 70 IBM desenvolve a linguagem Sequel para o System R. Standards ISO e ANSI SQL-86, SQL-89, SQL-92, SQL:1999, SQL:2003

8. Outros tipos de Transação (Modo de Transação de Autoconfirmação e Modo Implícito)

Sistemas de Informação

Tarefa Orientada 13 Agrupamento e sumário de dados

ORACLE 11 G INTRODUÇÃO AO ORACLE, SQL,PL/SQL. Carga horária: 32 Horas

BASES DE DADOS I LTSI/2. Universidade da Beira Interior, Departamento de Informática Hugo Pedro Proença, 2010/2011

Álgebra Relacional. Conjunto de operações que usa uma ou duas relações como entrada e gera uma relação de saída. Operações básicas:

Exercícios de Lógica Exercícios de Fixação 08

Bases de Dados 2007/2008. Aula 9

Administração e Optimização de BDs

Banco de Dados. Marcio de Carvalho Victorino Exercícios SQL

EXEMPLOS DE COMANDOS NO SQL SERVER

SQL. Autor: Renata Viegas

Tarefa Orientada 11 Junção Interna

BANCO DE DADOS: SQL. Edson Anibal de Macedo Reis Batista. 27 de janeiro de 2010

Básico da Linguagem SQL. Definição de Esquemas em SQL. SQL(Structured Query Language)

A VISTA BACKSTAGE PRINCIPAIS OPÇÕES NO ECRÃ DE ACESSO

Modelo de Dados Relacional Restrições de um Banco de Dados Relacional

Estudo do Sistema de Gestão de Bases de Dados PostgreSQL. André Ricardo Carlos Nobre Cristiano Lopes Nº Nº Nº 17662


Bases de Dados 2012/2013 Restrições de Integridade em SQL. Helena Galhardas 2012 IST. Bibliografia

Bases de Dados. O ficheiro create-bank.sql contém um conjunto de instruções SQL para criar a base de dados de exemplo ilustrada na figura 1.

Tarefa Orientada 10 Obter informação a partir de uma tabela

Introdução à Engenharia da Computação. Banco de Dados Professor Machado

Introdução ao SQL. O que é SQL?

LINGUAGEM SQL. DML - Linguagem de Manipulação de Dados

Programação Orientada a Objetos com PHP & MySQL Sistema Gerenciador de Banco de Dados: Introdução e configuração de bases de dados com Postgre e MySQL

Structured Query Language (SQL)

Os dados no MySQL são armazenado em tabelas. Uma tabela é uma colecção de informação relacionada e consiste em colunas e linhas.

BD Oracle. Licenciatura em Engenharia Informática e Computação. Bases de Dados 2003/04

Bases de Dados 2007/2008. Aula 1. Referências

Iniciar o Data Adapter Configuration Wizard. Toolbox Data Duplo clique em OleDbDataAdapter. Botão next na caixa de diálogo

Possui como idéia central a divisão de um universo de dados a ser organizado em subconjuntos mais gerenciáveis.

Treinamento sobre SQL

SQL: Definição de tabelas, Modificações à Base de Dados

Controle de transações em SQL

Structured Query Language (SQL)

Banco de Dados. Um momento crucial na organização dos dados é a forma com que cadastramos estes dados, a estrutura de armazenamento que criamos.

Linguagem de Consulta Estruturada (SQL)

Prof.: Clayton Maciel Costa

4.6. SQL - Structured Query Language

SQL Server Triggers Aprenda a utilizar triggers em views e auditar as colunas atualizadas em uma tabela

Transcrição:

Sistemas de Base de Dados Análise do SGBD PostgreSQL Bruno Barão nº28061 Fábio Afonso nº30218 Hugo Fernandes nº28062

Índice de Conteúdos Índice de Conteúdos...ii Introdução... 1 Introdução Histórica... 1 Cobertura SQL... 2 Tipos de Dados...3 Tipos Numéricos...3 Tipo Monetário... 3 Tipos Carácter... 3 Tipos de Dados Binários... 3 Tipo Data/Hora... 4 Tipo Booleano... 4 Tipos Geométricos... 4 Tipo Endereço de Rede...5 Tipo UUID...5 Arrays... 5 Tipos Compostos...6 Tipo Identifcador de Objectos (OID)...6 Pseudo-Tipos...7 DML (Data Manipulation Language)...8 Select...8 Insert... 8 Update... 8 Delete... 9 DDL (Data Defnition Language)...10 Create Table... 10 Alter Table... 10 Drop Table... 11 Restrições... 11 Chave-Primária (Primary Key)... 11 Chave Estrangeira...12 Unique...12 Check... 13 Not-Null... 14 Herança...15 With...15 Triggers...15 Assertion... 16 Armazenamento e File Structure... 17 Database File Layout... 17 Toast (The Oversized-Attribute Storage Technique...19 Free Space Map (FSM)... 20 Visibility Map (VM)... 20 Database Page Layout...20 Indexação e Hashing... 22 Tipos de Índices... 24 B-tree...24 Hash... 24 GiST...24 Índices Multi-Coluna...25 ii

Índices e ORDER BY... 26 Combinação de Múltiplos Índices... 27 Unicidade...28 Índices em Expressões...29 Índices Parciais... 29 Classes de Operadores e Famílias de Operadores... 30 Análise de Uso dos Índices... 31 Processamento e Optimização de Perguntas...32 Representação Interna das Perguntas... 34 Reescrita de Perguntas... 35 Optimização e Planeamento...36 Transformação de Perguntas...36 Análise de Perguntas... 37 Geração de Planos... 38 Algoritmos de Junção...38 Modelo de Custo...39 Gestão de transacções e controlo de concorrência... 41 Savepoints...42 Lidar com a consistência...43 Isolamento das transacções... 43 Nível de Isolamento: Read Committed... 44 Nível de Isolamento: Serializable... 45 Locks...46 Locks ao nível da tabela... 46 Locks ao nível das linhas... 49 Deadlocks...49 Advisory Locks... 50 Suporte para Bases de Dados Distribuídas... 51 Outras características do sistema estudado...52 Stored Functions...52 Triggers... 52 Database Connectivity... 53 Suporte para XML...53 Ferramentas...54 Conclusão... 55 Bibliografa... 56 iii

Introdução Neste trabalho iremos abordar o sistema de gestão de bases de dados PostgreSQL, focando alguns pontos considerados essenciais no mesmo. Assim, é nosso objectivo contribuir positivamente para a compreensão dos seguintes temas: Cobertura do SQL, Armazenamento e File Structure, Indexação e Hashing, Processamento e Optimização de Perguntas, Gestão de Transacções e Controlo de Concorrência, Suporte para Bases de Dados Distribuídas e algumas outras características dos sistema em foco. De realçar, ainda, que escolhemos este SGBD, dada a sua aplicação no mercado e, pelo facto, de ser um sistema de código aberto. Introdução Histórica O PostgreSQL é um Sistema de Gestão de Bases de Dados de código aberto. O seu início aconteceu em 1986, na Universidade de Berkeley, na California, pela mão de Michael Stonebraker, líder do projecto. O PostgreSQL começou, no entanto, a ser pensado uns anos antes. Na verdade, este SGBD é uma evolução do Ingres. O Ingres, foi outro projecto liderado por Michael Stonebraker que ainda tentou a sua comercialização após abandonar, temporariamente, a universidade. Contudo, acabou por retomar a mesma e criar, então, o PostgreSQL, tendo em vista resolver problemas com o modelo anterior, o Ingres. Na verdade, o nome que resultou da evolução do Ingres foi Postgres e, só mais tarde, em 1996, se passou a chamar PostgreSQL, tendo como objectivo refectir a nova linguagem de consulta à base de dados, o SQL. A primeira versão do PostreSQL, a 6.0, foi disponibilizada em 1997 e, desde então, conta com milhares de contribuidores em todo o mundo. À data da realização deste trabalho, a versão mais actual do PostgreSQL é a 8.4.1. 1

Cobertura SQL O PostgresSQL, tal como a maioria dos sistemas de bases de dados, procede à implementação de um subconjunto estendido do SQL. Neste caso concreto é denominado de PL/pgSQL (Procedural Language/PostgreSQL Structured Query Language). Esta é baseada na linguagem PL/SQL da Oracle, no entanto nem sempre foi assim. Nos seus primórdios para interrogar e gerir a base de dados utilizava-se uma linguagem de nome POSTQUEL. Somente a partir de 1995 é que esta alteração surgiu pelas mãos de dois estudantes de doutoramento dos laboratórios da Stonebraker, Andrew Yu e Jolly Chen. Além de ser possível escrever consultas de SELECT, manipular objectos DDL e dados DML em PL/pgSQL, podemos ainda integrar a base de dados com outras linguagens procedurais tais como PL/TCL, PL/Perl ou PL/Python. Passamos a enunciar os tipos de dados suportados por este sistema de gestão de base de dados, bem como comandos disponíveis no PL/pgSQL. 2

Tipos de Dados São suportados todos os tipos básicos do SQL, no entanto neste SGBD foram ainda incluídos tipos e mecanismos mais elaborados, tais como vectores, funções e a noção de herança. Tipos Numéricos Tipo Monetário Tipos Carácter Tipos de Dados Binários 3

Tipo Data/Hora Tipo Booleano Tipos Geométricos 4

Tipo Endereço de Rede Tipo UUID Os Universally Unique Identifers ou Global Unique Identifers não são mais do que uma sequência de 128-bits que pretende identifcar um objecto de forma inequívoca. Poderá parecer uma redundância se tivermos em conta as sequências, mas estas apenas permitem a identifcação única exclusivamente na base de dados. Para tais sequências o PostgreSQL aceita a defnição de qualquer um deste tipo, equivalente, de sequências: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11 {a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11} a0eebc999c0b4ef8bb6d6bb9bd380a11 a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11 {a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11} Embora no seu core não tenha um algoritmo de geração de UUID s, a extensão contrib/uuid-ossp fornece essa capacidade. Arrays Podem ser defnidos vectores de qualquer tipo suportado por este SGBD ou criados pelo utilizador, no entanto não existe ainda suporte para vectores de domínios. Podemos criar este tipo de dados de duas formas distintas, como podemos ver a baixo, sendo que estas são equivalentes, pois o PostgreSQL trata todos os vectores adoptando a segunda defnição. 5

Sintaxe: field_name field_type ARRAY[array_length] ou field_name field_type ARRAY Tipos Compostos Representam a estrutura de uma linha ou um registo. Podemos utilizar este tipo de dados exactamente da mesma forma que um qualquer tipo nativo disponibilizado. Sintaxe: CREATE TYPE complex_name AS ( field_name field_type, field_name field_type ); Tipo Identifcador de Objectos (OID) Os identifcadores de objecto são utilizados internamente pelo PostgreSQL como chaves primárias para várias tabelas do sistema. Somente são associados a tabelas criadas pelo utilizador se, a quando da criação da tabela, a opção WITH OID seja explicitada, ou caso se encontre activa a variável de sistema DEFAULT_WITH_OIDS (BOOLEAN). Estes são defnidos como um inteiro de quatro bytes sem sinal, dessa forma não poderão ser considerados sufcientemente grandes para que seja possível garantir unicidade a todos os elementos de uma base de dados, ou mesmo a tabelas num domínio relativamente grande, assim sendo apenas deverão ser utilizados para identifcar componentes do sistema. 6

Pseudo-Tipos O PostgreSQL contém um certo número de entradas especiais que são colectivamente chamadas de pseudo-tipos. Estes podem ser utilizados para declarar um argumento de uma função ou o seu tipo de retorno. Cada um destes tipos de dados disponíveis tornam-se bastante úteis em situações onde o comportamento de uma função não corresponde a simplesmente aceitar ou devolver um valor de um determinado tipo de dados SQL. 7

DML (Data Manipulation Language) A linguagem de manipulação de dados do SQL inclui comandos para seleccionar, actualizar, inserir e apagar registos. Select Retorna linhas de zero ou mais tabelas Sintaxe: SELECT [ALL DISTINCT [ON (expression [,...])]]* expression [[AS] output_name] [,...] [FROM from_item [,...]] [WHERE condition] [GROUP BY expression [,...]] [HAVING condition [,...]] Insert Insere novos registos numa tabela. Caso sejam especifcados por valor irão ser inseridas uma ou mais linhas, se estes advirem de uma consulta poderão ser inseridas várias linhas ou nenhuma, dependendo do resultado desta. Sintaxe: INSERT INTO table [(column [,...])] {DEFAULT VALUES VALUES ({xpression DEFAULT} [,...]) [,...] query} [RETURNING * output_expression [[AS] output_name] [,...]] Update Altera os valores de todas as linhas da coluna indicada que satisfaçam a condição enunciada. Sintaxe: UPDATE [ONLY] table [[AS] alias] SET {column = {expression DEFAULT} (column [,...]) = ({expression DEFAULT}[, ])} [, ] [FROM fromlist] [WHERE condition WHERE CURRENT OF cursor_name] [RETURNING * output_expression [[AS] output_name] [,...]] 8

Delete Remove os tuplos da tabela enunciada que satisfaçam a cláusula where. Caso esta seja omissa toda a tabela é apagada sendo retornada uma vazia. Sintaxe: DELETE FROM [ONLY] table [[AS] alias] [USING usinglist] [WHERE condition WHERE CURRENT OF cursor_name] [RETURNING * output_expression [[AS] output_name] [,...] ] 9

DDL (Data Defnition Language) De forma a ser possível a manipulação de tabelas, a linguagem de defnição de dados SQL possui as funções a baixo sucintamente descritas. Create Table Cria uma nova tabela vazia na base de dados. Esta tabela pode ainda ser temporária ou persistente. Caso se enquadre na segunda opção poderá ser criada segundo um schema pré-defnido pelo utilizador, ou utilizara o corrente caso nenhum seja explicitado. Já as tabelas temporárias são sempre criadas obedecendo a um schema especial. Sintaxe: CREATE [[GLOBAL LOCAL] {TEMPORARY TEMP}] TABLE table_name ([{column_name data_type [DEFAULT default_expr] [column_constraint [...]] table_constraint LIKE parent_table [{INCLUDING EXCLUDING} {DEFAULTS CONSTRAINTS INDEXES}]...} [,...]]) [INHERITS (parent_table [,...])] [WITH (storage_parameter [=value] [,...]) WITH OIDS WITHOUT OIDS] [ON COMMIT {PRESERVE ROWS DELETE ROWS DROP}] [TABLESPACE tablespace] Alter Table Modifca a forma como uma tabela existente na base de dados foi previamente defnida. Sintaxe: ALTER TABLE [ONLY] name [*] action [,... ] ALTER TABLE [ONLY] name [*] RENAME [COLUMN] column TO new_column ALTER TABLE name RENAME TO new_name ALTER TABLE name SET SCHEMA new_schema 10

Drop Table Procede à remoção de tabelas da base de dados. Apenas os seus donos as podem remover. Caso se deseje somente apagar as linhas da tabela deveremos utilizar a função DELETE ou TRUNCATE, sendo esta última uma extensão do PostgresSQL. Sintaxe: DROP TABLE [IF EXISTS] name [,...] [CASCADE RESTRICT] Restrições Chave-Primária (Primary Key) A restrição chave primária pode ser visto como a conjugação de duas outras, NOT NULL com UNIQUE, assim sendo esta não é mais do que uma especifcação que obriga a existência de valores únicos e não nulos numa coluna (ou colunas) de uma determinada tabela. Cada tabela somente pode ter uma chave primária, sendo indiferente que esta seja defnida como uma restrição de coluna ou de tabela. Passaremos a demonstrar as sintaxes utilizadas para criar uma chave primária a aquando da criação de uma tabela bem como ao alterar uma previamente existente. Na criação de uma tabela CREATE TABLE table_name (... field_name field_type PRIMARY KEY... ); Alteração de uma tabela existente ALTER TABLE table_name ADD CONSTRAINT constraint_name PRIMARY KEY (field_name); 11

Chave Estrangeira Uma Chave Estrangeira corresponde a uma ou mais colunas que servirá para estabelecer uma ligação entre dados de duas tabelas. Esta restrição é defnida a quando da criação de uma nova tabela, ou quando se procede a uma alteração numa existente. Uma chave estrangeira existe na tabela (T 2 ) quando a chave primária de uma tabela (T 1 ) é referenciada nesta. Será ainda criada uma restrição deste tipo se, em vez de ser referenciada a chave primária de T 1 sejam referenciadas colunas desta do tipo UNIQUE. Dependendo da forma de correspondência de padrões poderão existir valores nulos nestes campos. No caso de ser Match Full só existirão valores nulos numa coluna caso todos os valores das colunas da chave estrangeira sejam nulos. Caso seja Match Simple o critério é menos rígido, pois permite que algumas das colunas da chave estrangeira sejam nulas e outras sejam não nulas. Na criação de uma tabela CREATE TABLE table_name (... field_name field_type REFERENCES other_table_name (other_field_name)... ); Alteração de uma tabela existente ALTER TABLE table_name ADD CONSTRAINT constraint_name FOREIGN KEY (field) REFERENCES other_table_name(other_field_name); Unique A restrição UNIQUE especifca um grupo de uma ou mais colunas da tabela referida somente poderá conter valores únicos. O comportamento da restrição de tabela é exactamente igual ao de coluna, com a diferença de no primeiro pode abranger várias colunas simultaneamente. Para efeitos de uma restrição de unicidade os valores nulos não são considerados iguais. Cada restrição da tabela original deve nomear um conjunto de 12

colunas que é diferente do conjunto de colunas abrangido por uma restrição exclusiva ou outra chave primária defnida para a tabela. Caso tal não se verifcasse apenas teríamos a mesma restrição listada duas vezes. Na criação de uma tabela CREATE TABLE table_name(... field_name field_type,... UNIQUE (field_name) ); ou CREATE TABLE table_name(... field_name field_type CONSTRAINT constraint_name UNIQUE (field_name) ); Alterar uma tabela existente ALTER TABLE table_name ADD CONSTRAINT constraint_name UNIQUE (feld_name); Check A cláusula CHECK pode ser vista como uma função booleana que as novas linhas a serem inseridas na tabela têm de satisfazer de forma à operação ser bem sucedida. Somente as expressões que retornam FALSE não têm sucesso, assim sendo resultados como TRUE e UNKNOWN são vistos como aceitáveis. Uma restrição deste tipo especifcada para uma coluna apenas deve verifcar o valor desta, caso seja declarada a quando da criação da tabela poderá referenciar várias. Na actualidade expressões deste tipo não podem referir colunas de diferentes linhas nem conter sub-consultas. 13

Na criação de uma tabela CREATE TABLE table_name(... field_name field_type CHECK (logic_expression) ); ou CREATE TABLE table_name(... field_name field_type CONTRAINT constraint_name CHECK (logic_expression) ); Alterar uma tabela existente ALTER TABLE table_name ADD CONSTRAINT constraint_name CHECK (logic_expression); Not-Null A restrição de não nulidade obriga a que uma ou mais colunas não possam tomar valores nulos. Esta não é mais do que uma restrição de CHECK(feld_name NOT NULL), mas neste sistema de base de dados é bem mais efciente criar campos que não podem assumir valores nulos desta forma. Existe uma restrição inversa a esta denominada de NULL, isto não signifca que todos os elementos da tabela têm de ser nulos, mas sim que o seu valore por defeito é esse. Este tipo não está presente no SQL standard apenas foi adicionado ao PostgresSQL por uma questão de compatibilidade com outros sistemas de bases de dados, devido a isso não deve ser usados em sripts que assumam portabilidade. Ambas as restrições podem ser defnidas tanto na criação de uma nova tabela, mas somente a de não nulidade pode ser defnida na alteração de uma tabela existente. 14

Na criação de uma tabela CREATE TABLE table_name(... field_name field_type NOT NULL... ); e CREATE TABLE table_name(... field_name field_type NULL... ); Alterar uma tabela existente ALTER TABLE [ONLY] table_name [*] ALTER [COLUMN] column_name {SET DROP} NOT NULL Herança O mecanismo de herança múltipla é disponibilizado como uma extensão ao standard SQL. Este não é mais do que o mecanismo de herança singular, mas com diferente sintaxe. With Esta cláusula é uma extensão feita pelo sistema de gestão de base de dados ao SQL standard. Tal tornou-se necessário para, por exemplo, permitir algum controlo aos identifcadores de objectos gerados a quando da criação de uma tabela. Triggers Um TRIGGER é uma especifcação da base de dados que executa automaticamente uma determinada função sempre que um determinado tipo de operação é desencadeada. Este pode ser defnido para ser executado, uma vez por linha ou uma vez por 15

instrução SQL, antes ou depois de qualquer operação de INSERT, UPDATE ou DELETE. A função deste deve ser criada antes dele próprio, esta não recebe argumentos e retorna o tipo TRIGGER. Poderá ser escrita em qualquer linguagem procedural. Sintaxe: CREATE TRIGGER name {BEFORE AFTER} {event [OR...]} ON table [FOR [EACH] {ROW STATEMENT}] EXECUTE PROCEDURE function_name (arguments) Assertion O PostgresSQL não suporta a criação de asserções, no entanto é possível garantir a mesma integridade oferecida por estes mecanismos através das restrições a cima enunciadas. 16

Armazenamento e File Structure Database File Layout Começaremos por descrever o armazenamento ao nível de fcheiros e directorias. O SGBD PostgreSQL procede ao armazenamento dos seus dados em fcheiros assente no sistema de fcheiros gerido pelo sistema operativo anftrião. Os dados são guardados numa directoria denominada PGDATA, sendo que esta por omissão aponta para /var/lib/pgsql/data. Aqui serão encontrados todos os fcheiros e directorias necessários para o sistema armazenar e gerir os dados, bem como alguns fcheiros de confguração. Passa-mos então a explicitar o conteúdo da directoria PGDATA. Caso a base de dados seja portadora de clusters irá ser criado um subdirectório de nome PGDATA/BASE, herdando este o nome do OID da base de dados presente em PG_DATABASE. Cada tabela e índice são armazenados num fcheiro distinto, sendo que, ao excederem o tamanho de um gigabyte o PostgreSQL procede automaticamente à divisão deste em pedaços de tamanho mais reduzido. Enquanto o primeiro pedaço é baptizado com o nome do fcheiro (FILENODE), as restantes partes herdam o mesmo nome recebendo o número de partição após um ponto (FILENODE.N, de onde N é o número de partição do fcheiro que varia entre um e 17

número total de partições do fcheiro de nome FILENODE). Isto é necessário pois alguns sistemas não puderem suportar fcheiros de tamanho elevado. Qualquer tabela com atributos potencialmente grandes terá uma tabela TOAST associada. Esta possibilita o armazenamento de valores fora das linhas da tabela original (seguidamente tal será explicado em mais detalhe). Ficheiros temporários, como os usados para ordenar dados que não cabem em memória, são criados em PGDATA/BASE/PGSQL_TEMP ou dentro de uma subdirectoria sua. 18

Toast (The Oversized-Attribute Storage Technique O PostgreSQL usa um tamanho de página fxo, por norma de oito kilobytes, sendo que cada tuplo não pode ultrapassar uma página. Assim sendo, não é possível armazenar directamente linhas com atributos muito grandes. De forma a ser possível ultrapassar esta limitação o sistema comprime ou divide atributos grandes em enumeras linhas físicas. De notar ainda que este fenómeno acontece transparentemente para o utilizador. Esta técnica somente é utilizada quando o tamanho de um tuplo é superior a um quarto do tamanho do bloco. Nesses casos procedese à redução, por compressão dos campos, do tamanho da linha. Se tal falhar, ou seja, a compressão foi insufciente, o sistema procede ao armazenamento dos dados numa outra tabela (OUT_OF_LINE), sendo armazenada uma referência na tabela original. A aplicação destas técnicas é feita de forma cíclica até se obter o resultado desejado ou este seja o melhor possível. Tais características tornam este método bastante útil e vantajoso em relação com outras aproximações pois, ao ter um tamanho reduzido a tabela principal e os dados referenciados somente são acedidos após cada operação, pode-mos concluir que certas operações, tais como ordenações, serão facilitadas. 19

Free Space Map (FSM) Cada pilha e índice relacional, excepto os índices de hash, possuem um mapa de espaço livre de forma a terem a noção do restante espaço disponível. Este é guardado conjuntamente com os dados principais sob o nome FILENODE_FSM, sendo que o nome do fcheiro principal é FILENODE. O FSM encontra-se organizado numa estrutura de árvore. Enquanto os níveis superiores armazenam informação referentes aos níveis inferiores, o último nível guarda o restante espaço livre para cada pilha ou índice. Visibility Map (VM) Todas as pilhas relacionais possuem um mapa de visibilidade para terem a noção de quais os tuplos visíveis para cada transacção em cada instante. Esta encontra-se armazenada com a relação principal tendo o nome de FILENODE_VM, sendo o nome de fcheiro é FILENODE. De notar que os índices não são possuidores deste tipo de estruturas. O VM apenas guarda um bit para cada página da pilha. A defnição de um conjunto de bits signifca que todos os tuplos devem ser visíveis para todas as transacções, o mesmo é dizer que não é preciso proceder à aspiração (VACCUM) dos mesmos. Futuramente podemos ainda utilizar esta estrutura para evitar visitas à página com o intuito de fazer verifcações de visibilidade. No entanto, a exploração do mapa apenas no permite concluir que a condição é verdadeira caso o bit esteja defnido, caso contrario nada podemos concluir. Database Page Layout Finalmente iremos descrever a forma de armazenamento das tabelas e dos índices. De registar que tanto as tabelas TOAST como as de sequência seguem o mesmo formato. Cada fcheiro procederá ao armazenamento da informação 20

relativa a índices ou tabelas. No caso das tabelas será guardada informação referente às linhas, enquanto nos índices iremos guardar as suas entradas. Abstendo-nos das diferenças entre ambos vamos apelida-los de itens. O fcheiro é constituído por um vector de páginas, sendo que cada uma delas vai conter os itens a ser armazenados. Nos índices existe uma ligeira diferença, pois a primeira página encontra-se reservada para o armazenamento de informação respeitante aos dados de controlo deste. A tabela a baixo contém a informação correspondente às várias parcelas das páginas dos fcheiros de armazenamento. 21

Indexação e Hashing Numa base de dados, a criação de índices são uma forma de as bases de dados melhorarem a performance. Isto é, através dos índices é possível, numa tabela, encontrar, mais rapidamente, as linhas, que nos interessam. No entanto, a utilização deste recurso acrescenta overhead ao sistema de base de dados. Os índices numa base de dados assemelham-se aos índices remissivos dos livros. Nestes, os leitores podem navegar pelas diversas páginas, indo de encontro ao que procuram, sem terem que ler o livro todo. Já na base dados, signifca que o sistema não necessita de percorrer a tabela toda. Suponhamos ter a seguinte tabela: CREATE TABLE test1 ( id integer, content varchar ); Se a aplicação fzer muitas perguntas da seguinte forma: SELECT content FROM test1 WHERE id = constant; Sem nenhum tipo de preparação o sistema teria que percorrer a tabela test1, linha por linha, para encontrar todas entradas que satisfazem a pergunta. Se se der o caso de ser uma tabela com muitas linhas e em que apenas algumas (poderão até ser 0 ou 1) sejam retornadas pela pergunta, este método é, claramente, inefciente. Assim, para se criar um índice sobre a coluna id, deve-se usar o seguinte comando: CREATE INDEX test1_id_index ON test1 (id); 22

De modo análogo, para se remover um índice, o comando a usar é DROP INDEX. Uma vez criados, os índices não necessitam de intervenção uma vez que o sistema automaticamente os actualiza quando a tabela é modifcada e os usa em perguntas quando achar que as torna mais efcientes. Neste último caso o sistema usa estatísticas que lhe permitem tomar decisões fundamentadas (educated decisions). Deste modo, é útil que regularmente se corra o comando ANALYZE para actualizar as referidas estatísticas. Para além de aumentar a performance nas situações acima referidas, os índices podem ainda melhora-la nas instruções de UPDATE, DELETE ou mesmo de JOIN. Contudo, a criação de índices também tem desvantagens como a demora na criação dos mesmos sobre grandes tabelas. Por defeito, o PostgreSQL permite leituras (operação SELECT) em paralelo com a criação do índice. No entanto, bloqueia todas as operações de escrita (INSERT, UPDATE, DELETE) enquanto a indexação não termina. A juntar a isto, o facto de o sistema manter o índice sincronizado com a tabela acrescenta overhead às operações de manipulação dos dados. Assim, índices raramente ou nunca usados devem ser eliminados. 23

Tipos de Índices O PostgreSQL oferece quatro tipos diferentes de estruturas para indexação. São elas: B-tree, Hash, GiST e GIN. Cada um dos tipos usa diferentes algoritmos apropriados a distintos tipos de perguntas. B-tree Por defeito, o comando CREATE INDEX cria índices B-tree. Em particular, o PostgreSQL utiliza B-tree quando a coluna indexada está envolvida em comparações que utilizam os seguintes operadores: <, <=, = >=, >. Construções equivalentes como BETWEEN e IN poderão também usar este tipo de estrutura. É ainda possível possível utilizar B-tree com a condição IS NULL sobre a coluna a indexar. Hash Ao contrário das B-tree, os índices Hash só conseguem lidar com comparações de igualdade. Deste modo, poderá ser útil usar esta estrutura quando a coluna a indexar está envolvida em comparações que usem o operador =. Opostamente às B-tree, os índices Hash não suportam pesquisas IS NULL. Para se usar, explicitamente, a estrutura Hash terá que se fazer o seguinte comando: CREATE INDEX name ON table USING hash (column); GiST GiST (Generalized Search Tree) é uma estrutura em árvore usada para construir diferentes esquemas de indexação, como as árvores B e R. Na distribuição standard do PostgreSQL a classe GiST suporta perguntas que usem os seguintes operadores: <<, &<, &>, >>, <<, &<, &>, >>, @>, <@, ~= e &&. 24

GIN são índices invertidos que suportam valores com mais de uma chave (vectores, por exemplo). Como o GiST, os índices GIN suportam diferentes tipos de estratégias de indexação defnidas pelo utilizador. Índices Multi-Coluna Como o nome sugere, índices multi-coluna, são índices defnidos sobre mais do que uma coluna da tabela. Consideremos a seguinte tabela: CREATE TABLE test2 ( major int, minor int, content varchar ); Suponhamos, ainda, que recorrentemente fazemos perguntas como a seguinte: SELECT name FROM test2 WHERE major = constant AND minor = constant; Neste caso, poderá ser apropriado defnir um índice sobre as colunas major e minor: CREATE INDEX test2_mm_idx ON test2 (major, minor); Actualmente, apenas índices B-tree, GiST e GIN suportam indexação multi-coluna. O limite, por defeito, são 32 colunas, contudo, é possível alterá-lo. Os índices multi-coluna B-tree podem ser usados com perguntas que envolvam qualquer subconjunto das colunas indexadas mas o índice torna-se mais efciente quando existem restrições na coluna mais à esquerda. A regra exacta é que restrições de igualdade na coluna mais à esquerda, juntamente com restrições de desigualdade na primeira coluna que não tem restrição de igualdade, serão usadas para limitar a porção do índice que será 25

lido. Os índices multi-coluna GiST, à semelhança do anterior, também podem ser usados com perguntas que envolvam qualquer subconjunto das colunas indexadas. Condições em colunas adicionais restringem o número de tuplos retornados pelo índice mas a condição na primeira coluna é a mais importante para determinar quanto do índice necessita ser lido. Se a primeira coluna do índice tiver apenas alguns valores distintos, mesmo que nas outras colunas isto não se verifque, a utilização do GiST é algo inefcaz. Não fugindo à regra, os índices GIN também podem ser usados em perguntas que envolvam qualquer subconjunto das colunas indexadas. No entanto, ao contrário dos índices anteriores, a efcácia do mesmo não está dependente das colunas usadas nas restrições da querie. Índices e ORDER BY Por vezes necessitamos que o resultado de uma pergunta respeite uma determinada ordem. Assim, os índices deverão ter essa mesma capacidade. Isto poderá ser obtido usando a cláusula ORDER BY. Actualmente, no PostgreSQL, apenas os índices B-tree conseguem produzir um resultado de saída ordenado. Dependendo de alguns factores, o programador deverá considerar usar a cláusula ORDER BY num índice existente ou percorrer a tabela física e fazer uma ordenação explícita. Assim, numa pergunta que necessite ler uma grande parte da tabela, um ordenação explícita tenderá a ser mais rápida do que a utilização do índice porque requer menos I/O no disco. Os índices são mais úteis quando apenas algumas linhas precisam de ser utilizadas. Um exemplo importante é a utilização de ORDER BY com LIMIT n. Neste caso, numa ordenação explícita sobre a tabela seria necessário processar todas as linhas para identifcar as primeiras 26

n. Ao invés, se houver um índice que respeite a cláusula ORDER BY, será apenas necessário retornar as primeira n linhas sem termos que percorrer as restantes. Por defeito os índices B-tree guardam as entradas de forma ascendente. Ou seja, o índice assume o seguinte: ORDER BY x ASC NULLS LAST. Podem ainda ser percorridos de trás para a frente, o que corresponde a ter ORDER BY x DESC NULLS FIRST. É ainda possível ajustar a ordem de um índice B-tree, aquando da sua criação, usando as seguintes opções: ASC, DESC, NULLS FIRST e/ou NULLS LAST. Vejamos os seguintes exemplos: CREATE INDEX test2_info_nulls_low ON test2 (info, NULLS FIRST); CREATE INDEX test3_desc_index ON test3 (id DESC NULLS LAST); À partida todas estas opções poderão parecer redundantes quando consideramos índices de uma coluna. Contudo, se utilizarmos índices multi-coluna constatamos que não é assim. Consideremos o índice sobre duas colunas x e y. ORDER BY x, y poderá ser satisfeito se percorrermos o índice por ordem ou, caso percorramos inversamente, satisfazemos a condição ORDER BY DESC x, y. No entanto, poderemos querer percorrer x ascendentemente e y descendentemente e, nesse caso, teremos de tirar proveito das capacidades de ordenação fornecidas utilizando (x ASC, y DESC). Combinação de Múltiplos Índices Para a que seja feita uma única leitura no índice, só é possível usar clausulas nas perguntas que usem as colunas do índice com operadores da sua classe e juntos com AND. Por exemplo, se tivermos um índice (a, b), uma pergunta do tipo WHERE a = 5 AND b = 6 poderia usar o índice. Contudo, um pergunta como WHERE a = 5 OR b = 6 já não podia directamente usar o índice. Para lidar com situações em que não é possível satisfazer a pergunta com uma única leitura do índice, o PostgreSQL cria 27

condições de AND e OR sobre as várias leituras do índice. Por exemplo, uma pergunta do tipo WHERE x = 42 OR x = 47 OR x = 53 OR x = 99, poderá ser partida em quatro leituras distintas do índice, cada uma usando uma das cláusulas. O resultado fnal é calculado fazendo um OR sobre os quatro resultados intermédios obtidos. O procedimento é análogo para a intersecção (AND). Internamente, para realizar estas operações, o PostgreSQL lê cada índice que será necessário e prepara um bitmap em memória dando a localização das linhas da tabela que correspondem às condições do índice. Depois, são feitas operações de OR e AND conforme a pergunta pedida. Por fm, as linhas da tabela são visitadas e retornadas pela ordem com que são lidas. Ou seja, qualquer ordem dos índices originais é perdida e será necessário reordenar, no fm, caso a pergunta tenha a cláusula ORDER BY. Uma vez que estas operações poderão ser demoradas e consumir recursos importantes é necessário que o programador analise, por exemplo, se compensa utilizar esta técnica ou fazer uso dos índices multi-coluna. Por exemplo, se a maior parte das perguntas envolve apenas a coluna x, ou a coluna e por vezes as duas, talvez seja aconselhável criar dois índices separados (para x e y) e utilizar, quando necessário, a técnica acima descrita. Unicidade Os índices poderão ser usados para reforçar a unicidade de valores numa coluna ou a unicidade da combinação de valores de várias colunas. Para tal, utiliza-se a seguinte instrução: CREATE UNIQUE INDEX name ON table (column [, ]); Actualmente, apenas os índices B-tree suportam esta funcionalidade. O PostgreSQL cria automaticamente um índice deste tipo quando é declarado, na tabela, um campo como UNIQUE ou como chave primária. 28

Índices em Expressões Um índice não precisa apenas de ser uma coluna da tabela mãe. Poderá, também, ser uma expressão calculada a partir de uma ou mais colunas da tabela. Assim é possível aceder rapidamente a estes resultados já computados. Vejamos o exemplo que se segue: SELECT * FROM test1 WHERE lower(col1) = 'value'; Esta pergunta poderia usar um índice caso este já estivesse calculado: CREATE INDEX test1_lower_col1_idx ON test1 (lower(col1)); Para além disso, se defníssemos este índice como UNIQUE, estaríamos a garantir que não seriam criadas linhas cujos valores de col1 apenas diferissem no facto de serem maiúsculas ou minúsculas. Índices Parciais Um índice parcial, é um índice construído sobre apenas um subconjunto da tabela. Esse subconjunto é defnido por uma expressão condicional chamada predicado do índice parcial. Deste modo, o índice contém apenas entradas para as linhas da tabela que satisfazem esse predicado. Exemplo da criação de um índice parcial para excluir valores muito comuns: Consideremos a seguinte tabela: CREATE TABLE access_log ( url varchar, client_ip inet, ); 29

Criemos então o índice parcial: CREATE INDEX access_log_client_ip_ix ON access_log (client_ip) WHERE NOT (client_ip > inet '192.168.100.0' AND client_ip < inet '192.168.100.255'); Um possível exemplo de uma pergunta que usaria o índice é: SELECT * FROM access_log WHERE url = '/index.html' AND client_ip = inet '212.78.10.32'; Contudo, a seguinte pergunta não poderia usar o índice uma vez que o ip pedido foi excluído do índice aquando da sua criação: SELECT * FROM access_log WHERE client_ip = inet '192.168.100.23'; A criação deste tipo de índices também pode ser útil para seguintes situações: Excluir valores que não interessam. Isto é, valores que apesar de poderem estar em menor percentagem na tabela, são os mais acedidos. Quando queremos assegurar a unicidade. Suponhamos ter uma tabela que descreve os resultados de alguns testes e queremos assegurar que há apenas uma entrada que obteve sucesso dada a combinação do assunto e do objectivo apesar de haver inúmeras entradas sem sucesso. Classes de Operadores e Famílias de Operadores Ao criarmos um índice é possível defnirmos uma classe de operador para cada coluna de índice. Tal é feito da seguinte forma: CREATE INDEX name ON table (cloumn opclass [sort options] [, ]); As classes de operadores servem para modifcarmos o comportamento padrão adoptado pelo PostgreSQL aquando da 30

criação de um índice. Isto é, podemos querer que os valores objecto de comparação sejam comparados de uma ou outra maneira. Tomemos como exemplo a ordenação de um número complexo. Neste caso, poderemos querer ordenar pela parte inteira ou pela parte fraccionária. Análise de Uso dos Índices O PostgreSQL não exige manutenção dos índices. No entanto, é importante ir verifcando quais os índices realmente usados. Apesar de haverem algumas dicas possíveis de seguir, não existe nenhum procedimento correcto para determinar quais os índices a criar. 31

Processamento e Optimização de Perguntas Segundo Silberschatz, Korth e Sudarshan em Database System Concepts, a optimização de perguntas é o processo de selecção do plano mais efciente, de entre várias estratégias alternativas possíveis, para a execução de uma dada interrogação. Vejamos primeiro, em imagem, a sequência de actividades de processamento de perguntas no PostgreSQL. Como se vê na fgura, quando o PostgreSQL recebe uma nova pergunta procede à sua análise léxica, sintáctica e semântica. Durante a fase da análise léxica, é feita uma leitura sequencial dos caracteres que constituem a interrogação, separam-se os caracteres em palavras e o faz-se o reconhecimento dos vocábulos representados por cada palavra (ex.: cláusula SELECT, nome de uma coluna, etc.). Na fase de análise sintáctica verifca-se se a sintaxe da consulta respeita as regras sintácticas da linguagem (por exemplo, SQL). No decorrer desta fase é criada uma representação interna da consulta, através de um modelo em árvore. Com base nesta árvore, o sistema de reescrita verifca se existem 32

regras (guardadas em catálogos do sistema) aplicáveis a esta árvore. Caso existam, então a árvore é alterada de forma a refectir as alterações indicadas nas regras. Os utilizadores podem defnir regras cuja activação seja despoletada pelas operações: UPDATE, DELETE, INSERT e SELECT. As vistas encontram-se implementadas através de regras SELECT. Quando o SGBD recebe uma pergunta sobre uma vista, a regra SELECT correspondente é despoletada e a pergunta original é reescrita de acordo com a regra. O sistema de regras lida, em primeiro lugar, com as regras UPDATE, DELETE e INSERT, e só depois com as regras SELECT. Note-se que as operações de escrita também podem conter operações SELECT embebidas. Uma vez que após a reescrita de uma pergunta podem ser despoletadas novas regras, o sistema processa, iterativamente, o conjunto de regras até que mais nenhuma seja aplicável. Uma vez terminada a chamada fase de reescrita, a pergunta passa à fase de optimização e planeamento. O optimizador do PostgreSQL baseia-se, essencialmente, na estimativa do custo de execução para determinar o melhor plano. Inicialmente são gerados todos os caminhos que forneçam o mesmo resultado. Isto é, por exemplo, se existir um índice sob uma relação a pesquisar, existem duas possibilidades: o uso ou não do índice. De seguida o custo de cada plano é estimado e escolhe-se aquele que apresentar menor custo. Este é, então, expandido num plano completo. Existem dois algoritmos para a geração do plano óptimo: algoritmo de programação dinâmica, usado por omissão, e o algoritmo genético, usado sempre que o número de tabelas referenciadas numa operação SQL seja elevado. Mais à frente falaremos sobre eles. Com o resultado da fase de planeamento e optimização obtém-se o plano óptimo de execução da pergunta. 33

Representação Interna das Perguntas Como referido anteriormente o PostgreSQL representa, internamente, as perguntas através de um modelo em árvore. A árvore contém os seguintes nós principais: tipo do comando, lista das tabelas, tabela do resultado, lista das colunas, expressão de selecção, sub-árvore de junção. Analisemos, então, cada um destes nós: Tipo do comando identifca a operação SQL usada pelo utilizador e pode ter um dos seguintes valores: UPDATE, DELETE, INSERT e SELECT; Lista das tabelas contém uma lista de todas as tabelas usadas na pergunta. Numa operação SELECT, este nó contém todas as tabelas da cláusula FROM, cada uma representada por um número único; Tabela do resultado representa a tabela onde vai ser colocado o resultado; Lista das colunas contém uma lista de expressões que representa o resultado da pergunta. Por exemplo, quando é usada uma operação SELECT, o nó contém as colunas a partir das quais deve ser feita a projecção. Na operação INSERT, representa as expressões presentes na cláusula VALUES ou na cláusula SELECT. Na operação UPDATE, contém as expressões usadas para obter os novos valores dos atributos, envolvendo, apenas, constantes ou atributos da tabela do resultado. No caso da operação DELETE, este nó não é usado, uma vez que não são produzidos novos registos; Expressão de selecção representa o conteúdo da cláusula WHERE numa instrução SQL; Árvore de junção mostra a estrutura da cláusula FROM. Consideremos a seguinte pergunta: SELECT * FROM a,b,c,d. Neste caso, a árvore de junção teria apenas a lista de tabelas da cláusula FROM. 34

A fgura seguinte representa um exemplo da representação interna de uma pergunta: Reescrita de Perguntas O sistema de regras do PostgreSQL permite aos utilizadores defnir regras de reescrita de perguntas no servidor da base de dados. A sintaxe usada é a seguinte: CREATE RULE regra AS ON { SELECT INSERT UPDATE DELETE } TO tabela [ WHERE expressão_de_selecção ] DO [ INSTEAD ]{ NOTHING comando ( comando; comando )}; As vistas são implementadas sobre o sistema de regras. Assim, o seguinte comando: CREATE VIEW vista as SELECT * FROM tabela; 35

É transformado em: CREATE TABLE vista (lista das colunas da tabela) CREATE RULE _RETURN AS ON SELECT TO vista DO INSTEAD SELECT * FROM tabela; As regras SELECT são aplicadas como o último passo na transformação das perguntas, mesmo que o comando SQL usado seja: INSERT, UPDATE ou DELETE. Ao contrário das regras sobre operações de escrita, as SELECT modifcam a árvore da interrogação localmente, em vez de criarem uma nova. Optimização e Planeamento Como refere T. Lane em Recent PostgreSQL Optimizer Improvements, a tarefa de planeamento está dividida em três passos: transformação da pergunta, análise e geração de planos. No passo da transformação, a pergunta é convertida numa forma equivalente mais efciente. Durante a fase da análise extrai-se informação implícita sobre a pergunta. Terminado o préprocessamento, são analisados todos os planos alternativos de execução, sendo escolhido o plano de menor custo. Transformação de Perguntas O principal objectivo desta fase é converter as perguntas aninhadas em perguntas simples. A existência de perguntas aninhadas limita o número de ordens de junção possíveis. Considere-se a tabela bolsas(aid:string, montante:real), que guarda informação sobre os alunos que têm bolsas de estudo. Se pretendermos saber a que cadeiras estão inscritos os alunos com bolsas de estudo, podemos utilizar a seguinte pergunta: SELECT v_inscricoes.aid, v_inscricoes.nome, v_inscricoes.cid FROM v_inscricoes, bolsas WHERE v_inscricoes.aid = bolsas.aid; 36

Após a expansão da vista obtém-se a seguinte pergunta: SELECT v_inscricoes.aid, v_inscricoes.nome, v_inscricoes.cid FROM (SELECT a.aid, a.nome, i.cid FROM alunos a, inscricoes i WHERE a.aid = i.aid) v_inscricoes, bolsas; WHERE v_inscricoes.aid = bolsas.aid; Nesta forma não é possível considerar a junção entre a tabela bolsas e qualquer uma das tabelas da pergunta interior: alunos e inscricoes. Após a conversão, passa a ser possível considerar ordens de junção que envolvam as três tabelas: SELECT alunos.aid, alunos.nome, inscricoes.cid FROM alunos, inscricoes, bolsas WHERE inscricoes.aid = alunos.aid AND inscricoes.aid = bolsas.aid; Análise de Perguntas Na fase de análise de interrogações são realizadas, essencialmente, duas operações de optimização: dedução da igualdade implícita e a aplicação antecipada dos predicados (predicate push-down). Ao analisarmos as condições de igualdade na cláusula WHERE, conseguimos outras condições de igual implícitas. Vejamos o seguinte exemplo: SELECT alunos.aid, alunos.nome, inscricoes.cid FROM alunos, inscricoes, bolsas WHERE inscricoes.aid = alunos.aid AND inscricoes.aid = bolsas.aid; Com base nas duas condições de igualdade: inscricoes.aid = alunos.aid e inscricoes.aid = bolsas.aid, é possível deduzir uma terceira igualdade implícita: alunos.aid = bolsas.aid. Esta terceira igualdade permite juntar as tabelas alunos e bolsas primeiro. A aplicação antecipada dos predicados permite que as expressões de selecção sejam avaliadas tão cedo quanto possível. Esta regra de optimização é considerada uma heurística visto que normalmente, mas nem sempre, reduz o custo de execução do plano. 37

Geração de Planos Existem dois algoritmos para a geração do plano óptimo: algoritmo de programação dinâmica, usado por omissão, e o algoritmo genético, usado sempre que o número de tabelas referenciadas numa operação SQL seja elevado. O algoritmo de programação dinâmica caracteriza-se por, numa primeira fase, determinar os melhores planos de acesso às tabelas. De seguida, são encontrados os melhores planos para juntar grupos de duas tabelas do bloco da pergunta. Na fase seguinte, são encontradas as melhores estratégias para juntar grupos de três tabelas, com base nos melhores planos para juntar duas tabelas, determinados anteriormente. O algoritmo termina quando for encontrado o bom plano para executar o actual bloco da pergunta. É devido a todo este processo que quando o número de tabelas referenciadas é muito grande, o algoritmo se torna dispendioso. Uma alternativa ao algoritmo de programação dinâmica é o algoritmo genético (GenEtic Query Optimizer, GEQO, no PostgreSQL). Este algoritmo, inicialmente idealizado para resolver o conhecido problema do caixeiro viajante, pensa-se ser mais efcaz que o algoritmo de programação dinâmica quando o número de tabelas na cláusula FROM ronda as quarenta e cinco. Algoritmos de Junção O PostgreSQL implementa três algoritmos para a junção de tabelas. São eles: nested-loops-join, sort-merge-join e hash-join. Para efeitos explicativos, considere-se a junção da tabela A B. No algoritmo nested-loops-join a tabela B (interior) é percorrida na totalidade para cada registo da tabela A (exterior). Para cada par de registos é então verifcada se a condição imposta é satisfeita e caso tal aconteça, adiciona-se o registo composto ao resultado. Tal como o algoritmo de pesquisa linear, o nested-loops-join não depende da existência de índices e pode ser usado independentemente da condição de selecção. Uma vez que a relação interior é lida sempre que um novo registo é acedido, torna-se conveniente que seja a mais pequena das duas tabelas. 38

Caso a relação interior caiba totalmente em memória, necessita ser lida apenas uma vez. Se existir um índice sobre a relação interior, então as pesquisas lineares podem ser substituídas por pesquisas em índices. Para cada registo exterior, o índice é usado para encontrar os registos interiores que satisfaçam a condição de selecção. O algoritmo sort-merge-join pode ser usado em junções naturais e de igualdade. Considerando a junção natural entre duas tabelas A e B, se as tabelas já estiverem ordenadas segundo os atributos comuns, então o processo de junção assemelha-se ao merge do algoritmo merge-sort. Caso contrário, é necessário ordenar primeiro, utilizando o algoritmo merge-sort.a excepção acontece quando, por exemplo, existe um índice sobre os atributos de junção de A. Neste caso, não é necessário proceder à ordenação dos registos. O algoritmo hash-join, à semelhança do algoritmo sort-merge-join, é usado em junções naturais e de igualdade sendo uma boa opção quando não existem índices. Neste caso, o algoritmo, primeiramente, cria um índice hash, em memória, para a tabela interior (mais pequena). De seguida percorre a tabela exterior e para cada registo desta, os atributos de junção são usados para pesquisar o índice hash já criado. Modelo de Custo Como foi já referido anteriormente, o PostgreSQL, para determinar o plano óptimo, estima o custo de execução de cada plano gerado. Ora, a fórmula usada é a seguinte: C = P + W * R, onde C representa o custo total, P o número de páginas transferidas, que corresponde ao custo de I/O, R representa o número de registos examinados, que corresponde ao custo de processamento pelo CPU, e W indica o peso relativo entre o custo de I/O e o custo de processamento. As fórmulas de estimativa de custo para as estratégias de junção são função do tamanho, em termos de páginas e registos, das tabelas interior e exterior. Para estimar o tamanho de uma tabela, o 39

SGBD multiplica o tamanho original da tabela pelo factor de selectividade associado aos predicados. Este, representa a fracção dos registos, pertencentes ao produto cartesiano das tabelas envolvidas, que se espera satisfaçam a condição de junção. A informação estatística usada na estimativa de custo é actualizada, periodicamente, pelo SGBD e, manualmente, sempre que se execute a operação ANALYZE. Eis uma tabela de estimativa do custo de junções: Custo das Junções Nested-loops-join C ext + N ext * C int Sort-merge-join C ext + C ord_ext + C int + C ord_int Hash-join C ext + C const_hash + N ext * C hash Onde: C : custo para aceder à tabela exterior ext C : custo para aceder à tabela interior int C : custo para ordenar a tabela exterior (igual a zero, caso ord_ext já esteja ordenada) C : custo para ordenar a tabela interior (igual a zero, caso ord_int já esteja ordenada) C : custo para construir o índice hash sobre a tabela const_hash interior C : custo de uma pesquisa através do índice hash hash N : tamanho da tabela exterior ext 40

Gestão de transacções e controlo de concorrência O PostgreSQL garante a consistência dos dados usando um modelo MVCC (Multiversion Concurrency Control) oferecendo assim a cada transacção uma visão da base de dados como esta se encontrava num momento, do passado, independentemente do estado actual dos dados. Este sistema garante a todas as sessões isolamento uma vez que oferece a cada sessão uma snapshot da base de dados onde não é visível as alterações provocadas pelas outras sessões. A principal vantagem em utilizar transacções em vez de utilizar locks reside no facto de as escritas não bloquearem as leituras e as leituras não bloquearem as escritas. No entanto os locks são também disponibilizados para aplicações que não se adaptem facilmente ao modelo transaccional. No PostgreSQL as transacções estão defnidas implicitamente para cada instrução realizada, ou seja cada instrução tem autocommit. Mas para blocos de comandos é necessário explicitar que se quer realizar uma transacção. Isto faz-se ao preceder uma sequência de comandos SQL por BEGIN (ou START TRANSACTION), defnindo opcionalmente o modo da transacção. E ao colocar no fnal do bloco de comandos COMMIT. A sintaxe do BEGIN é a seguinte: BEGIN [ WORK TRANSACTION ] [ transaction_mode [,...] ] where transaction_mode is one of: ISOLATION LEVEL { SERIALIZABLE REPEATABLE READ READ COMMITTED READ UNCOMMITTED } READ WRITE READ ONLY 41

Um exemplo poderia ser: BEGIN; UPDATE accounts SET balance = balance - 100.00 WHERE name = 'Alice'; UPDATE branches SET balance = balance - 100.00 WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Alice'); UPDATE accounts SET balance = balance + 100.00 WHERE name = 'Bob'; UPDATE branches SET balance = balance + 100.00 WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Bob'); COMMIT; Savepoints É possível controlar com maior granularidade as instruções dentro de um bloco transaccional utilizando savepoints. Os savepoints permitem que se grave toda a transacção até um ponto, e é possível voltar a esse estado a qualquer momento (desde que se esteja ainda dentro do mesmo bloco) utilizando um ROLLBACK TO, que descarta todas as instruções executadas deste esse savepoint. Um savepoint depois de ser utilizado para rollback, continua defnido e pode ser reutilizado quantas vezes for necessário. O PostgreSQL não suporta nested transactions por defeito, pois ao iniciar uma transacção dentro de outra transacção o sistema retorna um warning, mas é possível simular as nested transactions utilizando os savepoints. Tudo isto é apenas válido dentro do bloco transaccional, por isso não é visível para as outras sessões na base de dados. Quando o commit é feito, as alterações fcam visíveis para as outras sessões, e por outro lado se for feito um rollback a todas as instruções, nenhuma alteração fca visível nas outras sessões. 42