I'm Back...

segunda-feira, 19 de julho de 2010

Olá! Seis longos meses depois, eis que surjo novamente por aqui! Sem delongas, estava atarefada em alguns projetos profissionais e sem tempo para dar uma atenção aqui. Como não tenho nenhum seguidor (se tiver, identifique-se!), acho que não foi grande falta.
Mas tô de volta, para prestar contas do que tenho aprendido por aí, sobre as novidades na minha carreira e para fortalecer a relação com outros DBA´s e simpatizantes.

Bom, começo indicando o blog/site do Giuliano: http://www.giulianocardoso.wordpress.com. Ele é DBA SQL Server, manja pra caramba e logo vai começar a atacar de Oracle também. Além de ótimo conteúdo, é um prazer pra mim divulgá-lo pelo simples fato dele ter mencionado o 'DBA de saia' como motivador para que o blog dele saísse. demais, né? Sorte aí Giuliano e vamos continuar trocando...

Mesmo tendo postado em janeiro, acabei omitindo que consegui minha primeira certificação em dezembro de 2009. Resolvi deixar de lado a 70-432 e tentei antes a 70-433. Adivinha só: Bomba!!! Caíram diversas questões de XML e de fato esta é [ainda] uma deficiência. Marquei a segunda prova pro dia 21/12/2009, apenas três dias depois da primeira. Me lembro que eu tinha tirado férias e que voltava dia 22 ao trabalho. Morreria de vergonha em voltar sem a certificação, afinal a equipe em que atuo, era quase 100% certificada em diversas tecnologias e de fato isso era importante para eles [pra mim também!]. Estudei pra caramba um fds todo... me lembro que tinha niver de amiga, amigo-secreto, participei assim, chegando no final de todos e finalmente, dia 21, passei! com 95% de acertos! Nossa, que alegria!!! Valeu a pena o esforço! Finalmente eu recebei meu MCP ID. :D

Fim de ano, festas, ano novo... Como boa representante do povo brasileiro, confesso que esperei o Carnaval passar pra voltar aos estudos. Mas fui alocada em um projeto externo e fiquei super OUT das novidades. Neste projeto infelizmente não aprendi muito. Era Oracle 9i e 10g e participei de uma certa migração de sistema, onde alguns dados tiveram que ser trazidos para a nova aplicação. O trabalho mesmo foi entender o negócio e mapear dentro da nova modelagem, os dados realmente importantes. Infelimente, meus acessos eram identicos aos dos programadores. Não tive acesso à DUMP e muito menos acessar o servidor. Tirei leite de pedra e a partir de um VBzinho que montei, consegui gerar em arquivos texto, as cargas necessárias via comandos INSERT´s. Se aprendi algo mesmo, foi em Visual Basic... rs!

Quando o projeto estava terminando, pedi dois dias pro gerente (uma sexta e uma segunda-feira) e decidi: PRECISO DA 70-432. Logo voltaria à consultoria e não queria ter perdido tanto tempo assim, sem evoluir como DBA. Assim o fiz... Estudei novamente como doida e marquei a prova para dia 31/05/2010. Tive um voucher para second shot, mas nem foi necessário usá-lo! Passei de cara!!! Com 75% de acertos.

A grande diferença entre o primeiro certificado e este segundo foi eu ter começado aulas de inglês no início de maio. Parece que não, mas senti sim uma grande diferença. Depois que comecei as aulas, sinto que tenho mais interesse em ler coisas em inglês. Busco, tento entender, leio, releio, associo palavras e assim foi de fato mais tranquilo.

Bom, neste momento, estou novamente na consultoria, num projeto com SQL Server 2008! Mas já precisei dar um suporte Oracle ao mesmo cliente... Não tem jeito, temos que conhecer de tudo. Enquanto não for especialista em algo, preciso aplicar tudo que aprendi até aqui. Embora tenha acabado de me tornar uma profissional MCTS em SQL Server 2008, operacionalmente conheço melhor o Oracle... Pretendo tentar uma prova de PL/SQL até o fim do ano.

Já tenho algumas coisas no pente para postar e volto quando tiver mais tempo!


See you later!

Novo valor para SEQUENCE

segunda-feira, 18 de janeiro de 2010

Após criar uma sequence e a utilizar por um bom tempo (Nome_SQ.nextval), tive a necessidade de deixar o valor atual da sequence idêntico ao da base de produção.

De cara tentei um ALTER na sequence:



ALTER SEQUENCE Nome_SQ MINVALUE 1 MAXVALUE 9999999999 START WITH 45562;



Mas isso não funcionou. O ALTER pode ser usado para alterar outros parâmetros de uma sequence. O valor inicial não é permitido. Então o jeito seria dropar e recriar o objeto ajustando o parâmetro START WITH.

Então, no mundo ideal com permissão DML, faria o seguinte:



DROP SEQUENCE Nome_SQ;

CREATE SEQUENCE Nome_SQ MINVALUE 1 MAXVALUE 9999999999 INCREMENT BY 1 START WITH 45562 NOCYCLE ;



Masssss, o fato é que eu não tinha permissão DML. Então, o que fazer?

Resolvi criar um bloco anônimo com um processo de looping para atingir o valor necessário e incrementá-lo à sequence via NEXTVAL:



DECLARE
x NUMBER;
y NUMBER := 45562;

BEGIN
SELECT Nome_SQ.currval into x from dual;
WHILE x < y LOOP
SELECT Nome_SQ.nextval into x from dual;
END LOOP;
END;





SELECT Nome_SQ.currval from dual;

CURRVAL
----------------------

455632



Isso só funcionou porque precisava posicionar o valor da sequence para um valor maior que o atual. Se precisasse diminuir o valor, não teria jeito, eu ou o DBA de plantão teria que dropar e recriar o objeto.


Uma boa referência está no próprio site da Oracle: http://download.oracle.com/docs/cd/B13789_01/server.101/b10759/statements_6014.htm

Recursividade - Registro pai, filhos, netos, bisnetos...

terça-feira, 1 de dezembro de 2009

O problema da vez foi o seguinte: Numa base Oracle 11g, foi identificado um furo em uma regra de negócio que consequentemente afetou a aplicação e o banco de dados. Ocorreu que uma informação pôde ser gravada duas vezes, ou seja, ficou duplicada em termos de negócio.

Identificado o problema, o cliente pediu uma query para varrer todos os registros que referenciavam de alguma forma aquele ID específico.

O agravante era que o sistema já estava em produção e em momento nenhum poderia sair do ar.

Achei que alterar as constraints para conseguir um 'Delete Cascade' seria a solução = FAIL.

Então, analisei bem a situação e o padrão de nomenclatura dos objetos deste banco. Como existia um padrão bem definido, carreguei em tabelas temporárias as informações que precisava e depois saí montando as queries. Vamos lá:

Tudo começava em um Projeto (TB_PROJETO). Então no primeiro nível carreguei as tabelas e campos pai e tabelas e campos filhos:



-- CARREGAR NIVEL 1 = pai + filhos
CREATE GLOBAL TEMPORARY TABLE TB_ADM_NIVEL_1 AS
SELECT REPLACE(R_CONSTRAINT_NAME,'PK_', '') AS TABELA_PAI_0,
REPLACE(R_CONSTRAINT_NAME AS CAMPO_PAI_0, 'PK_', 'ID_') AS CAMPO_PAI_0,
TABLE_NAME AS TABELA_FILHA_1,
REPLACE(TABLE_NAME, 'TB', 'ID') AS CAMPO_FILHA_1
FROM ALL_CONSTRAINTS WHERE OWNER = 'O-W-N-E-R' AND CONSTRAINT_TYPE = 'R' AND replace(R_CONSTRAINT_NAME,'PK_', '') = 'TB_PROJETO' ORDER BY TABLE_NAME;

No segundo nível carreguei as tabelas e campos que são filhas no nível 1 e agora pai no nível 2:

