Arquivo da categoria ‘Python’

Database – Triggers – Oracle – Python

Publicado: 20 de julho de 2014 em Python

Uma Trigger ou gatilho é um tipo especial de procedimento armazenado (Stored Procedure) que executa automaticamente quando um evento ocorre no servidor de banco de dados. Gatilhos DML executam quando um usuário tenta modificar dados através de uma linguagem de manipulação de dados (DML). Eventos DML são INSERT, UPDATE, ou DELETE em uma tabela ou exibição. Esses gatilhos disparam quando qualquer evento válido é acionado, independentemente de haver ou não linhas da tabela afetadas.

As Triggers possuem muitas diferenças de sintaxe e funcionalidades de um banco de dados para outro, por exemplo Trigger que dispara o evento INSTEAD OF INSERT (ao invés de inserir) no banco de dados Oracle só pode ser declarada sobre uma VIEW enquanto em MSSQL Server pode ser declarada em uma tabela comum, o evento BEFORE INSERT (antes de inserir) existe nos bancos de dados Oracle e IBM DB2, mas não existe no MSSQL. Por este motivo é recomendado que você procure os links oficiais para saber mais detalhes sobre as Triggers.

Trigger - Python - Oracle

Trigger – Python – Oracle

Algo extremamente útil sobre Triggers

As Triggers foram regulamentadas na revisão da língua SQL em 1999 e passou a ser padrão em banco de dados relacionais, no entanto alguns bancos de dados vem atualizando estes recursos regularmente, por exemplo o Oracle utiliza Triggers que disparam por esquema (Schema-level triggers) desde a sua versão 9i, enquanto o MSSQL suporta trigger do tipo DDL, ou logon trigger apenas desde a versão 2008.

Muitos sistemas de grande porte como SAP e vários outros não utilizam triggers, principalmente sistemas que no qual rodam em diversos bancos de dados, estas são consideradas verdadeiras armadilhas por muitos DBAs, podem ser facilmente esquecidas na hora de alterações e se tornarem invalidas (Oracle), gerando assim muitos erros caso não haja uma boa politica de desenvolvimento e qualidade (testes unitários e integrados) antes de colocar as rotinas do sistema em produção. Mesmo as IDEs de estudios SQL já mudaram varias vezes a localização do node de triggers ao longo de suas versões para tentar facilitar a manutenção das mesmas.

As Triggers também devem ser desenvolvidas com cuidado sempre seguindo as melhores praticas, pois o desenvolvimento SQL é muito abrangentes e cheio de recursos, então é fácil extrapolar e criar triggers contendo cursores, stored procedures, rollbacks, acessos repetitivos, outras triggers, lembre-se da lei de Murphy, tudo que não pode ser escrito em uma trigger, será e isto reduz sensivelmente a performance de um sistema, sem contar com a complexidade do desenvolvimento, transformado seu desenvolvimento SQL em verdadeiros planos da ACME, lembre-se do coite que tentava incessantemente capturar o papa-léguas.

Cuidado com as Triggers

Cuidado com as Triggers

Nunca use Triggers para criar campos auto incremento para os códigos (ID), os bancos de dados possuem recursos específicos para este trabalho, o uso de triggers somente criará acessos e UPDATES desnecessários no banco de dados, além de reduzir a performance, imagine importar 10.000 linhas utilizando um Insert…Select, à partir de uma outra tabela ou arquivos externos, você efetuara um Select e um Update para cada registro se utilizar triggers, enquanto os bancos de dados contam com seus campos auto incremento que já nos permitem utilizar dados em massa e inserir as 10.000 linhas em um único acesso.

As Triggers também são um alto risco a segurança, é possível escalar privilégios e se tornar proprietário do banco de dados e do servidor SQL caso você insira um código mal intencionado em uma Trigger, exemplo: suponha que o usuário Jõao da Silva tenha diretos de Administrador do banco de dados,  se um desenvolvedor mal intencionado criar uma trigger DDL sobre o evento DELETE de uma tabela qualquer utilizando por exemplo a instrução: GRANT CONTROL SERVER TO BadUser; Quando o usuário João da Silva executar o procedimento que dispara a trigger o Grant será delegado ao usuário mal intencionado e os direitos aplicados a ele, enquanto ele mesmo não teria acesso para delegar tal direito, deste modo possibilitando que o desenvolvedor mal intencionado tome conta do servidor SQL, podendo fazer o que bem entender com os dados.

Security: http://msdn.microsoft.com/pt-br/library/ms191134.aspx

Exemplo:

Neste exemplo criamos uma funcionalidade similar do nosso exemplo anterior (Cursor), utilizando a mesma modelagem de dados, porem para executar este procedimento as tabelas DESCONTO e SALARIO devem estar vazias, o usuário entrará com o código do funcionário e a porcentagem do desconto e a trigger sobre a tabela DESCONTO se encarregara de criar o lançamento na tabela de SALARIO.

SQL

Oracle

create table Funcionarios(
  ID_Funcionario  NUMBER(5),
  Nome            VARCHAR2(30),
  Sobrenome       VARCHAR2(70),
  Cargo           VARCHAR2(30),
  Salario         NUMBER(9,2));

-- Cria Funcionarios
Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);
Insert into FUNCIONARIOS values (3,'Wozniak','Gates','Desenvolvedor', 4389.21);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO NUMBER,
  PORCENTAGEM NUMBER(9,2));

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO NUMBER,
  DATA_LANC  DATE,
  VDESCONTO NUMBER(9,2));

-- Deleta dados antigos
-- Caso utlizou exemplos anteriores
delete from desconto;
delete from salario;

-- Cria trigger na tabela Desconto
create or replace TRIGGER DESCONTO_INSERT
   BEFORE INSERT ON DESCONTO
   FOR EACH ROW

   -- Declara variáveis
   DECLARE pID NUMBER;
           pSalario NUMBER(9,2);
           pPorcentagem NUMBER(9,2);
   BEGIN   

    -- Alimenta variáveis com os valores a serem inseridos
    pID := :new.ID_FUNCIONARIO;
    pPorcentagem := :new.PORCENTAGEM;

    -- Seleciona Salario do funcionario corrente
    Select SALARIO INTO pSalario FROM FUNCIONARIOS
    WHERE ID_FUNCIONARIO = pID;

    -- insere na tabela de lançamentos de salario
    Insert into SALARIO
     values (
             pID,
             SYSDATE,
             (pSalario * pPorcentagem)/100);

   END DESCONTO_INSERT;

Python

#!/usr/bin/env python
# -*- coding: latin-1 -*-
# Desenvolvimento Aberto
# TriggerOracle.py
 
# importa modulos
import wx
import wx.grid
import cx_Oracle
 
# Cria classe generica de uma WX.Grid
# A classe abaixo faz parte da documentação WXPython oficial
# Este trecho de código é util para manipular a grade
 
class GenericTable(wx.grid.PyGridTableBase):
    def __init__(self, data, rowLabels=None, colLabels=None):
        wx.grid.PyGridTableBase.__init__(self)
        self.data = data
        self.rowLabels = rowLabels
        self.colLabels = colLabels
 
    def GetNumberRows(self):
        return len(self.data)
 
    def GetNumberCols(self):
        return len(self.data[0])
 
    def GetColLabelValue(self, col):
        if self.colLabels:
            return self.colLabels[col]
 
    def GetRowLabelValue(self, row):
        if self.rowLabels:
            return self.rowLabels[row]
 
    def IsEmptyCell(self, row, col):
        return False
 
    def GetValue(self, row, col):
        return self.data[row][col]
 
    def SetValue(self, row, col, value):
        pass     


# Inicializa Grade
dados = []
colLabels  = []
rowLabels = ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")

# Cria conexão
def conectarORA():
    sconexao = "user/password@localhost/XE"
    try:
       con = cx_Oracle.connect(sconexao)
    except ValueError:
       tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela)
    return con

# Executa e retorna SQL
def retornaTabelaORA(sql, con):
     cursor = con.cursor()
     cursor.execute(sql)
     return cursor
        
