Fórum Será isso um BUG no Firebird? #54147

06/12/2005

0

colegas, eu estava tentando ajudar um outro colega a respeito de uma dúvida numa instrução SQL e encontrei um problema que não pude solucionar.

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)
depois de executada essa instrução, deveria haver [b:88d1fec4b4]4 registros[/b:88d1fec4b4] com depto=1 e [b:88d1fec4b4]5 registros[/b:88d1fec4b4] com depto=2, certo?

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

Emerson Nascimento

Responder

Posts

06/12/2005

Vinicius2k

Emerson,

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+


Responder

Gostei + 0

07/12/2005

Emerson Nascimento

recursividade na execução dessa instrução? acho isso estranho...

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.


Responder

Gostei + 0

07/12/2005

Vinicius2k

Emerson,

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+


Responder

Gostei + 0

07/12/2005

Thomaz_prg

Colegas, desculpem mas também não vejo isso como um erro, deixem-me dizer o porque:

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??


Responder

Gostei + 0

07/12/2005

Vinicius2k

Só que fiquei curioso, Vinicius quais bancos tiveram resultados diferentes desse??

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+


Responder

Gostei + 0

07/12/2005

Emerson Nascimento

Colegas, desculpem mas também não vejo isso como um erro, deixem-me dizer o porque: A subselect (como já dito pelo Vinicius) é executada para cada registro pego pelo comando Update

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!


Responder

Gostei + 0

07/12/2005

Martins

Concordo com o [b:ff75b8da48]Emerson[/b:ff75b8da48], esse resultado é no mínimo estranho:
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.


Responder

Gostei + 0

07/12/2005

Vinicius2k

Colegas,
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.

[i:31a22eb5dd]se executarmos essa instrução: update tabelateste set depto=1 where extract(year from data)=2005 and depto=1[/i:31a22eb5dd] funcionou. ela executa e pára. porque? porque é feita a seleção dos registros conforme o where e, depois, nesses registros selecionados, é aplicada a atualização no campo. se houvesse recursividade essa instrução geraria um loop infinito!

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+


Responder

Gostei + 0

07/12/2005

Emerson Nascimento

não Vinícius, a discussão não é com você.
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]


Responder

Gostei + 0

07/12/2005

Thomaz_prg

Mas Emerson, veja só... quando você coloca da forma que você disse:

update tabelateste set depto=(select max(campo) from tabela where blá, blá, blá) where extract(year from data)=2005


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...


Responder

Gostei + 0

07/12/2005

Emerson Nascimento

certo, thomaz_prg.

e o que me diz disso abaixo? não estou fazendo qualquer alteração nos registros, porém o resultado não é o esperado.

não Vinícius, a discussão não é com você. a discussão é sobre o conceito. estou querendo dizer exatamente isso: [b:0dab24b3e1]NÃO HÁ RECURSIVIDADE nesse tipo de instrução.[/b:0dab24b3e1] [color=red:0dab24b3e1][b:0dab24b3e1]o problema está na aplicação do first na subselect.[/b:0dab24b3e1][/color:0dab24b3e1] 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:0dab24b3e1]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:0dab24b3e1] o mecanismo traz 6 registros!!! todos o que tem depto=1 e ano=2005. e nesse caso não está acontecendo nenhum [i:0dab24b3e1]update[/i:0dab24b3e1], 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:0dab24b3e1]ou seja: BUG!!! ERRO!!![/b:0dab24b3e1]



Responder

Gostei + 0

07/12/2005

Martins

Colegas, 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+


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!!!


Responder

Gostei + 0

07/12/2005

Thomaz_prg

Colega Emerson.En...
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.


Responder

Gostei + 0

07/12/2005

Thomaz_prg

Realmente colega, devo adimitir... é uma falha geral... eu estava testando com 2 tabelas... não havia testado com a mesma... quando testei com a mesma tabela.... BOOOM! O erro tava lá...

Fica aí minhas desculpas...


Responder

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
e agora executando a seguinte instrução: [i:c30d93925f]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:c30d93925f] o mecanismo traz 6 registros!!! todos o que tem depto=1 e ano=2005. e nesse caso não está acontecendo nenhum [i:c30d93925f]update[/i:c30d93925f], certo? então a afirmação da recursividade não faz sentido, pois não é feita alteração nos dados.

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+


Responder

Gostei + 0

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar