António Rocha Nuno Melo e Castro ! Transact-SQL (T-SQL) Extensão ao SQL Permite controlo do fluxo de execução Permite comandos DDL (contrariamente ao PLSQL) T-SQL combina: poder de manipulação de dados do SQL com poder de processamento das linguagens procedimentais # $% #$ Variáveis Tipos de dados escalares Controlo do fluxo de execução Funções integradas Gestão de cursores Processamento de excepções Código armazenado na base de dados " ' % ( A unidade de programação é semelhante ao já visto para PL/SQL, podendo ter: Blocos de código sem nome – scripts. Normalmente são construídos dinamicamente e executados apenas uma vez. Blocos com nome Procedures e functions - são guardados na BD. Este blocos normalmente não são alterados depois de construídos e são executados várias vezes. Os subprogramas são executados explicitamente via uma chamada a um procedure ou function. Triggers: São também guardados na BD. Este blocos normalmente não são alterados depois de construídos e são executados várias vezes. Os Triggers são executados implicitamente quando acontecem determinados eventos na BD (Insert, Update e Delete) & ' % ( DECLARE --Definição de variáveis. BEGIN --Acções executáveis END Contrariamente ao PL/SQL, não existe propriamente o conceito de bloco. Um bloco em T/-SQL é um script. Os elementos BEGIN e END não são obrigatórios e delimitam o conjunto de acções a efectuar. Como não existe o conceito de bloco, DECLARE não define uma secção de declaração de variáveis. É opcional e é utilizada para definir objectos de T-SQL, tais como as variáveis. É possível também ter tratamento de excepções, e definir acções a tomar quando estas ocorrem. Contrariamente ao PL/SQL não é necessário terminar as instruções com ponto e vírgula. ) * + Syntax DECLARE DECLARE { @local_variable [AS] data_type, […n] } Declare Declare @DataNasc @DataNasc @idade @idade @nome @nome @Controlador @Controlador @valido @valido DATETIME, DATETIME, NUMERIC(2), NUMERIC(2), VARCHAR(50), VARCHAR(50), NUMERIC(2), NUMERIC(2), BIT BIT * + ,$ Contrariamente ao PL/SQL, o T-SQL não permite no momento da definição: Inicializar Ter defaults Constantes Definir tipos de dados com base no tipo de outras variáveis Definir tipos de dados com base na estrutura de uma tabela % -, % $( . % $ O T-SQL tem o conceito de variáveis globais: Não podem ser definidas pelo utilizador, são do sistema Distinguem-se das locais por terem no início os símbolos @@. Ex. SELECT @@VERSION Muito semelhante ao já visto para o PL/SQL: As instruções podem, se necessário, passar de uma linha para a outra, mas as palavras-chave não podem ser divididas. As unidades léxicas (identificadores, operadores, etc) podem ser separadas por um ou mais espaços ou por outros limitadores que não se confundam com a unidade léxica. Não se podem usar palavras reservadas como identificadores, excepto se entre aspas. Os identificadores têm que começar por uma letra e as variáveis locais por @. Os valores literais do tipo caracter ou data têm que ser indicados entre plicas. Os comentários podem ser incluídos entre os símbolos /* e */ quando o comentário engloba várias linhas, ou então após -– quando o comentário é apenas uma linha. / 0 $.-$% #$ + * / IS NULL, LIKE, BETWEEN, IN, =, >, <, <>, !=, ^=, <=, >= = + NOT AND OR Adição Subtracção Multiplicação Divisão Comparação Atribuição Concatenação Negação lógica Conjunção Disjunção % 1% $ ( Expressão ou lista ) Expressão ou lista ; Fim de instrução ‘ Cadeia de caracteres [] Identificador : Etiqueta -- Comentário /* Comentário */ Comentário * 1 % $ .2# $ Contrariamente ao PL/SQL o domínio de Y estende-se desde a sua definição até ao final. Não existindo o conceito de bloco, não é possível declarar variáveis com o mesmo nome dentro do mesmo script (mesmo estando declaradas antes de BEGIN … END) $ $ # 3 IF-ELSE IF condition1 statement1 ELSE IF condition2 statement2 Para colocar mais do que uma instrução dentro do IF, é ncessário usar o Begin / End IF condition1 begin statement11 statement12 end ELSE IF condition2 statement2 " 0 $ 1 $ # Keyword 4# 5 6 Description BEGIN...END Defines a statement block. BREAK Exits the innermost WHILE loop. GOTO label Continues processing at the statement following the label as defined by label. IF...ELSE Defines conditional, and optionally, alternate execution when a condition is FALSE. RETURN Exits unconditionally. WHILE Repeats statements while a specific condition is TRUE. Other Transact-SQL statements that can be used with control-of-flow language statements are: /*...*/ (Comment) EXECUTE -- (Comment) PRINT CASE RAISERROR DECLARE local_variable & # DECLARE @VNome @VPosto BEGIN SELECT FROM WHERE END % . %! , $ , % -, % $ varchar(50), varchar(50) @VNome = Nome, @ VPosto = Posto Empregado NumEmp = 5 Caso a query retorne mais do que um registo as variáveis ficam com o valor do último registo seleccionado (não dá erro). Este comando é feito no PL/SQL com o INTO. ) $ Os registos retornados por uma instrução SQL podem ser manipulados através de um cursor. Um cursor é semelhante a uma lista de valores, sendo possível percorrer e posicionar em qualquer um dos registos. Os cursores são semelhantes aos já vistos para o PL/SQL. No entanto o T-SQL tem muito menos possibilidades de tratamento. $ $ # $$ % # O processo típico da utilização de um cursor é : 1. Declarar variáveis para guardar os valores retornados pelo cursor 2. Declarar um cursor com o comando Declare e associar-lhe um Select 3. Usar o comando Open para executar o Select e abrir o cursor 4. Utilizar instruções de posicionamento do cursor - Fetch 5. Fechar o cursor com a instrução Close, para libertar a memória dos resultados do cursor, e possivelmente utilizar a instrução Deallocate se não for necessário usar novamente o cursor. @@Fetch_Status - Permite testar o status do fetch, se diferente de zero, ocorreu um erro. $ 7$ $% #% 1 Utilizar instruções de posicionamento do cursor Fetch First - Avança para o primeiro registo do cursor. Fetch Next - Avança para o registo seguinte. Fetch Prior - Avança para o registo anterior. Fetch Last - Avança para o último registo do cursor. Fetch Absolute n - Avança para posição n do cursor. Fetch Relative n - Avança n registos a partir do registo actual do cursor. Para utilizar os comandos de fetch (à excepção do Next) é necessário declarar um cursor do tipo SCROLL. $ $8 9 Syntax básica: DECLARE cursor_name CURSOR [FORWARD_ONLY | SCROLL] [LOCAL | GLOBAL] FOR select_statement [FOR UPDATE [OF column_name [,...n]]] Para abrir OPEN nome_cursor Buscar valores FETCH NEXT FROM nome_cursor INTO lista_variaveis Para fechar CLOSE nome_cursor Para retirar o Declare DEALLOCATE nome_cursor / $ 91 DECLARE Cursor_Cli CURSOR FOR SELECT ClienteID, Nome FROM Clientes Declare @Cli int, @Nome varchar(50) OPEN Cursor_Cli FETCH NEXT FROM Cursor_Cli INTO @Cli, @Nome WHILE @@FETCH_STATUS = 0 BEGIN PRINT @Cli PRINT @Nome FETCH NEXT FROM Cursor_Cli INTO @Cli, @Nome END CLOSE Cursor_Cli DEALLOCATE Cursor_Cli + % . $ # $ $% 1 #% $ Atributos de cursores implícitos: retornam informação sobre o resultado da última instrução INSERT, DELETE, UPDATE ou SELECT executada. São acedidos através de variáveis globais: @@ROWCOUNT retorna o número de registos afectados por uma instrução INSERT, DELETE, UPDATE ou SELECT @@ERROR o número do erro da última instrução executada, ou zero se não existir. # Sintaxe básica: CREATE PROC[EDURE] procedure_name [;number] [ {@parameter data_type} [VARYING] [= default] [OUTPUT] ] [,...n] AS sql_statement [...n] # 91 CREATE PROC Cli @CodCli int=NULL, @InicioNome Varchar(40) AS BEGIN SELECT * FROM CLIENTES WHERE ClienteID > @CodCli AND Nome LIKE (@InicioNome+'%') END Para invocar a procedure EXEC Cli 2, 'A' OU EXEC Cli @InicioNome = 'A', @CodCli=2 " # 91 ALTER PROC Cli @CodCli int, @InicioNome Varchar(40), @TotalReg int OUTPUT AS BEGIN SELECT @TotalReg = COUNT(*) FROM CLIENTES WHERE ClienteID > @CodCli AND Nome LIKE (@InicioNome+'%') END Para invocar a procedure DECLARE @Tot int EXEC Cli 1, 'A', @TotalReg = @Tot OUTPUT PRINT 'Numero de registos = ' + cast(@Tot AS Varchar(10)) & 3 #% $ A utilização de funções é vantajosa em determinadas circunstâncias : Podem ser utilizados para encapsular e centralizar a lógica das regras de negócio. Podem ser utilizadas numa query, contrariamente as Stored procedures e podem ser passados parâmetros, contrariamente às Views. Nos casos em que o resultado é uma tabela, esta pode ser usado na cláusula FROM como uma tabela normal. Nas situações em que o resultado é um único valor, as funções podem ser usadas na cláusula SELECT como uma função normal do SQL. Pode ser usada, por exemplo, para cálculos de valores ou formatação. ) 3 #% $ No T-SQL existem 3 tipos de funções: 1. Scalar Functions - São funções que retornam unicamente um valor. Exemplo: CREATE FUNCTION QtStock (@Qt Smallint) RETURNS Smallint AS BEGIN IF @QT<0 SET @Qt = 0 RETURN @Qt END Para invocar : SELECT dbo.QtStock(-3) 3 #% $ 2. Inline Table - São funções que têm como resultado uma tabela de valores que é resultado de uma única instrução de select. Exemplo: CREATE FUNCTION AnivMes (@Mes Tinyint) RETURNS TABLE AS RETURN (SELECT ClienteID, Nome FROM Clientes WHERE Month(DataNasc) = @Mes) Para invocar: SELECT Nome From AnivMes(1) 3 #% $ 3. Multi-statement Table - São funções que retornam tabelas definidas e trabalhadas pelo utilizador . Exemplo: CREATE FUNCTION ArtVendasAno (@Ano Int) RETURNS @VAno TABLE (ArtigoID int, Descricao Nvarchar(40), Vendas Int) AS BEGIN INSERT @VAno SELECT ArtigoID, Descricao, (Select SUM(Quant) FROM Facts, linhasFact FROM Artigos RETURN END Para invocar: WHERE LinhasFact.ArtigoID = Artigos.ArtigoID AND LinhasFact.FactID = Facts.FactID AND @Ano = YEAR(Data)) SELECT * FROM ArtVendasAno(2006) WHERE Vendas > 1