def retornaDados(idfunc):
    # Cria conexão
    con = conectarORA()    
    # Envia dados a grid
    sql = "Select A.ID_FUNCIONARIO, " + "A.NOME, " + " A.CARGO, " + \
          " A.SALARIO, " + "B.PORCENTAGEM, " + "C.VDESCONTO, " + \
          "C.DATA_LANC,  " + "A.SALARIO - C.VDESCONTO AS SLIQUIDO " +\
          "from FUNCIONARIOS A, DESCONTO B, SALARIO C " + "Where " + \
          "A.ID_FUNCIONARIO = B.ID_FUNCIONARIO  AND " + \
          "A.ID_FUNCIONARIO = C.ID_FUNCIONARIO  AND " + \
          "A.ID_FUNCIONARIO = " + idfunc
    # retorna set de dados
    tabela = retornaTabelaORA(sql, con)

    # Retorna metadados da tabela
    for i in range(0, len(tabela.description)):
        colLabels.append(tabela.description[i][0])

    # Executa um fecth em todos os registros
    resultado = tabela.fetchall()

    # Popula dados
    for conteudo in resultado:
        dados.append(conteudo)

             

# Cria classe da grid
class SimpleGrid(wx.grid.Grid):
    def __init__(self, parent):
        wx.grid.Grid.__init__(self, parent, -1, pos=(5,90), size=(850,200))    
                         
 
# Cria formulario
class TestFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "Desenvolvimento Aberto - Triggers - Python", size=(900, 350))
        panel        = wx.Panel(self, wx.ID_ANY)
        label        = wx.StaticText(panel, -1, label='Oracle Database - Triggers ->', pos=(300,10))
        lfuncionario = wx.StaticText(panel, -1, label='Cod funcionario:', pos=(20,50))
        self.funcionario  = wx.TextCtrl(panel, size=(100, -1), pos=(120,50))
        lporcentagem = wx.StaticText(panel, -1, label='Porcentagem (%):', pos=(260,50))
        self.porcentagem  = wx.TextCtrl(panel, size=(100, -1), pos=(360,50))
        botao        =   wx.Button(panel, label="Inserir", pos=(480,50))
        botao.Bind(wx.EVT_BUTTON, self.botaoInserir)
        self.grid         = SimpleGrid(panel)

    #Insere dados e dispara trigger
    def botaoInserir(self,event):
        # Conecta e cria um cursor
        con = conectarORA()        
        cursor = con.cursor()
        # Insere clausula SQL
        sql = "insert into DESCONTO VALUES (" + self.funcionario.GetValue() + ", " + self.porcentagem.GetValue() + ")";
        # Executa e comita a transação
        cursor.execute(sql)
        con.commit()
        # Retorna set de dados
        retornaDados(self.funcionario.GetValue())
        # Limpa grade
        self.grid.ClearGrid()
        # Insere set de dados contidos em um tuplas
        tableBase = GenericTable(dados, rowLabels, colLabels)
        self.grid.SetTable(tableBase)
        # Atualiza grade
        self.grid.ForceRefresh()
        
        
# Inicializa a aplicação
app = wx.PySimpleApp()
frame = TestFrame(None)
frame.Show(True)
app.MainLoop()

Database – Cursor – Oracle – Python

Publicado: 7 de julho de 2014 em Python

A linguagem de banco de dados SQL permite que você retorne um resultado de dados completo de uma única vez, mas as vezes precisamos manipular este resultado em tempo de execução, uma linha de cada vez, deste modo podemos utilizar um recurso chamado Cursor para realizar este procedimento.

Basicamente todos os bancos de dados possuem este recurso, entretanto cada um deles possui também suas peculiaridades e funcionalidades diferenciadas, mas no geral funcionam da seguinte maneira: a instrução DECLARE CURSOR define os atributos de um cursor do servidor SQL, como o seu comportamento de rolagem e a consulta usada para construir o conjunto de resultados no qual o cursor funciona. A instrução OPEN popula o conjunto de resultados e FETCH retorna uma linha do conjunto de resultados. A instrução CLOSE libera o conjunto de resultados atual associado ao cursor. A declaração DEALLOCATE libera os recursos usados ​​pelo cursor.

Python - Oracle - Cursor

Python – Oracle – Cursor

Preparando o Banco de dados

Utilizaremos uma modelagem básica contendo três tabelas básicas sem chave primaria, sem chave estrangeira, permitido assim que nosso exemplo possua dados duplicados,  basicamente utilizaremos apenas a logica relacional para brincar com os dados utilizando uma Stored procedure contendo um cursor.

Relacionamentos

A tabela de FUNCIONARIOS contem dados de cadastro e o salario de cada funcionário, a tabela DESCONTO possui os valores de porcentagens de desconto para cada funcionário, e a tabela SALARIO contem os lançamentos de salários e deve ser alimentada mês a mês para cada funcionário.  Utilize os Scripts abaixo para criar as tabelas e os dados iniciais.

Algo Extremamente Útil  Sobre Cursores

Cursores são mais rápidos do que os looping efetuados dentro da linguagem de programação de sua preferencia, funcionam basicamente da mesma maneira, porem residem no motor do banco de dados dentro de uma Stored Procedure.

Os Cursores utilizam memoria e também necessitam de um laço para rolar registro a registro do resultado de dados e manipula-lo, é de extrema importância que você utilize cursores somente quando necessário. Caso queira que seu programa tenha uma ótima performance, antes de optar por um cursor é necessário saber que os motores dos bancos de dados são projetados para manipular dados em massa, manipulação de dados registro a registro devem ser utilizados somente quando não houver possibilidade de usar instruções SQL, para ilustrar esta explicação vamos escrever um script SQL que executa o mesmo procedimento do cursor utilizando apenas um acesso ao banco de dados, você encontra este script logo após o código Python.

Exemplo:

Neste exemplo criamos um cursor que reside dentro de uma Stored Procedure, calcula o desconto do salario dos funcionários e insere os valores líquidos em uma tabela de lançamento, após efetuar o procedimento que seria mensal, uma query exibe o relatório em uma grade de dados.

Cursor

O cursor seleciona dados da tabela de FUNCIONARIOS e da tabela de DESCONTO, através de um looping, alimenta os dados relevantes dentro de variáveis e insere os lançamentos na tabela de SALARIO efetuando o calculo do desconto.

SQL

Oracle

create table Funcionarios
(     

    ID_Funcionario  NUMBER(5),
    Nome            VARCHAR2(30),
    Sobrenome       VARCHAR2(70),
    Cargo           VARCHAR2(30),
    Salario         NUMBER(9,2)

);

Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO NUMBER,
  PORCENTAGEM NUMBER(9,2));

-- Insere porcentagem por  funcionario
Insert Into DESCONTO Values (1, 5 );
Insert Into DESCONTO Values (2, 8 );

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO NUMBER,
  DATA_LANC  DATE,
  VDESCONTO NUMBER(9,2));

-- Lista lançamentos por funcionario
select * from salario;

-- Desenvolvimento Aberto - Cursor explicito
create or replace Procedure CalculoDesconto  is
-- Declara cursor
Cursor calculo is
  Select A.ID_FUNCIONARIO, A.SALARIO, B.PORCENTAGEM
  from FUNCIONARIOS A, DESCONTO B
  Where
  A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

  -- Declara variáveis
  pID NUMBER;
  pSalario NUMBER(9,2);
  pPorcentagem NUMBER(9,2);

  -- Abre cursor
  begin
    open calculo;
      -- Cria laço
      loop
        -- Alimenta variáveis
        fetch calculo into pID, pSalario, pPorcentagem;
        EXIT WHEN calculo%NOTFOUND;

        -- Insere valores da tabela
        Insert into SALARIO
        values  (
                 pID,
                 SYSDATE,
                 (pSalario * pPorcentagem)/100);

      end loop;
    -- Fecha cursor
    close calculo;  

  end;

Python

#!/usr/bin/env python
# -*- coding: latin-1 -*-
# Desenvolvimento Aberto
# CursorOracle.py

# importa modulos
import wx
import wx.grid
import cx_Oracle

# Cria classe generica de uma WX.Grid
# A classe abaixo faz parte da documentação WXPython oficial
# Este trecho de código é util para manipular a grade