-- CARREGAR NIVEL 2 = filhos + netos
CREATE GLOBAL TEMPORARY TABLE TB_ADM_NIVEL_2 AS
SELECT REPLACE(R_CONSTRAINT_NAME,'PK_', '') AS TABELA_PAI_1,
REPLACE(R_CONSTRAINT_NAME, 'PK_TB', 'ID' AS CAMPO_PAI_1,
TABLE_NAME AS TABELA_FILHA_2,
REPLACE(TABLE_NAME, 'TB', 'ID') AS CAMPO_FILHA_2
FROM ALL_CONSTRAINTS WHERE OWNER = 'O-W-N-E-R' AND CONSTRAINT_TYPE = 'R' AND replace(R_CONSTRAINT_NAME,'PK_', '') IN (SELECT TABELA_FILHA_1 FROM TB_ADM_NIVEL_1 WHERE TABELA_FILHA_1 <> TABELA_PAI_0);

-- CARREGAR NIVEL 3 = netos + bisnetos
CREATE GLOBAL TEMPORARY TABLE TB_ADM_NIVEL_3 AS
SELECT REPLACE(R_CONSTRAINT_NAME,'PK_', '') AS TABELA_PAI_2,
REPLACE(R_CONSTRAINT_NAME, 'PK_TB', 'ID') AS CAMPO_PAI_2,
TABLE_NAME AS TABELA_FILHA_3,
REPLACE(TABLE_NAME, 'TB', 'ID') AS CAMPO_FILHA_3
FROM ALL_CONSTRAINTS WHERE OWNER = 'O-W-N-E-R' AND CONSTRAINT_TYPE = 'R' AND replace(R_CONSTRAINT_NAME,'PK_', '') IN (SELECT TABELA_FILHA_2 FROM TB_ADM_NIVEL_2 WHERE TABELA_FILHA_2 <> TABELA_PAI_1);

-- CARREGAR NIVEL 4 = bisnetos + tataranetos
CREATE GLOBAL TEMPORARY TABLE TB_ADM_NIVEL_4 AS
SELECT REPLACE(R_CONSTRAINT_NAME,'PK_', '') AS TABELA_PAI_3,
REPLACE(R_CONSTRAINT_NAME, 'PK_TB', 'ID') AS CAMPO_PAI_3,
TABLE_NAME AS TABELA_FILHA_4,
REPLACE(TABLE_NAME, 'TB', 'ID') AS CAMPO_FILHA_4
FROM ALL_CONSTRAINTS WHERE OWNER = 'O-W-N-E-R' AND CONSTRAINT_TYPE = 'R' AND replace(R_CONSTRAINT_NAME,'PK_', '') IN (SELECT TABELA_FILHA_3 FROM TB_ADM_NIVEL_3 WHERE TABELA_FILHA_3 <> TABELA_PAI_2);

-- CARREGAR NIVEL 5 = VAZIO
CREATE GLOBAL TEMPORARY TABLE TB_ADM_NIVEL_5 AS
SELECT REPLACE(R_CONSTRAINT_NAME,'PK_', '') AS TABELA_PAI_4,
REPLACE(R_CONSTRAINT_NAME, 'PK_TB', 'ID') AS CAMPO_PAI_4,
TABLE_NAME AS TABELA_FILHA_5,
REPLACE(TABLE_NAME, 'TB', 'ID') AS CAMPO_FILHA_5
FROM ALL_CONSTRAINTS WHERE OWNER = 'O-W-N-E-R' AND CONSTRAINT_TYPE = 'R' AND replace(R_CONSTRAINT_NAME,'PK_', '') IN (SELECT TABELA_FILHA_4 FROM TB_ADM_NIVEL_4 WHERE TABELA_FILHA_4 <> TABELA_PAI_3);



Criei tabelas até o nível em que não existia mais registros no último nível. No caso, a tabela TB_ADM_NIVEL_5 não possui nenhum registro e portanto pôde ser dropada. Trabalhei apenas com as tabelas com informações de nível 0 a 4.



DROP TABLE TB_ADM_NIVEL_5;


Assim, recursivamente, da tabela 4 até a 1, usei uma query para carregar uma tabela com a ordem dos registros nas tabelas que deveriam ser apagados. Uni cada query pelo comando UNION para que ele fizesse a eliminação de possíveis linhas repetidas. Definir estas querys, foi a parte mais delicada da solução pois exigiu um entendimento quanto à lógica dos relacionamentos aplicados. Sei que o uso de subquery não é uma boa prática, mas neste caso pontual, serviu muito bem.



CREATE GLOBAL TEMPORARY TABLE TB_SCRIPT_DEL_PROJETO
AS

-- APAGAR REGISTROS DAS TABELAS FILHAS NIVEL 4
SELECT DISTINCT
'DELETE ' || TN4.TABELA_FILHA_4 ||
' WHERE ' || TN4.TABELA_FILHA_4 || '.' || TN4.CAMPO_PAI_3 ||
' IN (SELECT ' || TN3.TABELA_FILHA_3 || '.' || TN4.CAMPO_PAI_3 ||
' FROM ' || TN3.TABELA_FILHA_3 ||
' WHERE ' || TN3.TABELA_FILHA_3 || '.' || TN3.CAMPO_PAI_2 ||
' IN (SELECT ' || TN2.TABELA_FILHA_2 || '.' || TN3.CAMPO_PAI_2 ||
' FROM ' || TN2.TABELA_FILHA_2 ||
' WHERE ' || TN2.TABELA_FILHA_2 || '.' || TN2.CAMPO_PAI_1 ||
' IN (SELECT ' || TN1.TABELA_FILHA_1 || '.' || TN2.CAMPO_PAI_1 ||
' FROM ' || TN1.TABELA_FILHA_1 ||
' WHERE ' || TN1.TABELA_FILHA_1 || '.ID_PROJETO = NUMERO)));' AS SCRIPT, TN4.TABELA_FILHA_4 AS TABELA, 4 AS NIVEL
FROM TB_ADM_NIVEL_1 TN1, TB_ADM_NIVEL_2 TN2, TB_ADM_NIVEL_3 TN3, TB_ADM_NIVEL_4 TN4
WHERE TN1.CAMPO_FILHA_1 = TN2.CAMPO_PAI_1
AND TN2.CAMPO_FILHA_2 = TN3.CAMPO_PAI_2
AND TN3.CAMPO_FILHA_3 = TN4.CAMPO_PAI_3

UNION

-- APAGAR REGISTROS DAS TABELAS FILHAS NIVEL 3
SELECT DISTINCT
'DELETE ' || TN3.TABELA_FILHA_3 ||
' WHERE ' || TN3.TABELA_FILHA_3 || '.' || TN3.CAMPO_PAI_2 ||
' IN (SELECT ' || TN2.TABELA_FILHA_2 || '.' || TN3.CAMPO_PAI_2 ||
' FROM ' || TN2.TABELA_FILHA_2 ||
' WHERE ' || TN2.TABELA_FILHA_2 || '.' || TN2.CAMPO_PAI_1 ||
' IN (SELECT ' || TN1.TABELA_FILHA_1 || '.' || TN2.CAMPO_PAI_1 ||
' FROM ' || TN1.TABELA_FILHA_1 ||
' WHERE ' || TN1.TABELA_FILHA_1 || '.ID_PROJETO = NUMERO));' AS SCRIPT, TN3.TABELA_FILHA_3 AS TABELA, 3 AS NIVEL
FROM TB_ADM_NIVEL_1 TN1, TB_ADM_NIVEL_2 TN2, TB_ADM_NIVEL_3 TN3
WHERE TN1.CAMPO_FILHA_1 = TN2.CAMPO_PAI_1
AND TN2.CAMPO_FILHA_2 = TN3.CAMPO_PAI_2

UNION

-- APAGAR REGISTROS DAS TABELAS FILHAS NIVEL 2
SELECT DISTINCT
'DELETE ' || TN2.TABELA_FILHA_2 ||
' WHERE ' || TN2.TABELA_FILHA_2 || '.' || TN2.CAMPO_PAI_1 ||
' IN (SELECT ' || TN1.TABELA_FILHA_1 || '.' || TN2.CAMPO_PAI_1 ||
' FROM ' || TN1.TABELA_FILHA_1 ||
' WHERE ' || TN1.TABELA_FILHA_1 || '.ID_PROJETO = NUMERO);' AS SCRIPT, TN2.TABELA_FILHA_2 AS TABELA, 2 AS NIVEL
FROM TB_ADM_NIVEL_1 TN1, TB_ADM_NIVEL_2 TN2
WHERE TN1.CAMPO_FILHA_1 = TN2.CAMPO_PAI_1

UNION

-- APAGAR REGISTROS DAS TABELAS FILHAS NIVEL 1 SEM FILHOS NIVEL 2
SELECT
'DELETE ' || TABELA_FILHA_1 ||
' WHERE ' || CAMPO_PAI_0 || ' = NUMERO;' AS SCRIPT, TABELA_FILHA_1 AS TABELA, 1 AS NIVEL
FROM TB_ADM_NIVEL_1
WHERE TABELA_FILHA_1 <> 'TB_PROJETO'

UNION

-- SCRIPT PARA APAGAR REGISTRO DA TABELA DE PROJETO
SELECT 'DELETE FROM TB_SGP_PROJETO WHERE ID_PROJETO = NUMERO;' AS SCRIPT, 'TB_SGP_PROJETO' AS TABELA, 0 AS NIVEL FROM DUAL;



Finalmente, rodei o script que realmente importava:



-- PARA RECUPERAR SCRIPT DE EXCLUSÃO - EXECUTAR O RESULTADO
SELECT SCRIPT FROM TB_SCRIPT_DEL_PROJETO ORDER BY NIVEL DESC;



Daí foi só colocar o retorno desse script em um bloco anônimo com uma variável chamada NUMERO. De acordo com o número indicado, o script no bloco varre as tabelas apagando todas as referencias em tabelas bisnetas, netas, filhas e finalmente pai:


-- INFORMAR O NUMERO DO PROJETO A SER DELETADO AQUI
DECLARE NUMERO NUMBER := 7777;

BEGIN
DELETE TB_LANC_ITEM_ORC WHERE TB_LANC_ITEM_ORC.ID_ITEM_ORC IN (SELECT TB_ITEM_ORC.ID_ITEM_ORC FROM TB_ITEM_ORC WHERE TB_ITEM_ORC.ID_ACAO_PARC IN (SELECT TB_ACAO_PARC.ID_ACAO_PARC FROM TB_ACAO_PARC WHERE TB_ACAO_PARC.ID_PROJETO_PARC IN (SELECT TB_PROJ_PARC.ID_PROJETO_PARC FROM TB_PROJ_PARC WHERE TB_PROJ_PARC.ID_PROJETO = NUMERO))) OR TB_LANC_ITEM_ORC.ID_ITEM_ORC IN (SELECT TB_ITEM_ORC.ID_ITEM_ORC FROM TB_ITEM_ORC WHERE TB_ITEM_ORC.ID_ACAO_PARC IN (SELECT TB_ACAO_PARC.ID_ACAO_PARC FROM TB_ACAO_PARC WHERE TB_ACAO_PARC.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO)));

