Fórum Será isso um BUG no Firebird? #54147
06/12/2005
0
tomemos como exemplo essa tabela:
CREATE GENERATOR GEN_TABELATESTE_ID; CREATE TABLE TABELATESTE ( ID INTEGER NOT NULL, DEPTO INTEGER, DATA DATE ); CREATE TRIGGER TABELATESTE_BI FOR TABELATESTE ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (NEW.ID IS NULL) THEN NEW.ID = GEN_ID(GEN_TABELATESTE_ID,1); END
com esses registros:
ID DEPTO DATA -------------------------- 1 1 05/11/2005 2 1 05/11/2005 3 1 05/11/2005 4 1 05/11/2005 5 1 05/11/2005 6 1 05/11/2005 7 2 05/12/2005 8 2 05/12/2005 9 2 05/12/2005
fazendo:
[i:88d1fec4b4]select * from tabelateste
where depto= 1 and extract(year from data)=2005[/i:88d1fec4b4]
retornará [b:88d1fec4b4]6[/b:88d1fec4b4] registros
[i:88d1fec4b4]select * from tabelateste
where depto=2 and extract(year from data)=2005[/i:88d1fec4b4]
retornará [b:88d1fec4b4]3[/b:88d1fec4b4] registros
então temos [b:88d1fec4b4]6 registros[/b:88d1fec4b4] no depto=1 e [b:88d1fec4b4]3 registros[/b:88d1fec4b4] no depto=2, para o ano de 2005, certo?
agora preciso passar os dois registros de menor ID cujo depto=1, para depto=2.
assim terei 4 registros com depto=1 e 5 registros com depto=2.
fiz a seguinte instrução:
[i:88d1fec4b4]select first 2 id from tabelateste
where depto=1 and extract(year from data)=2005
order by id[/i:88d1fec4b4]
retornou os 2 registros de menor ID cujo depto=1 e o ano=2005 (no caso, IDs 1 e 2)
como mencionado acima, eu preciso passar esses dois registros para depto=2.
então, com a instrução abaixo, vou alterar esses dois registros de depto=1 para depto=2.
update tabelateste set depto=2 where extract(year from data)=2005 and id in (select first 2 id from tabelateste where depto=1 and extract(year from data)=2005 order by id)
façamos novamente:
[i:88d1fec4b4]select * from tabelateste
where depto=1 and extract(year from data)=2005[/i:88d1fec4b4]
retornou [b:88d1fec4b4]0[/b:88d1fec4b4] registros!!!
[i:88d1fec4b4]select * from tabelateste
where depto=2 and extract(year from data)=2005[/i:88d1fec4b4]
retornou [b:88d1fec4b4]9[/b:88d1fec4b4] registros!!!
imagine a minha surpresa ao ver que o update não obedeceu o FIRST!
ele alterou todos os registros que tinham depto=1 para depto=[b:88d1fec4b4]2[/b:88d1fec4b4]!!!
deveria alterar somente os de ID 1 e 2!!!
isso é um BUG ou algum conceito do banco que eu desconheço?!?!?!?
qualquer explicação será bem-vinda.
foi utilizado [b:88d1fec4b4][color=blue:88d1fec4b4]Firebird 1.5.2.4731[/color:88d1fec4b4][/b:88d1fec4b4]
Emerson Nascimento
Curtir tópico
+ 0Posts
06/12/2005
Vinicius2k
Não sei se poderia ser considerado um bug já que, no meu ponto de vista, é uma questão de lógica:
Como o SUBSELECT esta sendo realizado de forma recursiva na própria tabela a ser atualizada vem o *problema*:
1. A 1ª linha da tabela entra em analise e a SUBSELECT é executada e se a condição estabelecida for atendida, a linha é atualizada.
2. A 2ª linha da tabela entra em analise e a [b:543603b737]SUBSELECT é executada novamente[/b:543603b737], porém a partir deste ponto ela não mais retorna as mesmas linhas de antes, pois a coluna DEPTO da 1ª linha não contém mais o mesmo valor.
.
.
.
Esta operação irá se repetir para cada linha da tabela que satisfaça sua primeira condição ([i:543603b737]where extract(year from data)=2005[/i:543603b737]) e como ela está sendo atualizada ao mesmo tempo que selecionada, o retorno da seleção pode ser dinâmico.
Eu me recordo de ter lido sobre isso em algum dos Release Notes do FB. Recriei a sua situação no FB e comprovei o *problema*. Por curiosidade, irei testar a mesma lógica em outros SGBDs que trabalho para observar o comportamento destes em relação à esta situação.
*Acho* que a única forma de ser obdecida a idéia pretendida seria se a SUBSELECT trabalhasse em uma transação diferente do UPDATE, consequentemente, não sendo afetada por ele, mas não faço idéia se isto seria possível.
T+
Gostei + 0
07/12/2005
Emerson Nascimento
no momento da execução ele deveria montar a subselect, aplicar o resultado no where e pronto. não é isso?
eu já trabalhei com o IN e subselect de diversas formas e nunca havia percebido essa recursividade.
de qualquer forma, obrigado pela explicação Vinicius.
Gostei + 0
07/12/2005
Vinicius2k
Como eu disse que faria, recriei o cenário nos demais SGBDs que trabalho e o resultado...
O que você esperava que acontecesse, realmente, acontece no MS-SQL Server 2000 e no PosgreSQL 8: apenas as duas primeiras linhas da tabela são afetadas pelo UPDATE.
Também encontrei aonde eu havia dito que me lembrava de ter lido sobre isto (vide página 12): http://firebird.sourceforge.net/downloads/Firebird_v1_ReleaseNotes_Pt.pdf
Parece que a lógica seguida pelo engine do FB é a mesma que imaginei, mas agora também vejo que, realmente, é uma deficiência.
Talvez isto mude nas próximas versões por estar atrelado a um dos recursos novos implementados no FB (em relação ao IB) que é o FIRST/SKIP. De qualquer forma, se for considerado um BUG, ele é conhecido, já que está presente até mesmo no Release Notes.
T+
Gostei + 0
07/12/2005
Thomaz_prg
A subselect (como já dito pelo Vinicius) é executada para cada registro pego pelo comando Update. Sendo assim, quando o update pega o primeiro registro, os 2 valores constantes na tabela que atendem a sua restrição (depto=1 and extract(year from data)=2005 ) são os de ID 1 e 2. Então o Registro com ID = 1 é passado para Depto = 2. Então para o próximo registro (de ID = 2) a subselect é executada novamente. Porém dessa vez o registro com ID = 1 está com Depto = 2, sendo assim, ele está descartado, sendo pegos os ID´s 2 e 3, e assim sucessivamente (como já dito pelo colega Vinicius). Esse comportamento (como já citado pelo Vinicius) não é unicamente do Firebird. Testei o mesmo com DB2 e foi a mesma coisa.
Para esse caso, você deveria usar uma procedure (que ficaria bem pequena, mais exatamente 2 linhas). No DB2 fiz assim para funcionar:
BEGIN ATOMIC DECLARE VALOR INT; SET VALOR = (SELECT MAX(ID) FROM TABELATESTE WHERE DEPTO=1 AND YEAR(DATA)=2005 FETCH FIRST 2 ROWS ONLY); UPDATE TABELATESTE SET DEPTO = 2 WHERE ID <= VALOR; END!
Como podem ver, tive que criar uma variável e atribuir o valor a ela. Fiz o mesmo no firebird, e funcionou 100¬.
Só que fiquei curioso, Vinicius quais bancos tiveram resultados diferentes desse??
Gostei + 0
07/12/2005
Vinicius2k
Thomaz, eu recriei a situação no [b:92428a7714]SQL Server 2000[/b:92428a7714] e no [b:92428a7714]PostgreSQL 8.1[/b:92428a7714] e ambos se comportaram como o Emerson imagina que deveria ser no FB.
Como eu disse, não acho que seja um bug, apenas uma questão da lógica implementada pelo engine. Penso que seja uma deficiência pois esta lógica, no meu ponto de vista, dá margem à erros como este e até mesmo o apresentado no Release Notes e ainda acaba por exigir uma SP em uma operação que, teorioricamente, seria simples.
Bem, temos testes com com 4 SGBDs e está empatado em 2x2:
FB e DB2 comportam-se de uma forma e SQL Server e PostgreSQL de outra.
Gostaria de testar isto no Oracle...
T+
Gostei + 0
07/12/2005
Emerson Nascimento
isso não pode ser verdadeiro. minha subselect faz parte da instrução principal.
se fosse algo assim:
[i:f198d6a4cc]update tabelateste set depto=(select max(campo) from tabela where blá, blá, blá)
where extract(year from data)=2005[/i:f198d6a4cc]
eu concordo que dessa forma a subselect deve ser executada para cada resgistro.
mas no exemplo que eu passei a subselect é uma condição do [i:f198d6a4cc]where[/i:f198d6a4cc] principal, e não do [i:f198d6a4cc]set[/i:f198d6a4cc]:
[i:f198d6a4cc]update tabelateste set depto=2
where extract(year from data)=2005
and id in
(select first 2 id from tabelateste
where depto=1 and extract(year from data)=2005
order by id)[/i:f198d6a4cc]
conceitualmente falando, é sim uma falha.
tomemos como exemplo os mesmos registros passados anteriormente:
ID DEPTO DATA -------------------------- 1 1 05/11/2005 2 1 05/11/2005 3 1 05/11/2005 4 1 05/11/2005 5 1 05/11/2005 6 1 05/11/2005 7 2 05/12/2005 8 2 05/12/2005 9 2 05/12/2005
se executarmos essa instrução:
[i:f198d6a4cc]update tabelateste set depto=1
where extract(year from data)=2005
and depto=1[/i:f198d6a4cc]
funcionou. ela executa e pára.
porque? porque é feita a seleção dos registros conforme o [i:f198d6a4cc]where[/i:f198d6a4cc] e, depois, nesses registros selecionados, é aplicada a atualização no campo.
se houvesse recursividade essa instrução geraria um loop infinito!
Gostei + 0
07/12/2005
Martins
update tabelateste set depto=2 where extract(year from data)=2005 and id in (select first 2 id from tabelateste where depto=1 and extract(year from data)=2005 order by id)
nessa parte da instrução:
and id in (select first 2 id from tabelateste where depto=1 and extract(year from data)=2005
O ID deveria está entre os dois primeiros da tabelateste, já que a instrução [b:ff75b8da48]first 2[/b:ff75b8da48] deveria selecionar os dois primeiros registros.
ID DEPTO DATA -------------------------- 1 1 05/11/2005 2 1 05/11/2005
Se não for um Bug, deve ser por baixo uma deficiência do FB q deve ser corrigido nas próximas versões.
Se alguém conseguir uma correção ou uma forma de fazer funcionar para obter o resultado esperado, postem aqui.
Gostei + 0
07/12/2005
Vinicius2k
Acho que vocês não estão me compreendendo...
Eu não estou ´defendendo´ a forma com que o engine do FB trabalha esta instrução. Eu apenas busquei explicar, conforme eu já havia lido a respeito, a lógica que é seguida por ele. Lógica esta que, no meu entender, é deficiente.
Concordo Emerson, mas o lógica estranha que leva ao problema está no FIRST, pelo que eu pude entender. Problema não existente no ´TOP´ do SQL Server ou no ´LIMIT´ do PostgreSQL.
T+
Gostei + 0
07/12/2005
Emerson Nascimento
a discussão é sobre o conceito. estou querendo dizer exatamente isso:
[b:3afe19e149]NÃO HÁ RECURSIVIDADE nesse tipo de instrução.[/b:3afe19e149]
[color=red:3afe19e149][b:3afe19e149]o problema está na aplicação do first na subselect.[/b:3afe19e149][/color:3afe19e149]
tomando como exemplo aqueles mesmos registros:
ID DEPTO DATA -------------------------- 1 1 05/11/2005 2 1 05/11/2005 3 1 05/11/2005 4 1 05/11/2005 5 1 05/11/2005 6 1 05/11/2005 7 2 05/12/2005 8 2 05/12/2005 9 2 05/12/2005
e agora executando a seguinte instrução:
[i:3afe19e149]select * from tabelateste
where id in
(select first 2 id from tabelateste
where depto=1 and extract(year from data)=2005
order by id)[/i:3afe19e149]
o mecanismo traz 6 registros!!! todos o que tem depto=1 e ano=2005.
e nesse caso não está acontecendo nenhum [i:3afe19e149]update[/i:3afe19e149], certo? então a afirmação da recursividade não faz sentido, pois não é feita alteração nos dados.
deveria ter trazido somente os registros com ID igual 1 e 2.
[b:3afe19e149]ou seja: BUG!!! ERRO!!![/b:3afe19e149]
Gostei + 0
07/12/2005
Thomaz_prg
Você estaria usando um calculo para atualização do campo... ou seja, a subselect estaria sendo feita para cada campo, para alteração de valor.
QUando se tem uma tabela desse jeito:
ID DEPTO VALOR -------------------------------- 1 1 100 2 1 100 3 2 50
e você faz uma instrução do tipo:
UPDATE TABELA SET VALOR = 100 WHERE DEPTO = 2
Espera-se que o banco filtre todos os registros, trazendo todos os registros para atualização, ou seja, traga um cursor com o dado
ID DEPTO VALOR -------------------------------- 3 2 50
e só após isso faça a atualização (a lógica que você esperava).
Porém, bancos como o Firebird/Interbase, DB2 (Tão conceituado quanto Oracle e/ou SQL-Server) tratam de forma diferente... A medida que vão ´varrendo´ a tabela, já vão alterando os valores (se obedecerem as regras das restrições). Isso afeta diretamente o resultado da subselect, que é executado para cada registro. Isso traz uma brecha como você disse, mas usando a lógica que você esperava, também poderiamos ter considerados uma brecha, pois o que se espera é uma atualização em massa, sendo que a atualização é feita 1 a 1. Digo que poderia ser considerado uma brecha, pois, ao executar novamente a subselect, o resultado da mesma, está de acordo com a primeira (não considerando as atualizações/alterações já ocorridas). É como o Vinicius disse, não é defendendo, mas é apenas questão de ponto de vista...
Se alguém puder testar no Oracle para apresentar os resultados... porque MySql ´não vale´... é o mesmo que considerar o Interbase, pois o MySql usa mais ou menos a mesma lógica do Postgree...
Gostei + 0
07/12/2005
Emerson Nascimento
e o que me diz disso abaixo? não estou fazendo qualquer alteração nos registros, porém o resultado não é o esperado.
ID DEPTO DATA -------------------------- 1 1 05/11/2005 2 1 05/11/2005 3 1 05/11/2005 4 1 05/11/2005 5 1 05/11/2005 6 1 05/11/2005 7 2 05/12/2005 8 2 05/12/2005 9 2 05/12/2005
Gostei + 0
07/12/2005
Martins
Concordo com vc Vinicius, é uma deficiência mesmo da função [b:eff1fe7c80]First[/b:eff1fe7c80], espero q essa deficiência seja corrigida nas próximas versões, lembrando q ela pelo menos até onde vi, só ocorre em um subselect, quando está no select principal funciona, mas quando está em um subselect é pau!!!!
Valew!!!
Gostei + 0
07/12/2005
Thomaz_prg
Você tem toda razão... é realmente um bug, um erro.... MAS NÃO DA SUBSELECT, mas sim da função extract do Firebird... tente trocar o campo data, por qualquer outro campo e verá que o subselect trará os resultados desejados... o problema realmente existe, mas é com relação ao Extract... testei em 2 versões do Firebird (1.5.2 e a 2.0-ainda em testes) e as 2 tem esses erros... mas realmente é falha do Extract... testei com uma UDF e funcionou blz.
Gostei + 0
07/12/2005
Thomaz_prg
Fica aí minhas desculpas...
Gostei + 0
07/12/2005
Vinicius2k
ID DEPTO DATA -------------------------- 1 1 05/11/2005 2 1 05/11/2005 3 1 05/11/2005 4 1 05/11/2005 5 1 05/11/2005 6 1 05/11/2005 7 2 05/12/2005 8 2 05/12/2005 9 2 05/12/2005
Concordo. [b:c30d93925f]É bug mesmo[/b:c30d93925f], não há nada de lógica ou recursividade. Mas creio (e pelo que entendi, você também) que o bug é no FIRST quando utilizado em SUBSELECT e não no EXTRACT como dito pelo Thomaz.
T+
Gostei + 0
Clique aqui para fazer login e interagir na Comunidade :)