class GenericTable(wx.grid.PyGridTableBase):
    def __init__(self, data, rowLabels=None, colLabels=None):
        wx.grid.PyGridTableBase.__init__(self)
        self.data = data
        self.rowLabels = rowLabels
        self.colLabels = colLabels

    def GetNumberRows(self):
        return len(self.data)

    def GetNumberCols(self):
        return len(self.data[0])

    def GetColLabelValue(self, col):
        if self.colLabels:
            return self.colLabels[col]

    def GetRowLabelValue(self, row):
        if self.rowLabels:
            return self.rowLabels[row]

    def IsEmptyCell(self, row, col):
        return False

    def GetValue(self, row, col):
        return self.data[row][col]

    def SetValue(self, row, col, value):
        pass      

# Cria conexão Oracle
def conectarORA():
    sconexao = "user/passw0rd@localhost/XE"
    try:
        con = cx_Oracle.connect(sconexao)
    except ValueError:
        tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela)
    return con

# Cria conexão
con = conectarORA()

# Executa e retorna SQL
def retornaTabelaORA(sql, con):
    cursor = con.cursor()
    cursor.execute(sql)
    return cursor

# Inicializa Grade
dados = []
colLabels  = []
rowLabels = ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")   

# Executa Stored Procedure
proc = con.cursor()
proc.callproc("CALCULODESCONTO")
con.commit()

# Envia dados a grid
sql = "Select A.ID_FUNCIONARIO, " + "A.NOME, " + " A.CARGO, " + \
      " A.SALARIO, " + "B.PORCENTAGEM, " + "C.VDESCONTO, " + \
      "C.DATA_LANC,  " + "A.SALARIO - C.VDESCONTO AS SLIQUIDO " +\
      "from FUNCIONARIOS A, DESCONTO B, SALARIO C " + "Where " + \
      "A.ID_FUNCIONARIO = B.ID_FUNCIONARIO  AND " + \
      "A.ID_FUNCIONARIO = C.ID_FUNCIONARIO"

tabela = retornaTabelaORA(sql, con)

# Retorna metadados da tabela
for i in range(0, len(tabela.description)):
    colLabels.append(tabela.description[i][0])

# Executa um fecth em todos os registros
resultado = tabela.fetchall()

# Popula dados
for conteudo in resultado:
    dados.append(conteudo)

# Cria classe da grid
class SimpleGrid(wx.grid.Grid):
    def __init__(self, parent):
        wx.grid.Grid.__init__(self, parent, -1, pos=(5,60), size=(850,200))
        tableBase = GenericTable(dados, rowLabels, colLabels)
        self.SetTable(tableBase)                   

# Cria formulario
class TestFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "Desenvolvimento Aberto - Cursor - Python", size=(900, 350))
        panel = wx.Panel(self, wx.ID_ANY)
        label=wx.StaticText(panel, -1, label='Oracle Database - Stored Procedure -> Cursor', pos=(300,20))
        grid = SimpleGrid(panel)

# Inicializa a aplicação
app = wx.PySimpleApp()
frame = TestFrame(None)
frame.Show(True)
app.MainLoop()

SQL – A instrução a seguir, substitui o cursor em um único acesso. (Oracle)

-- Este script SQL efetua o mesmo procedimento do cursor
-- utilizando apenas um acesso ao banco de dados.
-- é importante projetar seu acesso a dados para obter alta performance
-- você deve utilizar cursores apenas quando realmente for necessário.
--

Insert into SALARIO
Select A.ID_FUNCIONARIO,SYSDATE, ((A.SALARIO * B.PORCENTAGEM)/100)
  from FUNCIONARIOS A, DESCONTO B
  Where
  A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

 

 

Stored Procedures são procedimentos de código SQL armazenados no banco de dados, as stored procedures são mais rápidas do que o código tradicional, o que significa que elas estão se tornando cada vez mais populares. Com código da aplicação se mudando dos programas externos para o motor de banco de dados, Os DBAs precisam entender os requisitos de memória relacionados para procedimentos armazenados e saber como gerenciar estes procedimentos para o ideal desempenho do banco de dados. Isto muda de banco para banco, cada um deles possui suas recomendações de melhores praticas para atingir um melhor desempenho.

Neste site existe uma versão deste mesmo programa utilizando código SQL direto dentro da aplicação e podemos comparar a diferença com o código SQL sendo executado dentro do motor do banco de dados e o resultado é visivelmente notado, sem duvida o código executado utilizando procedures é mais rápido.

Ainda existe um outro modo de utilizar o banco de dados que é utilizando um set de dados persistente, onde uma camada  de classes ou componentes geram o código SQL para o banco de dados através de instruções definidas pelas classes, mas este método por possuir uma camada que cria o código para o banco pode se tornar menos atrativo em termos de desempenho, apesar de ser muito mais fácil e rápido para se desenvolver, sendo assim vale avaliar a quantidade de dados que sua aplicação pretende manipular.

Para adquirir o script SQL para criar a tabela deste exemplo ou visualizar o método utilizando SQL direto da aplicação: clique aqui.

DB2 - Stored Procedure

DB2 – Stored Procedure

Exemplo:

Neste exemplo utilizamos Stored Procedures que retornam parâmetros para criar um método dentro do banco de dados ao invés de criar o método dentro da aplicação utilizando a linguagem de programação nativa.

IBM DB2

-- Cria procedure
CREATE PROCEDURE buscaFuncionario
( IN pid_funcionario INTEGER,
  OUT p_nome VARCHAR(30),
  OUT p_sobrenome VARCHAR(70),
  OUT p_cargo VARCHAR(30),
  OUT p_salario DECIMAL(9,2)
 )

P1: BEGIN

  SELECT NOME, SOBRENOME, CARGO, SALARIO
  INTO p_nome, p_sobrenome, p_cargo, p_salario
  FROM FUNCIONARIOS
  WHERE ID_FUNCIONARIO = pid_funcionario;

END P1

Python

#!/usr/bin/env python
# -*- coding: latin-1 -*-
# Desenvolvimento Aberto
# StoredDB2.py

# importa modulos
from Tkinter import *
import tkMessageBox
import ibm_db

# Cria formulario
formulario = Tk(className='Desenvolvimento Aberto')
formulario.geometry("350x280+300+300")

# Cria janela para menssagem
janela = Tk()
janela.wm_withdraw()

# Define banco de dados
# oracle = Oracle Database
DBconexao = "db2"

# Cria conexão com o banco de dados
def conectar(banco):
    # Cria string de conexão Oracle
    if (banco == "db2"):
        # Cria string de conexão IBM
        sconexao = "DATABASE=DEVA" +  \
                   ";HOSTNAME=localhost;PORT=50000;PROTOCOL=TCPIP;" + \
                   "UID=user;" + \
                   "PWD=p@55w0rd"
        try:
            con = ibm_db.connect(sconexao, "", "")
        except ValueError:
            tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela)

    return con

# Limpa campo
def limpar():
    tcodigo.delete(0, END)
    tpnome.delete(0, END)
    tsnome.delete(0, END)
    tcargo.delete(0, END)
    tsalario.delete(0, END)

# Evento do botão
def on_Pesquisar():
    # Exibe banco de dados
    titulo['text'] = "Stored Procedure: " + DBconexao

    # Cria conexão
    con = conectar(DBconexao)

    # Define e executa Stored Procedure
    if (DBconexao == "db2"):

        #Declara parametros
        pid = tpesquisa.get()
        pnome = ""
        psobrenome = ""
        pcargo = ""
        psalario = 0.0

        # Executa Stored Procedure
        stmt, pid, pnome, psobrenome, pcargo, psalario = ibm_db.callproc(con, "buscaFuncionario" , (pid, pnome, psobrenome, pcargo, psalario))

    # Prepara campos
    limpar()

    # Exibe dados
    tcodigo.insert(0, pid)
    tpnome.insert(0, pnome)
    tsnome.insert(0, psobrenome)
    tcargo.insert(0, pcargo)
    tsalario.insert(0, str(psalario))

# Cria componentes widgets
titulo = Label(formulario, text="Database: Nenhum")
# labels
lcodigo = Label(formulario, text="Codigo:")
lpnome = Label(formulario, text="Nome:")
lsnome = Label(formulario, text="Sobrenome:")
lcargo = Label(formulario, text="Cargo:")
lsalario = Label(formulario, text="Salario:")
# Entry
tcodigo = Entry(formulario)
tpnome = Entry(formulario)
tsnome = Entry(formulario)
tcargo = Entry(formulario)
tsalario = Entry(formulario)
# Pesquisa
lpesquisa = Label(formulario, text="Pesquisa:")
tpesquisa = Entry(formulario)
botao = Button(formulario, text = "Pesquisar", command=on_Pesquisar)

# Define Layout
titulo.grid(row=0, sticky=W+E+N+S, pady=20)
lcodigo.grid(row=1, sticky=W, padx=20)
tcodigo.grid(row=1, column=1, pady=5)
lpnome.grid(row=2,sticky=W, padx=20)
tpnome.grid(row=2, column=1, pady=5)
lsnome.grid(row=3,sticky=W, padx=20)
tsnome.grid(row=3, column=1, pady=5)
lcargo.grid(row=4, sticky=W, padx=20)
tcargo.grid(row=4, column=1, pady=5)
lsalario.grid(row=5, sticky=W, padx=20)
tsalario.grid(row=5, column=1, pady=5)
# Layout pesquisa
lpesquisa.grid(row=6, column=0, pady=20)
tpesquisa.grid(row=6, column=1, pady=20)
botao.grid(row=6, column=2,pady=20)

# loop do tcl
mainloop()

Stored Procedures são procedimentos de código SQL armazenados no banco de dados, as stored procedures são mais rápidas do que o código tradicional, o que significa que elas estão se tornando cada vez mais populares. Com código da aplicação se mudando dos programas externos para o motor de banco de dados, Os DBAs precisam entender os requisitos de memória relacionados para procedimentos armazenados e saber como gerenciar estes procedimentos para o ideal desempenho do banco de dados. Isto muda de banco para banco, cada um deles possui suas recomendações de melhores praticas para atingir um melhor desempenho.

Neste site existe uma versão deste mesmo programa utilizando código SQL direto dentro da aplicação e podemos comparar a diferença com o código SQL sendo executado dentro do motor do banco de dados e o resultado é visivelmente notado, sem duvida o código executado utilizando procedures é mais rápido.

Ainda existe um outro modo de utilizar o banco de dados que é utilizando um set de dados persistente, onde uma camada  de classes ou componentes geram o código SQL para o banco de dados através de instruções definidas pelas classes, mas este método por possuir uma camada que cria o código para o banco pode se tornar menos atrativo em termos de desempenho, apesar de ser muito mais fácil e rápido para se desenvolver, sendo assim vale avaliar a quantidade de dados que sua aplicação pretende manipular.

Para adquirir o script SQL para criar a tabela deste exemplo ou visualizar o método utilizando SQL direto da aplicação: clique aqui.

Python - Stored Procedure

Python – Stored Procedure

Exemplo:

Neste exemplo utilizamos Stored Procedures que retornam parâmetros para criar um método dentro do banco de dados ao invés de criar o método dentro da aplicação utilizando a linguagem de programação nativa.

Oracle

create or replace PROCEDURE buscaFuncionario
- Cria parametros
(pid_funcionario IN FUNCIONARIOS.ID_FUNCIONARIO%TYPE,
 p_nome OUT FUNCIONARIOS.NOME%TYPE,
 p_sobrenome OUT FUNCIONARIOS.SOBRENOME%TYPE,
 p_cargo OUT FUNCIONARIOS.CARGO%TYPE,
 p_salario OUT FUNCIONARIOS.SALARIO%TYPE
 )
AS
BEGIN
-- Insere dados da query dentro dos parametros de saida
  SELECT NOME, SOBRENOME, CARGO, SALARIO
  INTO p_nome, p_sobrenome, p_cargo, p_salario
  FROM FUNCIONARIOS
  WHERE ID_FUNCIONARIO = pid_funcionario;

END;

Python

#!/usr/bin/env python
# -*- coding: latin-1 -*-
# Desenvolvimento Aberto
# StoredOra.py

# importa modulos
from Tkinter import *
import tkMessageBox
import cx_Oracle

# Cria formulario
formulario = Tk(className='Desenvolvimento Aberto')
formulario.geometry("350x280+300+300")

# Cria janela para menssagem
janela = Tk()
janela.wm_withdraw()

# Define banco de dados
# oracle = Oracle Database
DBconexao = "oracle"

# Cria conexão com o banco de dados
def conectar(banco):
    # Cria string de conexão Oracle
    if (banco == "oracle"):
        sconexao = "user/p@55w0rd@localhost/XE"
        try:
            con = cx_Oracle.connect(sconexao)
        except ValueError:
            tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela) 

    return con

# Limpa campo
def limpar():
    tcodigo.delete(0, END)
    tpnome.delete(0, END)
    tsnome.delete(0, END)
    tcargo.delete(0, END)
    tsalario.delete(0, END)

# Evento do botão
def on_Pesquisar():
    # Exibe banco de dados
    titulo['text'] = "Database: " + DBconexao

    # Cria conexão
    con = conectar(DBconexao)

    # Define e executa Stored Procedure
    if (DBconexao == "oracle"):
        cursor = con.cursor()

        #Declara parametros
        pnome = cursor.var(cx_Oracle.STRING)
        psobrenome = cursor.var(cx_Oracle.STRING)
        pcargo = cursor.var(cx_Oracle.STRING)
        psalario = cursor.var(cx_Oracle.NUMBER)

        # Chama stored procedure utilizando um tupple
        cursor.callproc('buscaFuncionario', (tpesquisa.get(), pnome, psobrenome, pcargo, psalario))

    # Prepara campos
    limpar()

    # Exibe dados
    tcodigo.insert(0, tpesquisa.get())
    tpnome.insert(0, pnome.getvalue())
    tsnome.insert(0, psobrenome.getvalue())
    tcargo.insert(0, pcargo.getvalue())
    tsalario.insert(0, str(psalario.getvalue()))

# Cria componentes widgets
titulo = Label(formulario, text="Database: Nenhum")
# labels
lcodigo = Label(formulario, text="Codigo:")
lpnome = Label(formulario, text="Nome:")
lsnome = Label(formulario, text="Sobrenome:")
lcargo = Label(formulario, text="Cargo:")
lsalario = Label(formulario, text="Salario:")
# Entry
tcodigo = Entry(formulario)
tpnome = Entry(formulario)
tsnome = Entry(formulario)
tcargo = Entry(formulario)
tsalario = Entry(formulario)
# Pesquisa
lpesquisa = Label(formulario, text="Pesquisa:")
tpesquisa = Entry(formulario)
botao = Button(formulario, text = "Pesquisar", command=on_Pesquisar)

# Define Layout
titulo.grid(row=0, sticky=W+E+N+S, pady=20)
lcodigo.grid(row=1, sticky=W, padx=20)
tcodigo.grid(row=1, column=1, pady=5)
lpnome.grid(row=2,sticky=W, padx=20)
tpnome.grid(row=2, column=1, pady=5)
lsnome.grid(row=3,sticky=W, padx=20)
tsnome.grid(row=3, column=1, pady=5)
lcargo.grid(row=4, sticky=W, padx=20)
tcargo.grid(row=4, column=1, pady=5)
lsalario.grid(row=5, sticky=W, padx=20)
tsalario.grid(row=5, column=1, pady=5)
# Layout pesquisa
lpesquisa.grid(row=6, column=0, pady=20)
tpesquisa.grid(row=6, column=1, pady=20)
botao.grid(row=6, column=2,pady=20)

# loop do tcl
mainloop()

Na linguagem de programação Python você pode executar instruções SQL diretas, utilizando o método execute para os bancos de dados Oracle e Microsoft SQL Server e o método exec_immediate para executar instruções sobre o banco de dados IBM DB2.

Neste método utilizamos as APIs de conexão cx_oracle para o banco de dados Oracle, a API ibm_db para o banco de dados DB2 e a API win32 para criar uma conexão ODBC com o banco de dados MSSQL Server.

Execute

O método execute() analisa e executa uma instrução SQL.

exec_immediate

Este procedimento prepara e executa uma única instrução SQL.

API

Para saber mais sobre como utilizar estas APIs utilize nossa categoria Python ou digite a palavra “Conexão” no item de busca do menu deste site.

Python - Database

Python – Database

Exemplo:

Este programa executa as quatro operações básicas em diferentes bancos de dados.

Python

#!/usr/bin/env python
# -*- coding: latin-1 -*-
# Desenvolvimento Aberto
# campos.py