DELETE TB_APR_MUD_ACAO_EVENTO WHERE TB_APR_MUD_ACAO_EVENTO.ID_MUD_EVENTO IN (SELECT TB_MUD_EVENTO.ID_MUD_EVENTO FROM TB_MUD_EVENTO WHERE TB_MUD_EVENTO.ID_EVENTO_ACAO IN (SELECT TB_EVENTO.ID_EVENTO_ACAO FROM TB_EVENTO WHERE TB_EVENTO.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO))) OR TB_APR_MUD_ACAO_EVENTO.ID_MUD_EVENTO IN (SELECT TB_MUD_EVENTO.ID_MUD_EVENTO FROM TB_MUD_EVENTO WHERE TB_MUD_EVENTO.ID_CONJ_MUD IN (SELECT TB_CONJ_MUD.ID_CONJ_MUD FROM TB_CONJ_MUD WHERE TB_CONJ_MUD.ID_PROJETO = NUMERO));

DELETE TB_APR_MUD_ACAO_NCM WHERE TB_APR_MUD_ACAO_NCM.ID_MUD_ACAO_NCM IN (SELECT TB_MUD_ACAO_NCM.ID_MUD_ACAO_NCM FROM TB_MUD_ACAO_NCM WHERE TB_MUD_ACAO_NCM.ID_ACAO_NCM IN (SELECT TB_ACAO_NCM.ID_ACAO_NCM FROM TB_ACAO_NCM WHERE TB_ACAO_NCM.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO))) OR TB_APR_MUD_ACAO_NCM.ID_MUD_ACAO_NCM IN (SELECT TB_MUD_ACAO_NCM.ID_MUD_ACAO_NCM FROM TB_MUD_ACAO_NCM WHERE TB_MUD_ACAO_NCM.ID_CONJ_MUD IN (SELECT TB_CONJ_MUD.ID_CONJ_MUD FROM TB_CONJ_MUD WHERE TB_CONJ_MUD.ID_PROJETO = NUMERO));

DELETE TB_APR_MUD_ITEM_ORC_ WHERE TB_APR_MUD_ITEM_ORC_.ID_MUD_ITEM_ORC IN (select ID_MUD_ITEM_ORC from TB_MUD_ITEM_ORC WHERE ID_ITEM_ORC IN (SELECT TB_ITEM_ORC.ID_ITEM_ORC FROM TB_ITEM_ORC WHERE TB_ITEM_ORC.ID_ACAO_PARC IN (SELECT TB_ACAO_PARC.ID_ACAO_PARC FROM TB_ACAO_PARC WHERE TB_ACAO_PARC.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO))));

DELETE TB_APR_MUD_PROJ_MERCADO WHERE TB_APR_MUD_PROJ_MERCADO.ID_MUD_PROJ_OBJ_MERCADO IN (SELECT TB_MUD_PROJ_OBJ_MERCADO.ID_MUD_PROJ_OBJ_MERCADO FROM TB_MUD_PROJ_OBJ_MERCADO WHERE TB_MUD_PROJ_OBJ_MERCADO.ID_PROJ_OBJ_MERCADO IN (SELECT TB_PROJ_OBJ_MERCADO.ID_PROJ_OBJ_MERCADO FROM TB_PROJ_OBJ_MERCADO WHERE TB_PROJ_OBJ_MERCADO.ID_PROJETO_PAIS IN (SELECT TB_PROJ_PAIS.ID_PROJETO_PAIS FROM TB_PROJ_PAIS WHERE TB_PROJ_PAIS.ID_PROJETO = NUMERO)))
OR TB_APR_MUD_PROJ_MERCADO.ID_MUD_PROJ_OBJ_MERCADO IN (SELECT TB_MUD_PROJ_OBJ_MERCADO.ID_MUD_PROJ_OBJ_MERCADO FROM TB_MUD_PROJ_OBJ_MERCADO WHERE TB_MUD_PROJ_OBJ_MERCADO.ID_CONJ_MUD IN (SELECT TB_CONJ_MUD.ID_CONJ_MUD FROM TB_CONJ_MUD WHERE TB_CONJ_MUD.ID_PROJETO = NUMERO))
OR TB_APR_MUD_PROJ_MERCADO.ID_MUD_PROJ_OBJ_MERCADO IN (SELECT TB_MUD_PROJ_OBJ_MERCADO.ID_MUD_PROJ_OBJ_MERCADO FROM TB_MUD_PROJ_OBJ_MERCADO WHERE TB_MUD_PROJ_OBJ_MERCADO.ID_PROJETO_PAIS IN (SELECT TB_PROJ_PAIS.ID_PROJETO_PAIS FROM TB_PROJ_PAIS WHERE TB_PROJ_PAIS.ID_PROJETO = NUMERO));

