quarta-feira, junho 13, 2007

Prepared Statements no MySQL.

Esses tempos tinha uma pergunta pipocando em uma lista sobre MySQL onde uma pessoa queria fazer alguma coisa em um procedimento (procedure) e não conseguia. A solução que eu encontrei para ajudar a pessoa foi Prepared Statements.

Para ilustrar o seu uso criei uma tabela:

CREATE TABLE PEDIDO (
  CODPEDIDO  INT(3) ZEROFILL NOT NULL,
  CODCLIENTE INT(3) ZEROFILL NOT NULL,
  VALOR      DOUBLE          NOT NULL,
  STATUS     CHAR(1)         NOT NULL
);

Depois inseri dados:

INSERT INTO Pedido(CodPedido, CodCliente, Valor, Status) VALUES(1, 1, 99.87, 'E');
INSERT INTO Pedido(CodPedido, CodCliente, Valor, Status) VALUES(2, 2, 99.87, 'A');
INSERT INTO Pedido(CodPedido, CodCliente, Valor, Status) VALUES(3, 3, 99.87, 'A');
INSERT INTO Pedido(CodPedido, CodCliente, Valor, Status) VALUES(4, 2, 99.87, 'S');
INSERT INTO Pedido(CodPedido, CodCliente, Valor, Status) VALUES(5, 2, 99.87, 'A');
INSERT INTO Pedido(CodPedido, CodCliente, Valor, Status) VALUES(6, 3, 99.87, 'E');

O Status seria: E – Encerrado, A – Aberto, S – Suspenso.

Depois pode se criar o Prepared Statements, eu fiz dentro de um procedimento, mas poderia ser feito direto no console do MySQL que funcionaria.

Criando o procedimento (procedure):

CREATE PROCEDURE P_PEDIDOS_STATUS(IN STATUS CHAR(1))
BEGIN

  SET @StatusPedido = STATUS;

  PREPARE psmtp FROM 'SELECT CodPedido, CodCliente, Valor, Status FROM Pedido WHERE Status = (?) GROUP BY CodCliente ORDER BY CodPedido';
  EXECUTE psmtp USING @StatusPedido;

  DEALLOCATE PREPARE psmtp;

END;

Bom a finalidade aqui é selecionar todos os pedidos por status, para isso passamos por parâmetro o status que desejamos procurar:

Resultados:

mysql> call P_PEDIDOS_STATUS('E');
+-----------+------------+-------+--------+
| CodPedido | CodCliente | Valor | Status |
+-----------+------------+-------+--------+
|       001 |        001 | 99.87 | E      |
|       006 |        003 | 99.87 | E      |
+-----------+------------+-------+--------+
2 rows in set (0.00 sec)

Query OK, 0 rows affected (0.02 sec)

mysql> call P_PEDIDOS_STATUS('A');
+-----------+------------+-------+--------+
| CodPedido | CodCliente | Valor | Status |
+-----------+------------+-------+--------+
|       002 |        002 | 99.87 | A      |
|       003 |        003 | 99.87 | A      |
+-----------+------------+-------+--------+
2 rows in set (0.00 sec)

Query OK, 0 rows affected (0.02 sec)

mysql> call P_PEDIDOS_STATUS('S');
+-----------+------------+-------+--------+
| CodPedido | CodCliente | Valor | Status |
+-----------+------------+-------+--------+
|       004 |        002 | 99.87 | S      |
+-----------+------------+-------+--------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.02 sec)

Agora, e se quisermos ter um procedimento maluco que receba toda a minha query e a execute? Sei lá qual a finalidade disto (agora não me ocorre nada em mente que justifique, mas vai que você tem um relatório que muda muito pouco de uma query – por exemplo só o where ou o order by -  e pode generalizar a chamada passando a query ou parte dela? Sei lá!) então poderiamos “injetar” para o procedimento todo o comando a ser executado no SGBD:

CREATE PROCEDURE P_INJETA_SQL(IN SQLCOMMAND VARCHAR(500))
BEGIN

  SET @Command = SQLCOMMAND;

  PREPARE psmtp FROM @Command;
  EXECUTE psmtp;

  DEALLOCATE PREPARE psmtp;

END;

Teste:

mysql> CALL P_INJETA_SQL('SELECT CodPedido, CodCliente, Valor, Status FROM Pedido WHERE Status = ''E'' GROUP BY CodCliente ORDER BY CodPedido');
+-----------+------------+-------+--------+
| CodPedido | CodCliente | Valor | Status |
+-----------+------------+-------+--------+
|       001 |        001 | 99.87 | E      |
|       006 |        003 | 99.87 | E      |
+-----------+------------+-------+--------+
2 rows in set (0.00 sec)

Query OK, 0 rows affected (0.02 sec)

Beleza, afunfa!

Bom de asas a sua imaginação agora e qualquer coisa comenta aí! :D

2 comentários:

Lucas Renan disse...

boaaa...

eu uso prepared statements para fazer paginação de dados usando stored procedures, passo o "limit" via parâmetro..

mas na verdade ainda não sei qual a vantagem de prepared statement

Jean Michel disse...

Obrigado pela visita ao blog e pelo comentário.

Vantagem, não sei se é a palavra correta... mas diversas vezes montamos nossos comandos SQL na aplicação e queremos executalos, está é uma maneira :)

Abraço.