# importa modulos
from Tkinter import *
import tkMessageBox
import cx_Oracle
import ibm_db
import odbc

# Cria formulario
formulario = Tk(className='Desenvolvimento Aberto')
formulario.geometry("400x330+300+300")

# Cria janela para menssagem
janela = Tk()
janela.wm_withdraw()

# Define banco de dados
# oracle = Oracle Database
# db2 = IBM DB2 Database
# mssql = Microsoft SQL Server
DBconexao = "mssql"

# Cria conexão com o banco de dados
def conectar(banco):
    # Cria string de conexão Oracle
    if (banco == "oracle"):
        sconexao = "user/pass@localhost/XE"
        try:
            con = cx_Oracle.connect(sconexao)
        except ValueError:
            tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela)

    if (banco == "db2"):
        # Cria string de conexão IBM
        sconexao = "DATABASE=DEVA" +  \
                   ";HOSTNAME=localhost;PORT=50000;PROTOCOL=TCPIP;" + \
                   "UID=user;" + \
                   "PWD=pass"
        try:
            con = ibm_db.connect(sconexao, "", "")
        except ValueError:
            tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela)

    if (banco == "mssql"):
        # Cria string de conexão MSSQL ODBC
        sconexao =  "MSSQLSERVER/user/pass"
        try:
            con = odbc.odbc(sconexao)
        except ValueError:
           tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela)

    return con

# Executa e retorna cursor
def retornaFuncionarioID(sql, con, banco):
    if (banco == "oracle"):
        cursor = con.cursor()
        cursor.execute(sql)
    if (banco == "db2"):
        cursor = ibm_db.exec_immediate(con, sql)
    if (banco == "mssql"):
        cursor = con.cursor()
        cursor.execute(sql)
    return cursor

# Limpa campo
def limpar():
    tcodigo.delete(0, END)
    tpnome.delete(0, END)
    tsnome.delete(0, END)
    tcargo.delete(0, END)
    tsalario.delete(0, END)

# Evento do botão
def on_Pesquisar():
    # Exibe banco de dados
    titulo['text'] = "Database: " + DBconexao

    # Cria conexão
    con = conectar(DBconexao)

    # Define e executa SQL
    sql = "Select * From FUNCIONARIOS Where  ID_FUNCIONARIO = " + tpesquisa.get()
    tabela = retornaFuncionarioID(sql, con, DBconexao)

    # Cria cursor
    if (DBconexao == "oracle"):
        dados = tabela.fetchone()
    if (DBconexao == "db2"):
        dados = ibm_db.fetch_tuple(tabela)
    if (DBconexao == "mssql"):
        dados = tabela.fetchone()

    # Exibe dados
    limpar()
    tcodigo.insert(0, str(dados[0]))
    tpnome.insert(0, dados[1])
    tsnome.insert(0, dados[2])
    tcargo.insert(0, dados[3])
    tsalario.insert(0, str(dados[4]))

# limpa widgets
def on_novo():
    limpar()
    tcodigo.focus()

# Insere dados
def on_inserir():
    con = conectar(DBconexao)
    if (DBconexao != "db2"): cursor = con.cursor()

    sql ="Insert into Funcionarios Values (" + \
          tcodigo.get() + ", '" + \
          tpnome.get() + "', '" + \
          tsnome.get() + "', '" + \
          tcargo.get() + "', " + \
          str(tsalario.get()).replace(",",".") + ")"
    try:
        if (DBconexao != "db2"):
            cursor.execute(sql)
            con.commit()
        else:
            cursor = ibm_db.exec_immediate(con, sql)
        tkMessageBox.showinfo(title="Menssagem", message="Dados inseridos com sucesso!", parent=janela)
    except ValueError:
        tkMessageBox.showinfo(title="Menssagem", message="Erro ao inserir dados!", parent=janela)

# Altera dados
def on_alterar():
    con = conectar(DBconexao)
    if (DBconexao != "db2"): cursor = con.cursor()

    sql ="Update Funcionarios set " + \
          "ID_FUNCIONARIO = " + tcodigo.get() + ", NOME= '" + \
          tpnome.get() + "', SOBRENOME= '" + \
          tsnome.get() + "', CARGO= '" + \
          tcargo.get() + "', SALARIO= " + \
          str(tsalario.get()).replace(",",".") + " Where ID_FUNCIONARIO=" + tcodigo.get()

    try:

       if (DBconexao != "db2"):
           cursor.execute(sql)
           con.commit()
       else:
           cursor = ibm_db.exec_immediate(con, sql)            

       tkMessageBox.showinfo(title="Menssagem", message="Dados alterados com sucesso!", parent=janela)
    except ValueError:
        tkMessageBox.showinfo(title="Menssagem", message="Erro ao alterar dados!", parent=janela)

# Exclui dados
def on_apagar():
    con = conectar(DBconexao)
    if (DBconexao != "db2"): cursor = con.cursor()

    sql ="Delete From Funcionarios Where ID_FUNCIONARIO = " + tcodigo.get()

    try:
        if (DBconexao != "db2"):
            cursor.execute(sql)
            con.commit()
        else:
            cursor = ibm_db.exec_immediate(con, sql)
        limpar()
        tkMessageBox.showinfo(title="Menssagem", message="Dados excluidos com sucesso!", parent=janela)
    except ValueError:
        tkMessageBox.showinfo(title="Menssagem", message="Erro ao excluir dados!", parent=janela)

# Cria componentes widgets
titulo = Label(formulario, text="Database: Nenhum")

# labels
lcodigo = Label(formulario, text="Codigo:")
lpnome = Label(formulario, text="Nome:")
lsnome = Label(formulario, text="Sobrenome:")
lcargo = Label(formulario, text="Cargo:")
lsalario = Label(formulario, text="Salario:")

# Entry
tcodigo = Entry(formulario)
tpnome = Entry(formulario)
tsnome = Entry(formulario)
tcargo = Entry(formulario)
tsalario = Entry(formulario)

# Pesquisa
lpesquisa = Label(formulario, text="Pesquisa:")
tpesquisa = Entry(formulario)
botao = Button(formulario, text = "Pesquisar", command=on_Pesquisar)

#Ações
painel = Frame()
bnovo = Button(painel, text="Novo", command=on_novo)
binserir = Button(painel, text="Inserir", command=on_inserir)
balterar = Button(painel, text="Alterar", command=on_alterar)
bapagar = Button(painel, text="Apagar", command=on_apagar)

# Define Layout
titulo.grid(row=0, sticky=W+E+N+S, pady=20)
lcodigo.grid(row=2, sticky=W, padx=20)
tcodigo.grid(row=2, column=1, pady=5)
lpnome.grid(row=3,sticky=W, padx=20)
tpnome.grid(row=3, column=1, pady=5)
lsnome.grid(row=4,sticky=W, padx=20)
tsnome.grid(row=4, column=1, pady=5)
lcargo.grid(row=5, sticky=W, padx=20)
tcargo.grid(row=5, column=1, pady=5)
lsalario.grid(row=6, sticky=W, padx=20)
tsalario.grid(row=6, column=1, pady=5)

# Layout pesquisa
lpesquisa.grid(row=1, column=0, pady=20)
tpesquisa.grid(row=1, column=1, pady=20)
botao.grid(row=1, column=2,pady=20)

# Loayout Ações
bnovo.grid(row =1, column=0, pady=15)
binserir.grid(row =1, column=1, pady=15)
balterar.grid(row =1, column=2, pady=15)
bapagar.grid(row =1, column=3, pady=15)
painel.grid(row=7, columnspan= 3, padx=100, pady=10)

# loop do tcl
mainloop()

Na linguagem de programação Python você pode usar os módulos, cx_Oracle, ibm_db e odbc para se conectar aos bancos de dados Oracle, IBM DB2 e MSSQL Server e manipular dados através de um cursor.

Python - Database

Python – Database

Exemplo:

Este programa foi escrito para retornar uma linha de dados de uma tabela e exibir os campos em caixas de textos.

SQL

Oracle

create table Funcionarios
(     

    ID_Funcionario  NUMBER(5),
    Nome            VARCHAR2(30),
    Sobrenome       VARCHAR2(70),
    Cargo           VARCHAR2(30),
    Salario         NUMBER(9,2)

);

Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);

DB2