DELETE TB_APR_MUD_ACAO_PARC WHERE TB_APR_MUD_ACAO_PARC.ID_MUD_ACAO_PARC IN (SELECT TB_MUD_ACAO_PARC.ID_MUD_ACAO_PARC FROM TB_MUD_ACAO_PARC WHERE TB_MUD_ACAO_PARC.ID_ACAO_PARC IN (SELECT TB_ACAO_PARC.ID_ACAO_PARC FROM TB_ACAO_PARC WHERE TB_ACAO_PARC.ID_PROJETO_PARC IN (SELECT TB_PROJ_PARC.ID_PROJETO_PARC FROM TB_PROJ_PARC WHERE TB_PROJ_PARC.ID_PROJETO = NUMERO)))
OR TB_APR_MUD_ACAO_PARC.ID_MUD_ACAO_PARC IN (SELECT TB_MUD_ACAO_PARC.ID_MUD_ACAO_PARC FROM TB_MUD_ACAO_PARC WHERE TB_MUD_ACAO_PARC.ID_ACAO_PARC IN (SELECT TB_ACAO_PARC.ID_ACAO_PARC FROM TB_ACAO_PARC WHERE TB_ACAO_PARC.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO)))
OR TB_APR_MUD_ACAO_PARC.ID_MUD_ACAO_PARC IN (SELECT TB_MUD_ACAO_PARC.ID_MUD_ACAO_PARC FROM TB_MUD_ACAO_PARC WHERE TB_MUD_ACAO_PARC.ID_CONJ_MUD IN (SELECT TB_CONJ_MUD.ID_CONJ_MUD FROM TB_CONJ_MUD WHERE TB_CONJ_MUD.ID_PROJETO = NUMERO))
OR TB_APR_MUD_ACAO_PARC.ID_MUD_ACAO_PARC IN (SELECT TB_MUD_ACAO_PARC.ID_MUD_ACAO_PARC FROM TB_MUD_ACAO_PARC WHERE TB_MUD_ACAO_PARC.ID_PROJETO_PARC IN (SELECT TB_PROJ_PARC.ID_PROJETO_PARC FROM TB_PROJ_PARC WHERE TB_PROJ_PARC.ID_PROJETO = NUMERO));

DELETE TB_MUD_ITEM_ORC WHERE TB_MUD_ITEM_ORC.ID_ITEM_ORC IN (SELECT TB_ITEM_ORC.ID_ITEM_ORC FROM TB_ITEM_ORC WHERE TB_ITEM_ORC.ID_ACAO_PARC IN (SELECT TB_ACAO_PARC.ID_ACAO_PARC FROM TB_ACAO_PARC WHERE TB_ACAO_PARC.ID_PROJETO_PARC IN (SELECT TB_PROJ_PARC.ID_PROJETO_PARC FROM TB_PROJ_PARC WHERE TB_PROJ_PARC.ID_PROJETO = NUMERO)))
OR TB_MUD_ITEM_ORC.ID_ITEM_ORC IN (SELECT TB_ITEM_ORC.ID_ITEM_ORC FROM TB_ITEM_ORC WHERE TB_ITEM_ORC.ID_ACAO_PARC IN (SELECT TB_ACAO_PARC.ID_ACAO_PARC FROM TB_ACAO_PARC WHERE TB_ACAO_PARC.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO)))
OR TB_MUD_ITEM_ORC.ID_ACAO_PARC IN (SELECT TB_ACAO_PARC.ID_ACAO_PARC FROM TB_ACAO_PARC WHERE TB_ACAO_PARC.ID_PROJETO_PARC IN (SELECT TB_PROJ_PARC.ID_PROJETO_PARC FROM TB_PROJ_PARC WHERE TB_PROJ_PARC.ID_PROJETO = NUMERO))
OR TB_MUD_ITEM_ORC.ID_ACAO_PARC IN (SELECT TB_ACAO_PARC.ID_ACAO_PARC FROM TB_ACAO_PARC WHERE TB_ACAO_PARC.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO))
OR TB_MUD_ITEM_ORC.ID_CONJ_MUD IN (SELECT TB_CONJ_MUD.ID_CONJ_MUD FROM TB_CONJ_MUD WHERE TB_CONJ_MUD.ID_PROJETO = NUMERO);

DELETE TB_APR_MUD_ACAO_PAIS WHERE TB_APR_MUD_ACAO_PAIS.ID_MUD_ACAO_PAIS IN (SELECT TB_MUD_ACAO_PAIS.ID_MUD_ACAO_PAIS FROM TB_MUD_ACAO_PAIS WHERE TB_MUD_ACAO_PAIS.ID_ACAO_PAIS IN (SELECT TB_ACAO_PAIS.ID_ACAO_PAIS FROM TB_ACAO_PAIS WHERE TB_ACAO_PAIS.ID_PROJETO_PAIS IN (SELECT TB_PROJ_PAIS.ID_PROJETO_PAIS FROM TB_PROJ_PAIS WHERE TB_PROJ_PAIS.ID_PROJETO = NUMERO)))
OR TB_APR_MUD_ACAO_PAIS.ID_MUD_ACAO_PAIS IN (SELECT TB_MUD_ACAO_PAIS.ID_MUD_ACAO_PAIS FROM TB_MUD_ACAO_PAIS WHERE TB_MUD_ACAO_PAIS.ID_ACAO_PAIS IN (SELECT TB_ACAO_PAIS.ID_ACAO_PAIS FROM TB_ACAO_PAIS WHERE TB_ACAO_PAIS.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO)))
OR TB_APR_MUD_ACAO_PAIS.ID_MUD_ACAO_PAIS IN (SELECT TB_MUD_ACAO_PAIS.ID_MUD_ACAO_PAIS FROM TB_MUD_ACAO_PAIS WHERE TB_MUD_ACAO_PAIS.ID_CONJ_MUD IN (SELECT TB_CONJ_MUD.ID_CONJ_MUD FROM TB_CONJ_MUD WHERE TB_CONJ_MUD.ID_PROJETO = NUMERO))
OR TB_APR_MUD_ACAO_PAIS.ID_MUD_ACAO_PAIS IN (SELECT TB_MUD_ACAO_PAIS.ID_MUD_ACAO_PAIS FROM TB_MUD_ACAO_PAIS WHERE TB_MUD_ACAO_PAIS.ID_PROJETO_PAIS IN (SELECT TB_PROJ_PAIS.ID_PROJETO_PAIS FROM TB_PROJ_PAIS WHERE TB_PROJ_PAIS.ID_PROJETO = NUMERO));

DELETE TB_ITEM_ORC_ENT_COAUT WHERE TB_ITEM_ORC_ENT_COAUT.ID_ITEM_ORC IN (SELECT TB_ITEM_ORC.ID_ITEM_ORC FROM TB_ITEM_ORC WHERE TB_ITEM_ORC.ID_ACAO_PARC IN (SELECT TB_ACAO_PARC.ID_ACAO_PARC FROM TB_ACAO_PARC WHERE TB_ACAO_PARC.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO)))
OR TB_ITEM_ORC_ENT_COAUT.ID_ITEM_ORC IN (SELECT TB_ITEM_ORC.ID_ITEM_ORC FROM TB_ITEM_ORC WHERE TB_ITEM_ORC.ID_ACAO_PARC IN (SELECT TB_ACAO_PARC.ID_ACAO_PARC FROM TB_ACAO_PARC WHERE TB_ACAO_PARC.ID_PROJETO_PARC IN (SELECT TB_PROJ_PARC.ID_PROJETO_PARC FROM TB_PROJ_PARC WHERE TB_PROJ_PARC.ID_PROJETO = NUMERO)));

DELETE TB_APR_MUD_ACAO WHERE TB_APR_MUD_ACAO.ID_MUD_ACAO IN (SELECT TB_MUD_ACAO.ID_MUD_ACAO FROM TB_MUD_ACAO WHERE TB_MUD_ACAO.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO))
OR TB_APR_MUD_ACAO.ID_MUD_ACAO IN (SELECT TB_MUD_ACAO.ID_MUD_ACAO FROM TB_MUD_ACAO WHERE TB_MUD_ACAO.ID_CONJ_MUD IN (SELECT TB_CONJ_MUD.ID_CONJ_MUD FROM TB_CONJ_MUD WHERE TB_CONJ_MUD.ID_PROJETO = NUMERO));

DELETE TB_TAREFA WHERE TB_TAREFA.ID_MARCO_CRITICO IN (SELECT TB_MARCO_CRITICO.ID_MARCO_CRITICO FROM TB_MARCO_CRITICO WHERE TB_MARCO_CRITICO.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO));