create table Funcionarios
(     

    ID_Funcionario  INTEGER,
    Nome            VARCHAR(30),
    Sobrenome       VARCHAR(70),
    Cargo           VARCHAR(30),
    Salario         NUMERIC(9,2)

);

Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);

Mssql

create table Funcionarios
(     

    ID_Funcionario  Int,
    Nome            VARCHAR(30),
    Sobrenome       VARCHAR(70),
    Cargo           VARCHAR(30),
    Salario         Decimal(9,2)

);

Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);

Python

#!/usr/bin/env python
# -*- coding: latin-1 -*-
# Desenvolvimento Aberto
# campos.py

# importa modulos
from Tkinter import *
import tkMessageBox
import cx_Oracle
import ibm_db
import odbc

# Cria formulario
formulario = Tk(className='Desenvolvimento Aberto')
formulario.geometry("350x280+300+300")

# Cria janela para menssagem
janela = Tk()
janela.wm_withdraw()

# Define banco de dados
# oracle = Oracle Database
# db2 = IBM DB2 Database
# mssql = Microsoft SQL Server
DBconexao = "oracle"

# Cria conexão com o banco de dados
def conectar(banco):
    # Cria string de conexão Oracle
    if (banco == "oracle"):
        sconexao = "user/p@55w0rd@localhost/XE"
        try:
            con = cx_Oracle.connect(sconexao)
        except ValueError:
            tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela)

    if (banco == "db2"):
        # Cria string de conexão IBM
        sconexao = "DATABASE=DEVA" +  \
                   ";HOSTNAME=localhost;PORT=50000;PROTOCOL=TCPIP;" + \
                   "UID=user;" + \
                   "PWD=p@55w0rd"
        try:
            con = ibm_db.connect(sconexao, "", "")
        except ValueError:
            tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela)

    if (banco == "mssql"):
        # Cria string de conexão MSSQL ODBC
        sconexao =  "MSSQLSERVER/user/p@55w0rd"
        try:
            con = odbc.odbc(sconexao)
        except ValueError:
           tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela)

    return con

# Executa e retorna cursor
def retornaFuncionarioID(sql, con, banco):
    if (banco == "oracle"):
        cursor = con.cursor()
        cursor.execute(sql)
    if (banco == "db2"):
        cursor = ibm_db.exec_immediate(con, sql)
    if (banco == "mssql"):
        cursor = con.cursor()
        cursor.execute(sql)
    return cursor

# Evento do botão
def on_Pesquisar():
    # Exibe banco de dados
    titulo['text'] = "Database: " + DBconexao

    # Cria conexão
    con = conectar(DBconexao)

    # Define e executa SQL
    sql = "Select * From FUNCIONARIOS Where  ID_FUNCIONARIO = " + tpesquisa.get()
    tabela = retornaFuncionarioID(sql, con, DBconexao)

    # Cria cursor
    if (DBconexao == "oracle"):
        dados = tabela.fetchone()
    if (DBconexao == "db2"):
        dados = ibm_db.fetch_tuple(tabela)
    if (DBconexao == "mssql"):
        dados = tabela.fetchone()

    # Exibe dados
    tcodigo.insert(0, str(dados[0]))
    tpnome.insert(0, dados[1])
    tsnome.insert(0, dados[2])
    tcargo.insert(0, dados[3])
    tsalario.insert(0, str(dados[4]))

# Cria componentes widgets
titulo = Label(formulario, text="Database: Nenhum")
# labels
lcodigo = Label(formulario, text="Codigo:")
lpnome = Label(formulario, text="Nome:")
lsnome = Label(formulario, text="Sobrenome:")
lcargo = Label(formulario, text="Cargo:")
lsalario = Label(formulario, text="Salario:")
# Entry
tcodigo = Entry(formulario)
tpnome = Entry(formulario)
tsnome = Entry(formulario)
tcargo = Entry(formulario)
tsalario = Entry(formulario)
# Pesquisa
lpesquisa = Label(formulario, text="Pesquisa:")
tpesquisa = Entry(formulario)
botao = Button(formulario, text = "Pesquisar", command=on_Pesquisar)

# Define Layout
titulo.grid(row=0, sticky=W+E+N+S, pady=20)
lcodigo.grid(row=1, sticky=W, padx=20)
tcodigo.grid(row=1, column=1, pady=5)
lpnome.grid(row=2,sticky=W, padx=20)
tpnome.grid(row=2, column=1, pady=5)
lsnome.grid(row=3,sticky=W, padx=20)
tsnome.grid(row=3, column=1, pady=5)
lcargo.grid(row=4, sticky=W, padx=20)
tcargo.grid(row=4, column=1, pady=5)
lsalario.grid(row=5, sticky=W, padx=20)
tsalario.grid(row=5, column=1, pady=5)
# Layout pesquisa
lpesquisa.grid(row=6, column=0, pady=20)
tpesquisa.grid(row=6, column=1, pady=20)
botao.grid(row=6, column=2,pady=20)

# loop do tcl
mainloop()

Não existe um método simples para mostrar e manipular dados em uma grade na linguagem de programação Python, o método mais simples é bem parecido com o método utilizado em C++, você precisa escrever um código que retorna arrays e vetores para preencher a grade como se fosse uma tabela e precisa escrever métodos que permitem manipular a grade de vários modos, você encontra a toda documentação necessária neste link:

Classe WxGrid: http://wiki.wxpython.org/wxGrid#The_wxGrid_Class

WxGrid - Oracle

WxGrid – Oracle

Oracle

Você pode retornar os dados de uma tabela do banco de dados Oracle criando um cursor e executando uma pesquisa sobre ele, você utiliza o método fetchall() para retornar todas as linhas do seu set de dados.

Exemplo:

Neste exemplo criamos uma grade de dados e a preenchemos com os dados retornados de uma pesquisa no banco de dados.

Python

#!/usr/bin/env python
# -*- coding: latin-1 -*-
# Desenvolvimento Aberto
# GradeDados.py

# importa modulos
import wx
import wx.grid
import cx_Oracle

# Cria classe generica de uma WX.Grid
# A classe abaixo faz parte da documentação WXPython oficial
# Este trecho de código é util para manipular a grade

class GenericTable(wx.grid.PyGridTableBase):
    def __init__(self, data, rowLabels=None, colLabels=None):
        wx.grid.PyGridTableBase.__init__(self)
        self.data = data
        self.rowLabels = rowLabels
        self.colLabels = colLabels

    def GetNumberRows(self):
        return len(self.data)

    def GetNumberCols(self):
        return len(self.data[0])

    def GetColLabelValue(self, col):
        if self.colLabels:
            return self.colLabels[col]

    def GetRowLabelValue(self, row):
        if self.rowLabels:
            return self.rowLabels[row]

    def IsEmptyCell(self, row, col):
        return False

    def GetValue(self, row, col):
        return self.data[row][col]

    def SetValue(self, row, col, value):
        pass       

# Cria conexão Oracle
def conectarORA():
    sconexao = "user/p@55w0rd@localhost/XE"
    try:
        con = cx_Oracle.connect(sconexao)
    except ValueError:
        tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela)
    return con

# Cria conexão
con = conectarORA()

# Executa e retorna SQL
def retornaTabelaORA(sql, con):
    cursor = con.cursor()
    cursor.execute(sql)
    return cursor

# Inicializa Grade
dados = []
colLabels  = []
rowLabels = ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")   

#Retorna metadados
colunas = retornaTabelaORA("select COLUMN_NAME from ALL_TAB_COLUMNS where TABLE_NAME='BLOG'", con)
metadados = colunas.fetchall()
for cols in metadados:
    colLabels.append(str(str(str(str(cols).replace("(","")).replace(")","")).replace(",","")).replace("'",""))

# Envia dados a grid
tabela = retornaTabelaORA("Select * from Blog", con)
resultado = tabela.fetchall()
for conteudo in resultado:
    dados.append(conteudo)

# Cria classe da grid
class SimpleGrid(wx.grid.Grid):
    def __init__(self, parent):
        wx.grid.Grid.__init__(self, parent, -1, pos=(5,60), size=(850,200))
        tableBase = GenericTable(dados, rowLabels, colLabels)
        self.SetTable(tableBase)                   