DELETE TB_ACAO_PAIS WHERE TB_ACAO_PAIS.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO)
OR TB_ACAO_PAIS.ID_PROJETO_PAIS IN (SELECT TB_PROJ_PAIS.ID_PROJETO_PAIS FROM TB_PROJ_PAIS WHERE TB_PROJ_PAIS.ID_PROJETO = NUMERO);

DELETE TB_ACAO_PART WHERE TB_ACAO_PART.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO)
OR TB_ACAO_PART.ID_PROJETO_PART IN (SELECT TB_PROJ_PART.ID_PROJETO_PART FROM TB_PROJ_PART WHERE TB_PROJ_PART.ID_PROJETO = NUMERO);

DELETE TB_ANALISE_PAR_PROJETO WHERE TB_ANALISE_PAR_PROJETO.ID_PAR_PROJETO IN (SELECT TB_PAR_PROJETO.ID_PAR_PROJETO FROM TB_PAR_PROJETO WHERE TB_PAR_PROJETO.ID_CONJ_MUD IN (SELECT TB_CONJ_MUD.ID_CONJ_MUD FROM TB_CONJ_MUD WHERE TB_CONJ_MUD.ID_PROJETO = NUMERO))
OR TB_ANALISE_PAR_PROJETO.ID_PAR_PROJETO IN (SELECT TB_PAR_PROJETO.ID_PAR_PROJETO FROM TB_PAR_PROJETO WHERE TB_PAR_PROJETO.ID_PROJETO = NUMERO);

DELETE TB_AVAL_ACAO WHERE TB_AVAL_ACAO.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO);

DELETE TB_CONJ_APR_MUD WHERE TB_CONJ_APR_MUD.ID_CONJ_MUD IN (SELECT TB_CONJ_MUD.ID_CONJ_MUD FROM TB_CONJ_MUD WHERE TB_CONJ_MUD.ID_PROJETO = NUMERO);

DELETE TB_EVENTO WHERE TB_EVENTO.ID_ACAO IN (SELECT TB_ACAO.ID_ACAO FROM TB_ACAO WHERE TB_ACAO.ID_PROJETO = NUMERO);

DELETE TB_ITEM_MUD WHERE TB_ITEM_MUD.ID_CONJ_MUD IN (SELECT TB_CONJ_MUD.ID_CONJ_MUD FROM TB_CONJ_MUD WHERE TB_CONJ_MUD.ID_PROJETO = NUMERO);

DELETE TB_PAR_PROJETO WHERE TB_PAR_PROJETO.ID_CONJ_MUD IN (SELECT TB_CONJ_MUD.ID_CONJ_MUD FROM TB_CONJ_MUD WHERE TB_CONJ_MUD.ID_PROJETO = NUMERO) AND ID_PROJETO = NUMERO;

DELETE TB_PREST_CONTA WHERE TB_PREST_CONTA.ID_PROJETO_PARC IN (SELECT TB_PROJ_PARC.ID_PROJETO_PARC FROM TB_PROJ_PARC WHERE TB_PROJ_PARC.ID_PROJETO = NUMERO);

DELETE TB_PROJ_PARTICIP_SET_SECEX WHERE TB_PROJ_PARTICIP_SET_SECEX.ID_PROJETO_PART IN (SELECT TB_PROJ_PART.ID_PROJETO_PART FROM TB_PROJ_PART WHERE TB_PROJ_PART.ID_PROJETO = NUMERO);

DELETE TB_ACAO WHERE ID_PROJETO = NUMERO;

DELETE TB_PROJ_PART WHERE ID_PROJETO = NUMERO;

DELETE TB_PROJ_ENT_COA WHERE ID_PROJETO = NUMERO;

DELETE TB_PROJ_OBJ_ESTRAT WHERE ID_PROJETO = NUMERO;

DELETE TB_PROJ_PAIS WHERE ID_PROJETO = NUMERO;

DELETE TB_PROJ_PAPEL WHERE ID_PROJETO = NUMERO;

DELETE TB_PROJ_PARC WHERE ID_PROJETO = NUMERO;

DELETE TB_PROJ_FASE WHERE ID_PROJETO = NUMERO;

DELETE TB_PROJ_PARC WHERE ID_PROJETO = NUMERO;

DELETE FROM TB_PROJETO WHERE ID_PROJETO = NUMERO;
END;



Para concluir, DROP em todas as tabelas temporárias:



DROP TABLE TB_ADM_NIVEL_1;
DROP TABLE TB_ADM_NIVEL_2;
DROP TABLE TB_ADM_NIVEL_3;
DROP TABLE TB_ADM_NIVEL_4;
DROP TABLE TB_SCRIPT_DEL_PROJETO;



Mas atenção: É importante lembrar que só foi possível a criação destas queries porque foi adotado um padrão bem definido com relação à nomenclatura das tabelas, primary keys, foreign keis e nomes dos campos. Onde todas tabelas começam por "TB_", PKs por "PK_", todas as FKs por "FK_", campos identificadores por "ID_" e etc.

Nesta hora percebi o quanto valeu perder bastante tempo com a definição desta modelagem.

Estouro de espaço em campo LOB

quarta-feira, 4 de novembro de 2009

Olá, hoje quero falar de campos LOB em Oracle 9i (BLOB, CLOB e etc...). Recebi o seguinte erro abaixo para correção:

ORA-01691: unable to extend lob segment OW.SYS_LOB0000050980C00006$$ by 128 in tablespace TBS_DATA

Pelo número do erro, encontrei rapidamente no Google as soluções possíveis. Ao inserir um registro, mas não havia espaço suficiente pois o arquivo de dados especificado para a tablespace deste campo estava cheio.

Como solução poderia adicionar um novo datafile à tablespace, aumentar a o arquivo de dados ou ainda criar uma nova tablespace para os dados, outra para os campos LOB e uma nova tabela com a mesma estrutura que apontassem para estas tablespaces. Por fim, seria necessário exportar os dados da tabela antiga para a nova.

Analisei a situação e decidi por apenas aumentar o arquivo de dados.

Para saber qual(is) datafile(s) compunha(m) a tablespace, executei este script:

SELECT * FROM DBA_DATA_FILES where tablespace_name = 'TBS_DATA';
/


Resultado:



FILE_NAME FILE_ID TABLESPACE_NAME BYTES BLOCKS STATUS RELATIVE_FNO AUTOEXTENSIBLE MAXBYTES MAXBLOCKS INCREMENT_BY USER_BYTES USER_BLOCKS
------------------------------- --------------- ----------------------- --------------- ------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- -----------
C:\ORACLE\ORADATA\TBS_DATA.DBF 16 TBS_DATA 209715200 64000 AVAILABLE 16 NO 0 0 0 209715200 63992

1 rows selected



E para alterar o tamanho do datafile:
ALTER DATABASE DATAFILE 'C:\ORACLE\ORADATA\TBS_DATA.DBF' RESIZE 500M;
/


Referência:
http://www.cryer.co.uk/brian/oracle/ORA01691.htm

Varchar sem tamanho definido

sexta-feira, 30 de outubro de 2009

Responda rápido, qual o resultado deste SELECT:

DECLARE @teste varchar(40)
SET @teste = '1111111111222222222233333333334444444444'
go
SELECT CONVERT(varchar, @teste)
-- OU
SELECT CAST(@teste as varchar)

Se você respondeu '1' ou '1111111111222222222233333333334444444444', errou.

Quando não se informa o tamanho do datatype de uma variável do tipo varchar, o padrão é sempre 30. Ou seja, a resposta correta é '111111111122222222223333333333'.

Então, ao criar uma tabela não se especificar o tamanho campo de tipo varchar, seria 30 também? Este INSERT seria possível?

CREATE TABLE #teste (campo varchar)
INSERT INTO #teste VALUES ('111111111122222222223333333333')

Erro:
String or binary data would be truncated.
The statement has been terminated.

Não, quando não se especifica tamanho de uma coluna do tipo varchar, o tamanho default é 1. Portanto:


CREATE TABLE #teste (campo varchar)
GO

INSERT INTO #teste VALUES ('1');

SELECT * FROM #teste

campo
-----
1

(1 row(s) affected)


Referência: http://msdn.microsoft.com/en-us/library/ms176089%28SQL.90%29.aspx