# Cria formulario
class TestFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "Acessando Dados Oracle DataBase", size=(900, 350))
        panel = wx.Panel(self, wx.ID_ANY)
        label=wx.StaticText(panel, -1, label='WXPython - DataGrid - Oracle Database XE', pos=(300,20))
        grid = SimpleGrid(panel)

# Inicializa a aplicação
app = wx.PySimpleApp()
frame = TestFrame(None)
frame.Show(True)
app.MainLoop()

A grande maioria das empresas necessitam de sistemas que possam processar, guardar e manipular informações e o modo mais comum de se fazer este trabalho é usando um banco de dados relacional. Um sistema de gerenciamento de banco de dados relacional (RDBMS) é um sistema de gerenciamento de banco de dados (SGBD), que é baseado no modelo relacional introduzido por EF Codd, do Laboratório de Pesquisa de San Jose da IBM. Muitos bancos de dados populares atualmente em uso são baseados no modelo de banco de dados relacional.

Os RDBMSs tornaram-se uma escolha predominante para o armazenamento de informações em bancos de dados, usados para registros financeiros, fabricação e informações logísticas, dados de pessoal, e muito mais desde 1980. Bancos de dados relacionais, muitas vezes substituindo bancos de dados hierárquicos e bancos de dados de rede, porque eles são mais fáceis de entender e usar. No entanto, os bancos de dados relacionais vem sendo substituídos por banco de dados orientados a objeto que foram introduzidos na tentativa de resolver a diferença de impedância objeto-relacional em banco de dados relacional e bancos de dados XML.

Banco de Dados Relacional

Banco de Dados Relacional

Entre os maiores banco de dados relacionais do mercado, segundo a empresa Gartner, os líderes de vendas são:

  1. Oracle (48.8%),
  2. IBM (20.2%),
  3. Microsoft (17.0%),
  4. SAP incluindo (Sybase (4.6%), e Teradata (3.7%))

Banco de dados o grande vilão

Quem já ouviu esta afirmação? “O sistema está muito lento!”. Existem vários fatores que podem causar este problema mas eu já ouvi o que considero a pior resposta do mundo de para esta afirmação e foi de um especialista da empresa líder nacional do mercado para ERP, da qual você já imaginou o nome. O sistema estava em um cliente multinacional que fabrica peças para montadoras de carros e rodava sobre o banco de dados Microsoft SQL Server, e o consultor da tal empresa  disse: “Troca o banco de dados pelo Oracle“. Instantaneamente o gerente de TI  replicou: “Neste caso é melhor eu trocar seu sistema pelo SAP!”.

Eu concordo plenamente com a resposta do gerente de TI da multinacional, o SAP roda muito bem em SQL Server e atende a maioria das empresas no qual utiliza esta configuração e benchmark dos dois bancos de dados são comuns, basta procurar no Google, segue aqui um exemplo : BenchMark DB2 1o vs SQL Server 2012 vs Oracle 11g R2 . Será que todas as empresas utilizam as novas features dos bancos de dados em seu sistema assim como faz a SAP?

Geralmente a solução do problema é trazida por um empresa de consultoria, porque este tipo de empresa lida com vários bancos de dados, de vários sistemas diferentes e seus especialistas conhecem a fundo varias bases de dados dos mais diferentes tipos, incluindo sistemas como o SAP, JDEdwardsMS Dynamics. É comum algumas consultorias que também fabricam software desenvolver soluções que completam ou interagem com estes ERPs e estes desenvolvedores ou consultores já estão calejados e sabem que nem tudo que existe por ai é uma maravilha, e nem todos podem ter o luxo de utilizar uma base de dados criada e assinada pela SAP, Oracle, Microsoft ou IBM. E as vezes ou na maioria delas a melhor solução é sim trocar o sistema.

O que acontece por ai?

Algumas empresas não sabem utilizar corretamente o banco, ressalto que utilizar um o banco de dados não é apenas possuir um desenvolvedor SQL que saiba escrever scripts. Já presenciei sistemas de empresas que existem há mais de 10 anos no mercado e ainda não aprenderam a utilizar um banco de dados relacional corretamente, muito menos já seguiram algum dia as melhores praticas. Em um caso em particular o banco de dados era Oracle e o sistema rodava em clientes de médio porte, e a frase constantemente ouvida dos clientes era: “O sistema é muito lento e toda vez que atualiza o sistema gera muitos erros”. Bom, neste caso o especialista da empresa não poderia dizer para trocar o banco de dados e ao analisar a base dados instantaneamente se notava vários problemas como: muitas chaves primarias, a não utilização de índice único, triggers em excesso, scripts SQL no mínimo duvidosos e lembro de um script do relatório de inventario que demorava mais ou menos 10 minutos para retornar 750 produtos.

É! isto existe por ai! E pergunto para você, isto é normal?

Alguns casos são demorados de resolver como o caso da empresa acima que vinha costurando scripts e programação sem metodologia a mais de 10 anos em um banco sem normalização e outros podem ser resolvidos facilmente como o caso de um empresa cooperativa de grande porte que possuía outras 98 empresas em seu grupo e precisava importar as notas fiscais de todas as empresas para um sistema de validação fiscal  que utiliza Microsoft SQL Server e os dados seriam extraídos de um sistema da IBM, este processo demorava mais de 4 horas . Utilizando novos scripts SQL, implementando técnicas de BULK foi possível reduzir o tempo do processo para importação de 5 anos de dados  de 98 empresas para 15 minutos.

Falta de Normalização

Falta de Normalização

O Verdadeiro Vilão

Podemos apontar de primeira alguns dos principais problemas como, falta de conhecimento da documentação do banco de dados em questão, pouca ou nenhuma normalização, não tratar o modelo de dados como um organismo vivo, que respira e cresce constantemente, o armazenamento inadequado dos dados de referência, não usar chaves estrangeiras ou restrições de verificação ou o uso excessivo dos mesmos, o não uso de domínios e padrões de nomenclatura  e não escolher chaves primárias adequadamente.

Em segundo lugar está o estilo de programação tanto para os scripts do banco dados como na linguagem que acessa os dados, é comum encontrar o uso excessivo de cursores, acessos repetitivos e não saber quando usar o conceito de múltiplos bancos de dados.

Vou ilustrar os parágrafos acima com um outro caso que também é muito comum, e refere-se a uso excessivo de chaves estrangeiras,  não saber a hora de optar por um sistema de banco de dados múltiplos e não incluir no projeto do sistema a previsão de crescimento do mesmo.

O problema ocorre porque inicialmente o projeto de um tipo de ferramenta que extrai dados fiscais para validação foi escrito somente para o banco de dados Oracle, ao longo dos anos com o investimento da Microsoft no MSSQL Server o banco de dados conseguiu um bom espaço no mercado e varias empresas de médio e grande porte o adquiriram, então a ferramenta da empresa em questão precisava trabalhar e rodar nos dois banco de dados. Mas o trabalho era árduo porque era preciso extrair os últimos cinco anos de dados fiscais da empresa e suas filiais e valida-los em horas.

Com o passar dos anos as obrigatoriedades vieram a ser maiores e a empresa proprietária da ferramenta precisava atender estas obrigatoriedades e o próprio mercado ditou a necessidade da ferramenta estar apta a rodar em um banco de dados que vinha ganhando cada vez mais espaço, é obvio que se um cliente possuísse uma licença para o banco de dados que rodava em seu sistema principal (MSSQL) não iria comprar uma licença mais cara (Oracle) somente para poder utilizar uma ferramenta e sim procurar outra ferramenta que atendia sua especificação técnica.

Primeiro a empresa tentou sozinha desenvolver para o novo banco de dados e encontrou varias dificuldades ao ter que descobrir também sozinha que os conceitos entre os bancos de dados não eram iguais e decidiu procurar ajuda especializada, ficou surpresa com o resultado, ao trabalhar com mais de um banco de dados a empresa conseguiu benefícios de desempenho também para o banco de dados no qual a empresa dominava e era especialista.