Para finalizar, apenas como curiosidade, é possível criar uma variável ou campo em uma tabela informando um sinônimo de tipo de dados. Exemplos:

DECLARE @nome character varying(20)
SET @nome = 'Renata'
SELECT @nome

-- e ainda

CREATE TABLE #teste (
id integer,
nome character varying(40),
salario double precision
)
GO

INSERT INTO #teste (id, nome, salario) VALUES (1,'Maria', 5230.5)
GO


Neste exemplo, ao invés de int, foi colocado integer. No lugar de varchar, character varying. E no lugar de float, foi colocado double precision.

Ao verificar a estrutura da tabela criada, ela foi criada conforme o tipo de dados de sistema do SQL Server.

USE tempdb
go

SP_HELP #teste

Resultado:



Column_name Type Computed Length Prec Scale Nullable TrimTrailingBlanks FixedLenNullInSource Collation
--------------- ------------------- ----------- ------- ------- ------- ----------- ------------------- ----------------------- -----------------------------
id int no 4 10 0 yes (n/a) (n/a) NULL
nome varchar no 40 yes no yes SQL_Latin1_General_CP1_CI_AS
salario float no 8 53 NULL yes (n/a) (n/a) NULL

A lista completa de sinônimos se encontra aqui: http://msdn.microsoft.com/en-usprecision/library/ms177566%28SQL.90%29.aspxstyle

Separação de Lotes com o comando "GO"

quarta-feira, 28 de outubro de 2009

Esta semana me perguntaram: "Renata, para que serve o comando 'GO' ao final de uma query?". Titubiei e não consegui responder. Apenas disse: "É uma forma de comitar instruções que modificam estrutura do banco. Semelhante ao Commit que escreve o registro no arquivo de dados".

Diante dessa resposta mal dada, resolvi procurar pelo site do MSDN.

Sintaxe:
GO [count]
Sinaliza o término de um lote de instruções Transact-SQL para os utilitários SQL Server. O count define quantas vezes o lote acima do 'GO' deve ser executado.

Num exemplo, se executo uma query para criação de uma View e antes existe alguma outra instrução como um 'USE [Banco]', sei que a solução é colocar um 'GO' antes do 'Create View':

Tentativa 1 - Um único lote para escolha do banco de dados, criação da view e select na view

Comando:



USE B_BANCO

CREATE VIEW V_VIEW AS SELECT * FROM T_TABELA

SELECT * FROM V_VIEW


Resultado:
Msg 111, Level 15, State 1, Line 3
'CREATE VIEW' must be the first statement in a query batch.


Tentativa 2 - Adicionei ';' para tentar separar os comando

Comando:


USE B_BANCO;

CREATE VIEW V_VIEW AS SELECT * FROM T_TABELA;

SELECT * FROM V_VIEW;


Resultado:
Msg 111, Level 15, State 1, Line 3
'CREATE VIEW' must be the first statement in a query batch.


Tentativa 3 - Inclusão do comando 'GO' para separar a query em dois lotes

Comando:


USE B_BANCO;
GO


CREATE VIEW V_VIEW AS SELECT * FROM T_TABELA;

SELECT * FROM V_VIEW;


Resultado:
Msg 170, Level 15, State 1, Procedure V_VIEW, Line 2
Line 2: Incorrect syntax near ';'.


Tentativa 4 - Já conectada em B_BANCO, retirei o ';' e incluí 'GO' após o create view, criando assim três lotes

Comando:


USE B_BANCO;
GO

CREATE VIEW V_VIEW AS SELECT * FROM T_TABELA;
GO


SELECT * FROM V_VIEW;


Resultado:


SQ_DET_CLD PC_VR
----------- ---------------------------------------
1 2.00
2 2.00
4 2.00
5 2.00
6 2.00
8 43.00
9 2.00

(7 row(s) affected)



Explicação:

- Na tentativa 1, temos três instruções relacionadas e dependentes, por este motivo se encontram na mesma query. Porém, segundo o MSDN, Para CREATE DEFAULT, CREATE FUNCTION, CREATE PROCEDURE, CREATE RULE, CREATE SCHEMA, CREATE TRIGGER e CREATE VIEW não se pode combiná-los com outras instruções em um mesmo lote.

- Na tentativa 2, temos as mesmas instruções separadas por ponto e vírgula que é uma convenção usada na linguagem SQL que em Transact-SQL não é obrigatória, porém também não é errada. Mas em alguns comandos próprios da T-SQL não podem ser usados.

- Na tentativa 3, usando 'GO' depois do comando 'USE', o bloco de código foi separado entre dois lotes. Desta forma, o banco B_BANCO foi posicionado, porém a view não foi criada por haver ";" ao final do comando Create View.

- Já na tentativa 4, a retirada do ';' e a inclusão do 'GO' ao final do comando 'Create View', separou a transação em três lotes possibilitando a criação da view e exibição do resultado pelo comando Select ao final.

Apenas por curiosidade, tentei uma quinta tentativa:


Tentativa 5 - Retirada do 'GO' após o 'USE'

Comando:


USE B_BANCO

CREATE VIEW V_VIEW AS SELECT * FROM T_TABELA
GO

SELECT * FROM V_VIEW


Resultado:
Msg 111, Level 15, State 1, Line 4
'CREATE VIEW' must be the first statement in a query batch.
Msg 208, Level 16, State 1, Line 1
Invalid object name 'V_VIEW'.

Explicação:
Foram retornadas duas mensagens de erro pois existe aí dois lotes. O primeiro lote retorna erro pois conforme falado, 'CREATE VIEW' deve ser a primeira instrução de um lote. O segundo lote, do 'Select', é compilado, mas por não existir a View ainda, retorna erro.

Sobre o uso de um número como argumento count de 'GO':
Comando:


USE B_BANCO
GO

CREATE TABLE T_TABELA (texto char(5))
GO

INSERT INTO T_TABELA VALUES ('teste')
GO 4

SELECT * FROM T_TABELA




Resultado:


Beginning execution loop

(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)

Batch execution completed 4 times.
texto
-----------
teste
teste
teste
teste

(4 row(s) affected)



Referência: http://msdn.microsoft.com/pt-br/library/ms175502.aspx

Mais uma pro caderninho!

Conflito em Collation

segunda-feira, 19 de outubro de 2009

Olá! Hoje vou falar sobre Collation. Ou Agrupamento, que é responsável por definir o tipo de codificação para o conjunto de caracteres de um banco de dados, tabela ou coluna.

Obrigatoriamente, ao instalar uma instância do SQL Server (2000, 2005 ou 2008), é possível escolher a Collation a ser usada como padrão. Ao criar um banco de dados, se nada for definido/alterado, a Collation usada será a padrão escolhida para a instância. Mesmo caso para a criação de uma coluna em uma tabela, se nada for definido, a collation padrão é a do banco de dados.

Para explicar melhor, mostro um exemplo possível de diferentes Collations definidas:

  • SQL_Latin_General_CP850_CI_AS para uma instância;
  • Latin1_General_CI_AS para um database desta mesma instância;
  • SQL_Latin1_General_CP1_CI_AI para uma coluna de uma tabela deste mesmo banco de dados.

Bom, esta semana tive um problema onde através de dois bancos diferentes, uma consulta buscando dados em um Linked Server, buscava dados de uma tabela tA no banco de dados A e servidor A e os relacionava com os campos B1, B2, B3 da VIEW vB e servidor B. Sim, da View B.

Erro:
Cannot resolve collation conflict for equal to operation.

Localizei a tabela tB, representada na view vB, alterei a Collation das colunas envolvidas no select para a mesma Collation usada nas colunas A1, A2 e A3:



ALTER TABLE tB ALTER COLUMN B1 CHAR(10) COLLATE SQL_Latin1_General_CP1_CI_AI NOT NULL
GO
ALTER TABLE tB ALTER COLUMN B2 CHAR(10) COLLATE SQL_Latin1_General_CP1_CI_AI NOT NULL
GO
ALTER TABLE tB ALTER COLUMN B3 CHAR(10) COLLATE SQL_Latin1_General_CP1_CI_AI NULL
GO



Porém mesmo assim ao executar a query, o erro era o mesmo. Erro:
Cannot resolve collation conflict for equal to operation.

Percebi então que mesmo depois da alteração, nas propriedades daquela View, todas as colunas em sua estrutura ainda tinham a Collation antiga. Foi então que rodei o seguinte comando:



SP_REFRESHVIEW vB

GO



Executei novamente a query e já não havia mais conflito. \o/

Em resumo, se a alteração fosse feita no database, os objetos ali já criados permaneceriam com sua Collation anteriormente definida inalteradas. É necessário então modificar a Collation em tabela por tabela e ao final fazer Refresh nas Views.


Estes links ajudaram muito a solucionar este problema:

http://gustavomaiaaguiar.spaces.live.com/blog/cns!F4F5C630410B9865!183.entry

http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=96516

Certificação MCTS Já!

sábado, 10 de outubro de 2009

Olá, já terminei os treinamentos 2778, 6231 e 6232. Agora é hora de escolher uma prova e focar nos estudos.

Escohi fazer primeiro a 70-432 - Microsoft SQL Server 2008, Implementation and Maintenance, pois é o meu objetivo principal e grande parte das atividades que desenvolvo.

Para começar, reuni todo material que tenho como pdf´s de livros, material oficial (antigamente chamado de MOC) e ainda dei uma passadinha na livraria Cultura e dei uma bela lida no primeiro capítulo do Training Kit 70-432. Ao final de cada lição dentre os capítulo, o livro apresenta algumas questões para fixar o entendimento.

Não comprei o livro, mas deveria, pois além do ótimo conteúdo, ele traz também um CD que possui um simulado. Como este gasto não estava nos plano, decidi tentar passar no exame apenas com estudo sobre o material adquirido e os conhecimentos fresquinhos na mente. Caso, não consiga, daí sim, compro o Training Kit e pago a segunda prova.

Bom, para ajudar nessa empreitada, terça-feira marcarei a prova e depois será a corrida contra o relógio para aproveitar todo o tempo livre e fixar o conhecimento.

Existe um outro caminho para passar em certificações, seria decorando as respostas do teste. Ocorre que algumas empresas vendem um software com o banco de dados das questões das provas de cada certificação Microsoft. Sinceramente, prefiro "gastar" dinheiro (e tempo) estudando nos livros e na prática. Se tiver que passar assim, prefiro reprovar quantas vezes forem necessárias. Acabo de me lembrar que isso pode ser de família, a coitada da minha avó, resolver tirar carteira de motorista aos 65 anos, reprovou 8 vezes e na 9 vez, ela conseguiu! :)

Vó Maria e sua permissão para dirigir


Achei um site bem bacana que mostra detalhadamente o conteúdo que deve ser estudado. Taí: http://ochoco.blogspot.com/2009/02/exam-70-432-ts-microsoft-sql-server.html

Pra finalizar, o valor do Training Kit é de R$148,00 na loja da livraria Cultura e de R$135,78 pela loja virtual. <Link Aqui>
Já o teste, deve ser marcado pelo site da Prometric e custa $80,00. <Link Aqui>

Como segunda-feira é feriado, será meu SQL Server day!
Até logo!

E se Chuck Norris fosse DBA?

sexta-feira, 9 de outubro de 2009

Sei que o intuito deste espaço é mostra visão feminina dentre as atividades corriqueiras de um(a) DBA, mas, para descontrair, vejamos como os bancos de dados se comportam quando Chuck Norris está à frente.