Porque para competir com o Oracle a Microsoft também usou como estratégia restringir os erros cometidos pelos desenvolvedores na arquitetura do banco de dados, meio que forçando o banco de dados SQL Server a ser escrito e normalizado o mais corretamente possível, assim ganhando mais performance, um exemplo são as chaves estrangeiras, estas requerem memoria e custam para serem interpretadas pelo Engine do banco de dados e a partir da versão 2005 do MSSQL a Microsoft começou a restringir chaves estrangeiras por tabela. No caso de um banco de dados único em um sistema multi-empresas é comum possuir mais de 253 tabelas que possuem uma chave estrangeira conectada a tabela de empresas, então a Microsoft começou a emitir um erro quando se tentava deletar ou alterar um registro de uma tabela que possuía tal quantidade de chaves estrangeiras, a simples clausula executada sobre esta tabela:

Delete from EMPRESA where ID_EMPRESA = 1

retorna o seguinte erro: QUERY TOO COMPLEX.

Capacidade máxima do MSSQL Server (Atente-se para a coluna chamada Foreign key table references per table):

link: http://msdn.microsoft.com/en-us/library/ms143432.aspx

Você pode ler e inserir dados na tabela normalmente, mas não pode mais apagar ou alterar os dados devido ao alto custo da query, apesar do Oracle ou outros bancos de dados permitirem tal modelagem a query executada sobre estas circunstancias também terá um custo maior para ser resolvida pelo Engine do banco de dados. Para uma modelagem mais compacta e sem excessos de objetos no banco de dados, um sistema pode possuir múltiplos bancos de dados, um exemplo de um sistema de grande porte com múltiplos bancos de dados é o JDEdwards da Oracle.

Grande Volume de Dados

Grande Volume de Dados

Processamento de grandes volumes de dados

É recomendado seguir melhores praticas para cada banco de dados e ler os White Papers escritos para suas ferramentas e quanto a regras de programação para sistemas que acessam dados você pode seguir o critério acima ou pode buscar conhecimento em empresas que já possuem este know-how como o caso da SAP.

Como é de conhecimento de todos os sistemas da SAP foram projetados para trabalhar com grande volume de dados e a SAP especifica 5 regras para se programar com os banco de dados: Oracle, Db2, Microsoft SQL Server e Informix. Apesar destas regras serem escritas para programas ABAP e Java e a arquitetura de dados do SAP R/3, elas também se aplicam a forma como outras linguagens de programação devem acessar o banco de dados:

  1. Mantenha o conjunto de resultados Pequeno.
  2. Minimizar a quantidade de dados transferidos.
  3. Minimizar o número de transferências de dados.
  4. Minimizar a Pesquisa Overhead.
  5. Reduzir a carga do banco de dados.

 

 

Existem vários métodos para se conectar no banco de dados SQL Server através da linguagem Python, você pode criar uma conexão usando módulos escritos por terceiros como o pyMSSQL, você pode usar as biblioteca win32 através do modulo pyWin32 ou você se conectar usando o framework e ADO.NET usando o IronPython. Neste post usaremos o pyWin32 Extension para executar este trabalho, você pode baixar esta extensão no seguinte site:

Download: http://sourceforge.net/projects/pywin32/

Microsoft SQL Server - Python

Microsoft SQL Server – Python

Conectando ao Microsoft SQL Server

1 – Após baixar e instalar a extensão para o Python Win32 você precisa criar uma conexão ODBC para SQLSERVER, você pode optar por uma conexão ODBC win32 ou x64, para isto acesse o painel de controle, ferramentas administrativas e fontes de dados ODBC e preencha e teste a conexão com o seu servidor MSSQL:

MSQL -DNS

MSQL -DNS

2 – Abra sua IDE Python e utilize o código abaixo.

Exemplo:

Neste exemplo construiremos uma conexão com o banco de dados SQL Server através da extensão pyWin32 e utilizaremos um DNS ODBC para criar uma conexão com o banco de dados.

Python

#!/usr/bin/env python
# -*- coding: latin-1 -*-
# Desenvolvimento Aberto
# ConexaoMSSQL.py

# importa modulos
from Tkinter import *
import tkMessageBox
import odbc

# Cria formulario
formulario = Tk(className='Microsoft SQL Server')
formulario.geometry("300x200+300+300")

# Cria janela para menssagem
janela = Tk()
janela.wm_withdraw()

# Evento para o botão
def conectar():
    # Cria string de conexão
    sconexao = edb.get() + "/" +  eusuario.get() + "/" + esenha.get()
    try:
        con = odbc.odbc(sconexao)
        tkMessageBox.showinfo(title="Menssagem", message="Conectado com Sucesso!", parent=janela)
    except ValueError:
        tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela)

# Cria componentes
titulo = Label(formulario, text="MSSQL Express 2012 - Python")

lusuario = Label(formulario, text="Digite seu usuário:")
lsenha = Label(formulario, text="Digite sua senha:")
ldb = Label(formulario, text="Digite o DNS ODBC:")

eusuario = Entry(formulario)
esenha = Entry(formulario, show="*")
edb = Entry(formulario)

botao = Button(formulario, text="Conectar", command=conectar)

# Cria layout de tela
titulo.grid(row=0, sticky=W+E+N+S, pady=10)
lusuario.grid(row=1, sticky=W, padx=20)
eusuario.grid(row=1, column=1, pady=5)
lsenha.grid(row=2,sticky=W, padx=20)
esenha.grid(row=2, column=1, pady=5)
ldb.grid(row=3, sticky=W, padx=20)
edb.grid(row=3, column=1, pady=5)
botao.grid(row=5, sticky=W, pady=20, padx=20)

# Loop do tcl
mainloop()

 

A API ibm_db oferece uma variedade de funções úteis para acessar e manipular dados em um banco de dados de um servidor de dados IBM ®. A API inclui funções para se conectar a um banco de dados, executar e preparar instruções SQL, buscar linhas de conjuntos de resultados, chamar procedimentos armazenados, finalizar e reverter transações, manipular tratamento de erros e recuperação de metadados.

IBM DB2 - Python

IBM DB2 – Python

Python DBI driver for DB2 (LUW, zOS, i5) and IDS

Essa extensão é a implementação de banco de dados com especificação da API Python v2.0. A extensão suporta DB2 (LUW, zOS, i5) e IDS (Informix Dynamic Server)

Download: https://pypi.python.org/pypi/ibm_db/

Instalação:

Para instalar a API você pode compilar o código fonte ou utilizar as extensões já compiládas, copiando o arquivo ibm_db.pyd para o diretório de DLL da sua instalação do Python.

Diretório: C:\Python27\DLLs\ibm_db.pyd

Exemplo:

Neste exemplo usamos a API de conexão para o IBM DB2 para efetuar uma conexão com o banco de dados DB2 Express-C.

Python

#!/usr/bin/env python
# -*- coding: latin-1 -*-
# Desenvolvimento Aberto
# ConexaoDB2py

# importa modulos
from Tkinter import *
import tkMessageBox
import ibm_db

# Cria formulario
formulario = Tk(className='IBM DB2 Express-C')
formulario.geometry("300x200+300+300")

# Cria janela para menssagem
janela = Tk()
janela.wm_withdraw()

# Evento para o botão
def conectar():
    # Cria string de conexão
    sconexao = "DATABASE=" + edb.get() + \
               ";HOSTNAME=localhost;PORT=50000;PROTOCOL=TCPIP;" + \
               "UID=" + eusuario.get() + ";" + \
               "PWD=" + esenha.get()
    try:
        con = ibm_db.connect(sconexao, "", "")
        tkMessageBox.showinfo(title="Menssagem", message="Conectado com Sucesso!", parent=janela)
    except ValueError:
        tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela)

# Cria componentes
titulo = Label(formulario, text="IBM DB2 - Python")

lusuario = Label(formulario, text="Digite seu usuário:")
lsenha = Label(formulario, text="Digite sua senha:")
ldb = Label(formulario, text="Digite o database:")

eusuario = Entry(formulario)
esenha = Entry(formulario, show="*")
edb = Entry(formulario)

botao = Button(formulario, text="Conectar", command=conectar)

# Cria layout de tela
titulo.grid(row=0, sticky=W+E+N+S, pady=10)
lusuario.grid(row=1, sticky=W, padx=20)
eusuario.grid(row=1, column=1, pady=5)
lsenha.grid(row=2,sticky=W, padx=20)
esenha.grid(row=2, column=1, pady=5)
ldb.grid(row=3, sticky=W, padx=20)
edb.grid(row=3, column=1, pady=5)
botao.grid(row=5, sticky=W, pady=20, padx=20)

# Loop do tcl
mainloop()