Pra quem não sabe, Roundhouse Kick, é um golpe mortal dentre as artes marciais. Sim, mortal quando se fala em Chuck Norris (http://pt.wikipedia.org/wiki/Chute_circular):



* Chuck Norris não faz DELETEs. Ele olha para os registros e eles correm de medo.
* Chuck Norris não cria Primary Keys. Os registros simplesmente não atrevem-se a duplicar.
* Chuck Norris não usa LOG. Ele lembra de todos registros que alterou.
* Chuck Norris não cria Índices. Ele sabe que os registros retornarão o mais rápido que puderem.
* MSSQL Server é muito mais rápido que Oracle. Basta que o DBA seja CHUCK NORRIS.
* Uma vez adaptaram o Roundhouse Kick para o Oracle. Assim nasceu o TRUNCATE TABLE.
* Chuck Norris não dá DROP TABLE. Ele dá ROUNDHOUSE KICK TABLE.
* Uma vez Chuck Norris deu um Roundhouse Kick em um banco poderoso. Hoje ele é conhecido por Access.
* Chuck Norris sabe todos os erros do Oracle de cor. Porque ele os criou.
* SELECT SUM(FORÇA) FROM CHUCK_NORRIS; Internal error. Do not call the support.
* SELECT CHUCK_NORRIS; Drop database sucessful.
* SELECT ROUNDHOUSE_KICK FROM CHUC... Lost connection.
* DELETE FROM CHUCK_NORRIS. Not Found. (Ele está atrás de você, a ponto de dar um Roundhouse Kick!!!)
* Chuck Norris tem IGNORE CONTRAINTS automático. Ninguém restringe nada a Chuck Norris. Ninguém.
* Chuck Norris não faz cursos de Oracle. A Oracle é que faz cursos de Chuck Norris.
* Chuck Norris instala o Oracle sem ler o manual. (Quem você pensa que é para ter tentado isso?)
* Chuck Norris instala o Oracle em um 486. Rodando Kurumim. Em 2 minutos.
* Chuck Norris instala o MSSQL Server em um Pentium 100MHZ. Rodando Solaris. A partis dos fontes.
* Chuck Norris instala o DB2 em um 486. Rodando Windows Vista. Sem HD.
* Chuck Norris não tem Certificação. São as empresas que tentam tirar Certificação em Chuck Norris. Em vão.
* Se disser ao DBA Chuck Norris que "o problema está no banco", é melhor que esteja se referindo ao Itaú.
* Chuck Norris SABE qual o problema de performance do banco. Ele só está dando uma chance do banco de se arrepender. 5... 4... 3...
* Chuck Norris não cria Stored Procedures. Todas suas Queries já se armazenam no banco, tentando se esconder. Mas é inútil.
* TRIGGERS tem este nome porque Chuck Norris sempre ameaçava atirar no banco quando ele não fazia algo automático.
* Chuck Noris não faz Modelo de Dados. Ele encara o banco até que ele faça o modelo sozinho.
* Chuck Norris instala o Oracle sem a interface gráfica. E sem a interface texto. (Pergunte a ele você!)
* Megabyte, Gigabyte, Terabyte, Petabyte, Exabyte, Chuckbite.
* Oracle tem as versões Personal, Standard, Enterprise e ChuckNorris Edition. Mas nenhum computador é rápido o suficiente para rodar a último.



Texto extraído de: http://www.simpatico.com.br/piada.exibir.php?tipoPiada=23&codPiada=126

Erro de conexão de Java para SQL Server 2000

sexta-feira, 2 de outubro de 2009

Os principais SGBDRs podem ser usados como banco de dados de vários tipos de aplicações. Como Java, .Net, PHP e etc...

Mas, é necessário ficar atento para a versão do banco de dados que de fato pode ser usado. Normalmente nas especificações do cliente, se obtém estes dados.

Quando o banco é fornecido pela mesma empresa da aplicação, as chances de compatibilidade são grandes (Como .Net e Sql Server, ou .Net e Access).

Num espaço de uma semana, me deparei com o mesmo problema duas vezes: Aplicação Java não conecta ao banco de dados SQL Server 2000.

Como o acesso via Enterprise Manager e Query Analyser era feito normalmente pelo programador, fiquei sem entender e disse: deve ser um problema de bibliotecas do java ou algo assim.

O próprio programador foi atrás dos requisitos de sua aplicação (Websphere) e me passou a solução: era necessário atualizar o Service Pack da instância do SQL 2000 utilizada que a conexão passaria a funcionar. Dito e feito!!! Baixei o SP4 do SQL Server 2000 em http://www.microsoft.com/Downloads/details.aspx?familyid=8E2DFC8D-C20E-4446-99A9-B7F0213F8BC5&displaylang=pt-br#Overview, extrai os arquivos num diretório e executei o utilitário de atualização.

As atualizações do SQL Server 2000 são:

SP4 - versão 8.00.2039
SP3 - versão 8.00.760
SP2 - versão 8.00.534
SP1 - versão 8.00.384
RTM - versão 8.00.194

Para verificar a versão do SQL de seu servidor, em qualquer versão do SQL, execute:

SELECT @@version:



Microsoft SQL Server 2000 - 8.00.760 (Intel X86)
Dec 17 2002 14:22:05
Copyright (c) 1988-2003 Microsoft Corporation
Enterprise Edition on Windows NT 5.2 (Build 3790: Service Pack 2)


Para informações mais aprimoradas do servidor, como versão do Windows instalado e tipo de processador, execute:

EXEC master..xp_msver:


Index Name Internal_Value Character_Value
------ -------------------------------- -------------- ------------------------------------------------------------------------------------------------------------------------
1 ProductName NULL Microsoft SQL Server
2 ProductVersion 524288 8.00.760
3 Language 1033 English (United States)
4 Platform NULL NT INTEL X86
5 Comments NULL NT INTEL X86
6 CompanyName NULL Microsoft Corporation
7 FileDescription NULL SQL Server Windows NT
8 FileVersion NULL 2000.080.0760.00
9 InternalName NULL SQLSERVR
10 LegalCopyright NULL © 1988-2003 Microsoft Corp. All rights reserved.
11 LegalTrademarks NULL Microsoft® is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation
12 OriginalFilename NULL SQLSERVR.EXE
13 PrivateBuild NULL NULL
14 SpecialBuild 49807360 NULL
15 WindowsVersion 248381957 5.2 (3790)
16 ProcessorCount 1 1
17 ProcessorActiveMask 1 00000001
18 ProcessorType 586 PROCESSOR_INTEL_PENTIUM
19 PhysicalMemory 1023 1023 (1073164288)
20 Product ID NULL NULL



A lista completa de versões do SQL Sever e atualizações você encontra aqui:
http://www.sqlteam.com/article/sql-server-versions

O serviço do SQL não 'starta' depois da limpeza de disco (Disk Cleanup)

quinta-feira, 1 de outubro de 2009

Olá! vou relatar um problema que tive em um servidor de banco de dados ao executar a limpeza de disco.

Tenho uma máquina com uma instância SQL 2005 e outra SQL 2008 - ambas Enterprise. Como precisei liberar uso de memória e CPU, dei um stop em todos os serviços de SQL Server:



Por falta de espaço em disco, efeutei também a limpeza via Disk Cleanup:



Num dado momento, precisei startar o serviço do SQL 2005 que startou normalmente, mas o 2008 não.
Erro: Windows could not start the Sql Server (SQL2008) on Local Computer. For more information, review the System Event Log. If this is a non-Microsoft service, contact the service vendor, and refer to service-specific error code 3417.



Assim, após diversas buscas na web, e unindo infos de um post e outro, descobri que era necessário descomprimir os ldf´s e mdf´s do diretório DATA. Eles foram comprimidos no processo de limpeza de disco:






Pelo SQL Server Configuration Manager, startei normalmente o serviço. Poderia ser também pela ferramenta Services do Windows:



A maioria dos foruns relata comumente este erro no SQL Express 2005. Mas para 2008, não encontrei nada.

Prontinho, SQL 2008 de pé e pronto para uso. Let´s go!

LOGs gigantes, o que fazer?

terça-feira, 29 de setembro de 2009

Me deparei com a seguinte situação:

Servidor com cinco instâncias SQL, sendo três 2000 e duas 2005. Onde eu precisava criar outras duas (uma 2000 e outra 2005). A máquina tem Win 2003 Server, mas com 10 GB no C:\. Estas cinco instâncias iniciais continham os mdf´s e ldf´s na partição E:\.

Legal... Como organizar esse server?

Primeiro, foi necessário fazer a instalação de ambas instâncias.
Segundo, o cliente me disponibilizou arquivos .BAK para restaurar nessas novas instâncias (cerca de 30 de tamanhos variados).

Pouco a pouco foi restaurando os databases, mas um determinado BAK tinha um LOG gigantesco e para ajudar, os bancos tinham o Recovery Model FULL... Para o tamanho deste servidor e levando em consideração as demais instâncias, seria inviável continuar a restaurar os demais backups. Então fiz o seguite:


- Find no server para todos os LDF´s;
- Depois descobri este comandinho:
DBCC SQLPERF (LOGSPACE):



Database Name Log Size (MB) Log Space Used (%) Status
--------------- ------------- ------------------ -----------
master 0.4921875 45.43651 0
tempdb 0.4921875 58.26358 0
model 0.4921875 47.22222 0
msdb 2.242188 35.25697 0
DFIN465 0.9921875 33.95669 0
DCRD705 10.92969 93.71873 0



- Identificados os databases respectivos, reduzi o log de cada um da seguinte maneira:

USE dbTeste2
GO

SP_HELPDB dbTeste2 -- VERIFICAR DADOS DE ARQUIVOS FÍSICOS QUE COMPÕE O BANCO DE DADOS
GO



name db_size owner dbid created status compatibility_level
-------- ------------- ------- ------ ------------ -------------------------------------------------------------------------------------------------------- ------ ----------- ---------------------------------------------------------------------------------------------------- -------------------
dbTeste2 9.50 MB USER1 8 Oct 1 2009 Status=ONLINE, Updateability=READ_WRITE, UserAccess=MULTI_USER, Recovery=FULL, Version=539, Collation=SQL_Latin1_General_CP1_CI_AS, SQLSortOrder=52, IsTornPageDetectionEnabled, IsAutoCreateStatistics, IsAutoUpdateStatistics 80

name fileid filename filegroupsize maxsize growth usage
-------------- --------------------------------------------------- -------------- --------- ----------------- ---------
dbTeste2_Data 1 E:\MSSQL\BNBSQL00H\DATA\BRQ_DCRD705_Data.mdf PRIMARY 4608 KB Unlimited 1024 KB data only
dbTeste2_Log 2 F:\MSSQL\BNBSQL00H\LOG\BRQ_DCRD705_Log.ldf NULL 5120 KB Unlimited 1024 KB log only



CHECKPOINT -- EFETUA ESCRITA DE DADOS GRAVADOS NO LOG DIRETAMENTE NO DISCO
GO

BACKUP LOG dbTeste2 WITH TRUNCATE_ONLY -- LIMPA OS DADOS DO LOG
GO

DBCC SHRINKFILE(dbTeste2_Log,1) -- TRUNCAR O LOG NÃO SIGNIFICA QUE O ARQUIVO DE DADOS ESTÁ LIMPO. ESTE COMANDO DIMINUI O ARQUIVO FÍSICO PARA O TAMANHO ESPECIFICADO DE 1 MB
GO

DBCC SHRINKFILE(dbTeste2_Log,1) -- NOVAMENTE EXECUTADO ESTE COMANDO PARA GARANTIR O PROCESSO
GO

SP_HELPDB dbTeste2 -- VERIFICAR NOVAMENTE O TAMANHO DOS DADOS

Repara que a coluna size para o log agora tem 1024 KB!

Pronto... depois disso consegui seguir com minha atividade.

Próximo passo é migrar estas novas intâncias para um novo servidor, afinal, o limite está próximo.

DBA de saia

Olá... Sou Renata, formada em "Criação e Desenvolvimento de Web Sites" pela Anhembi Morumbi, pós graduada em "Administração em Banco de Dados Oracle" pela FIAP e após um curso de 1 ano como cabeleireira (para desbaratinar), estou concluindo os cursos de SQL Server 2008 na Braz e Figueiredo. Os cursos são 6231, 6232 e 2778.

Gente, adoro SQL Server, plataforma Microsoft e coisas usuais, intuitivas, interfaces gráficas e tudo o que facilita a vida da gente.

DBA´s das antigas gostam muito de montar e rodar scripts manualmente. Me encaixo em outra geração, a que preza a facilidade e rapidez, sem deixar de lado a qualidade.

Embora ainda trabalhe com Oracle, gosto mais do SQL e pretendo ser uma excelente DBA.

Este blog foi criado para ajudar a mim e a quem se encaixar nesse perfil!

Lets Go!!!