O que há de errado no meu select com left join?
Bom dia amigos, estou tentando juntar à tabela de vendas, o nome dos clientes para mostrar em um grid.
Quando executo me retorna o seguinte erro:
Segue meu select:
Vale lembrar que ambas as tabela possuem como chave primária o campo CODEMP
Um abraço
Mario[/code]
Quando executo me retorna o seguinte erro:
Ambiguous field name between table Vendas an table Clientes CODEMP
Segue meu select:
´Select Vendas.*,Clientes.NomCli FROM Vendas Left Join Clientes ON (Vendas.CodCli=Clientes.CodCli and Vendas.CodEmp=Clientes.CodEmp) WHERE CodEmp=:vCodEmp AND Pedido=:vPedido´
Vale lembrar que ambas as tabela possuem como chave primária o campo CODEMP
Um abraço
Mario[/code]
Aldus
Curtidas 0
Respostas
Vinicius2k
03/01/2005
Mario,
Como ambas possuem a coluna CODEMP, vc precisa determinar na condição WHERE a qual coluna pertence a tabela :
Espero ter ajudado...
T+
Como ambas possuem a coluna CODEMP, vc precisa determinar na condição WHERE a qual coluna pertence a tabela :
select VENDAS.*, CLIENTES.NOMCLI from VENDAS left join CLIENTES on (VENDAS.CODCLI = CLIENTES.CODCLI and VENDAS.CODEMP = CLIENTES.CODEMP) where (VENDAS.CODEMP = :vcodemp and VENDAS.PEDIDO = :vpedido)
Espero ter ajudado...
T+
GOSTEI 0
Aldus
03/01/2005
Olá Vinicius, o erro sumiu mas apenas o cliente numero 1 é colocado no dbgrid.
Segue o código.
O que estaria errado?
Atenciosamente
Mario
Segue o código.
´Select Vendas.*,Clientes.NomCli FROM Vendas Left Join Clientes ON (Vendas.CodCli=Clientes.CodCli and Vendas.CodEmp=Clientes.CodEmp) WHERE (Vendas.CodEmp=:vCodEmp AND Vendas.Pedido=:vPedido)´
O que estaria errado?
Atenciosamente
Mario
GOSTEI 0
Vinicius2k
03/01/2005
Mario,
Me parece que o retorno de apenas um registro pela query está correto de acordo com a instrução SQL que vc passou...
Vc informa na condição WHERE um parametro com o número do pedido e, a não ser que vc tenha números de pedidos duplicados, somente um registro será retornado. Concorda?
T+
Me parece que o retorno de apenas um registro pela query está correto de acordo com a instrução SQL que vc passou...
Vc informa na condição WHERE um parametro com o número do pedido e, a não ser que vc tenha números de pedidos duplicados, somente um registro será retornado. Concorda?
T+
GOSTEI 0
Aldus
03/01/2005
Concordo plenamente Vinicius, só que eu retirei o codemp e mesmo assim só me mostra o cliente 1
Veja o código:
O que eu poderia mudar?
Mario
Veja o código:
´Select Vendas.*,Clientes.NomCli FROM Vendas Left Join Clientes ON (Vendas.CodCli=Clientes.CodCli and Vendas.CodEmp=Clientes.CodEmp) WHERE (Vendas.CodEmp=:vCodEmp)
O que eu poderia mudar?
Mario
GOSTEI 0
Vinicius2k
03/01/2005
Mario,
Eu não vejo motivos para que a consulta não funcione, a menos que os dados existentes na tabela não satisfaçam a condição do WHERE...
Não atender à todas as condições do WHERE é a única razão (lógica) para que a query não retorne todos os registros que vc espera que retone...
Vc já verificou a existencia dos dados dentro da tabela?
Já tentou a execução da mesma query em uma ferramenta de administração do banco de dados ao invés da aplicação?
Tente executar a query sem nenhum parametro :
Existe também a possibilidade de que eu não esteja entendendo a lógica da sua consulta... Sua intenção é trazer todas as vendas com o respectivo nome do cliente para qual ela foi realizada? Foi desta forma que eu entendi...
Então, se só existir uma venda que satisfaça ao WHERE apenas ela será exibida...
Se a lógica não for esta, creio que vc foi pelo ´caminho errado´ e vale a pena a leitura deste tópico, para que vc entenda bem as diferenças entre os tipos de joins :
http://delphiforum.icft.com.br/forum/viewtopic.php?t=49308
T+
Eu não vejo motivos para que a consulta não funcione, a menos que os dados existentes na tabela não satisfaçam a condição do WHERE...
Não atender à todas as condições do WHERE é a única razão (lógica) para que a query não retorne todos os registros que vc espera que retone...
Vc já verificou a existencia dos dados dentro da tabela?
Já tentou a execução da mesma query em uma ferramenta de administração do banco de dados ao invés da aplicação?
Tente executar a query sem nenhum parametro :
select VENDAS.*, CLIENTES.NOMCLI from VENDAS left join CLIENTES on (VENDAS.CODCLI = CLIENTES.CODCLI and VENDAS.CODEMP = CLIENTES.CODEMP)
Existe também a possibilidade de que eu não esteja entendendo a lógica da sua consulta... Sua intenção é trazer todas as vendas com o respectivo nome do cliente para qual ela foi realizada? Foi desta forma que eu entendi...
Então, se só existir uma venda que satisfaça ao WHERE apenas ela será exibida...
Se a lógica não for esta, creio que vc foi pelo ´caminho errado´ e vale a pena a leitura deste tópico, para que vc entenda bem as diferenças entre os tipos de joins :
http://delphiforum.icft.com.br/forum/viewtopic.php?t=49308
T+
GOSTEI 0
Aldus
03/01/2005
Olá Vinicius, o que está ocorrendo é o seguinte:
Quando chamo o dbgrid coloquei a seguinte sql:
A lista veio corretamente, só que chamo novamente a lista dá o erro ´abstract error´.
Outro exemplo: chamo a lista mostra corretamente, aborto essa lista e incluo um novo pedido, ao ver a lista novamente, aparece apenas o nome do último cliente.
Tô perdido, se quiseres te envio o fonte pra você olhar.
Atenciosamente
Mario
Quando chamo o dbgrid coloquei a seguinte sql:
Select Vendas.*,Clientes.NomCli FROM Vendas Left Join Clientes ON (Vendas.CodCli=Clientes.CodCli and Vendas.CodEmp=Clientes.CodEmp) WHERE (Vendas.CodEmp=:vCodEmp) ORDER BY Vendas.Data+Vendas.Pedido DESC
A lista veio corretamente, só que chamo novamente a lista dá o erro ´abstract error´.
Outro exemplo: chamo a lista mostra corretamente, aborto essa lista e incluo um novo pedido, ao ver a lista novamente, aparece apenas o nome do último cliente.
Tô perdido, se quiseres te envio o fonte pra você olhar.
Atenciosamente
Mario
GOSTEI 0
Vinicius2k
03/01/2005
Olá Mario,
Qual o Banco de Dados ?
Se for Interbase, Firebird com IBX ou dbExpress ou MS SQL Server com ADO, eu poderei lhe ajudar, caso contrário, creio que ver o fonte não ´me diria´ nada...
Agora este ´Abstract Error´ eu nunca vi...
O que exatamente vc quer dizer com ´chamo novamente´? Lembre-se de que para passar novamente a SQL para a query, vc precisa fechá-la, passar a nova instrução, passar os parametros e reabrí-la... não seria algum procedimento faltante nesta sequência ?
T+
Qual o Banco de Dados ?
Se for Interbase, Firebird com IBX ou dbExpress ou MS SQL Server com ADO, eu poderei lhe ajudar, caso contrário, creio que ver o fonte não ´me diria´ nada...
Agora este ´Abstract Error´ eu nunca vi...
O que exatamente vc quer dizer com ´chamo novamente´? Lembre-se de que para passar novamente a SQL para a query, vc precisa fechá-la, passar a nova instrução, passar os parametros e reabrí-la... não seria algum procedimento faltante nesta sequência ?
T+
GOSTEI 0
Aldus
03/01/2005
Vinicius, deixa eu me explicar, ficou confuso.
Uso delphi7, firebird 1.5 e dbexpress.
Utilizo assim, o usuário está no campo pedido, pressionando zero e enter mostra a lista de pedidos num dbgrid, estando na lista só é aceitável esc ou enter. Eu pressiono esc, sai do dbgrid e retorna ao campo pedido. Daí repito a operação, zero e enter para ver a lista de novo. Aqui ocorre um erro assim: ´acess violation at adress XXXX in module siga.exe. read of adress FFFFFF´.
E veja bem apenas chamei a lista sai e acessei de novo:
Segue o código de acesso a rotina de consulta, essa mesma unit uso para todas as consultas no sistemas, e apenas nessa que está usando um join aparece esse erro:
Confira por favor esse código:
Criação: Application.CreateForm(TfConsulta, fConsulta);
Execução: fConsulta.ShowModal = mrOk
Destruição: fConsulta.Release;
E lá na unit fconsulta faço assim:
O código é grande mas é compreensivo.
O select no arquivo de vendas está correto assim, é a maneira correta de fazer?
Um abraço
Mario
Uso delphi7, firebird 1.5 e dbexpress.
Utilizo assim, o usuário está no campo pedido, pressionando zero e enter mostra a lista de pedidos num dbgrid, estando na lista só é aceitável esc ou enter. Eu pressiono esc, sai do dbgrid e retorna ao campo pedido. Daí repito a operação, zero e enter para ver a lista de novo. Aqui ocorre um erro assim: ´acess violation at adress XXXX in module siga.exe. read of adress FFFFFF´.
E veja bem apenas chamei a lista sai e acessei de novo:
Segue o código de acesso a rotina de consulta, essa mesma unit uso para todas as consultas no sistemas, e apenas nessa que está usando um join aparece esse erro:
Confira por favor esse código:
Application.CreateForm(TfConsulta, fConsulta); fConsulta.vProcura := False; fConsulta.vAlias := 11; if fConsulta.ShowModal = mrOk then begin EditCodigo.Text := TransString(fConsulta.Lista.DataSource.DataSet.Fieldbyname(´Pedido´).asInteger,EditCodigo.MaxLength ); fConsulta.Release; if Length(Trim(EditCodigo.Text)) = 0 then vCodPedido:=0 else vCodPedido:=TransInteiro(EditCodigo.Text); MostraPedido(self); MostraListaItens(self); end else begin fConsulta.Release; LimpaCampos(self); MostraPedido(self); MostraListaItens(self); exit; end; end;
Criação: Application.CreateForm(TfConsulta, fConsulta);
Execução: fConsulta.ShowModal = mrOk
Destruição: fConsulta.Release;
E lá na unit fconsulta faço assim:
FormShow: fConsulta.Caption := ´Lista de Pedidos´; DM.SQL_Vendas.Close ; DM.SQL_Vendas.CommandText := ´Select Vendas.*,Clientes.NomCli FROM Vendas Left Join Clientes ON (Vendas.CodCli=Clientes.CodCli and Vendas.CodEmp=Clientes.CodEmp) WHERE (Vendas.CodEmp=:vCodEmp) ORDER BY Vendas.Data+Vendas.Pedido DESC´; //DM.SQL_Vendas.CommandText := ´Select * FROM Vendas WHERE CodEmp=:vCodEmp ORDER BY Data+Pedido DESC´; DM.SQL_Vendas.ParamByName(´vCodEmp´).AsInteger := fMenu.CodEmpSelec ; DM.SQL_Vendas.Open ; DM.CDS_Vendas.Refresh ; DM.CDS_Vendas.First ;
O código é grande mas é compreensivo.
O select no arquivo de vendas está correto assim, é a maneira correta de fazer?
Um abraço
Mario
GOSTEI 0
Aldus
03/01/2005
Vinicius, olha que estranho. Mudei o select e o coloquei como normal, assim:
No ClientDataSet eu já havia criado um campo lookup para trazer o nome do cliente, e com o select com join e sem mostrou da mesma forma, na primeira vez que vejo a listagem no grid aparece todos os clientes, a partir do momento que selecionei algum, quando vejo a lista de novo, só aparece o cliente do pedido selecionado anteriormente.
Deixo esse campo no clientdataset? (adicionei também todos os campos da tabela vendas), como faço, agora tô confuso.
É necessário o campo lookup ou apenas o crio no select com join, e como o acesso no dbgrid?
Tá certo a chamada do form de consulta?
1 - Application.CreateForm(TfConsulta, fConsulta);
2 - fConsulta.ShowModal
3 - fConsulta.Release;
Tô te dando um cansaço né.
Um abraço
Mario
Select * FROM Vendas WHERE CodEmp=:vCodEmp
No ClientDataSet eu já havia criado um campo lookup para trazer o nome do cliente, e com o select com join e sem mostrou da mesma forma, na primeira vez que vejo a listagem no grid aparece todos os clientes, a partir do momento que selecionei algum, quando vejo a lista de novo, só aparece o cliente do pedido selecionado anteriormente.
Deixo esse campo no clientdataset? (adicionei também todos os campos da tabela vendas), como faço, agora tô confuso.
É necessário o campo lookup ou apenas o crio no select com join, e como o acesso no dbgrid?
Tá certo a chamada do form de consulta?
1 - Application.CreateForm(TfConsulta, fConsulta);
2 - fConsulta.ShowModal
3 - fConsulta.Release;
Tô te dando um cansaço né.
Um abraço
Mario
GOSTEI 0
Vinicius2k
03/01/2005
Mario,
Vamos com calma... é muita informação ! :)
Bem, erros de AV não são causados por instruções SQL e sim por tentativas de acesso a objetos que não existem (nunca existiram ou já foram destruídos). Eles podem estar sendo gerados em uma das diversas funções que vc está utilizando dentro do código...
No seu código existe um END ´sobrando´ ou falta um BEGIN em alguma parte dele... melhor reavaliar... vc poderia alterá-lo desta forma, se obedecer a sua lógica :
Não use campos lookup... são péssimos para ambiente Client/Server pois são muito lentos... Joins são extremamente eficientes e substituem, com muita vantagem, os lookups. A coluna resultante de um Join será apresentado a vc como se fosse ´original´ da própria tabela principal. Vc apenas deve ter o cuidade de setar as ProviderFlags destes TFields para false, para que o Provider não tente atualizá-las em caso de ApplyUpdates.
No seu form de consulta, após abrir o DataSet, não é necessário Refresh e nem tampouco First. Ele estará sendo aberto neste exato momento, não precisando ser atualizado e o seu ponteiro do já estará posicionado no primeiro registro em buffer.
Nunca abra, ou feche diretamente Queries ligadas à ClientDataSets, elas nunca estarão abertas a menos que vc abra manualmente em algum momento... o correto é :
Bem, verifique estes pontos... mas quase posso garantir que seu problema é de código e não de SQL...
T+
Vamos com calma... é muita informação ! :)
Bem, erros de AV não são causados por instruções SQL e sim por tentativas de acesso a objetos que não existem (nunca existiram ou já foram destruídos). Eles podem estar sendo gerados em uma das diversas funções que vc está utilizando dentro do código...
No seu código existe um END ´sobrando´ ou falta um BEGIN em alguma parte dele... melhor reavaliar... vc poderia alterá-lo desta forma, se obedecer a sua lógica :
Application.CreateForm(TfConsulta, fConsulta); fConsulta.vProcura := False; fConsulta.vAlias := 11; if fConsulta.ShowModal = mrOK then begin EditCodigo.Text := TransString(fConsulta.Lista.DataSource.DataSet.Fieldbyname(´Pedido´).asInteger, EditCodigo.MaxLength ); if Length(Trim(EditCodigo.Text)) = 0 then vCodPedido :=0 else vCodPedido := TransInteiro(EditCodigo.Text); MostraPedido(self); MostraListaItens(self); end else begin LimpaCampos(self); MostraPedido(self); MostraListaItens(self); end; fConsulta.Free;
Não use campos lookup... são péssimos para ambiente Client/Server pois são muito lentos... Joins são extremamente eficientes e substituem, com muita vantagem, os lookups. A coluna resultante de um Join será apresentado a vc como se fosse ´original´ da própria tabela principal. Vc apenas deve ter o cuidade de setar as ProviderFlags destes TFields para false, para que o Provider não tente atualizá-las em caso de ApplyUpdates.
No seu form de consulta, após abrir o DataSet, não é necessário Refresh e nem tampouco First. Ele estará sendo aberto neste exato momento, não precisando ser atualizado e o seu ponteiro do já estará posicionado no primeiro registro em buffer.
Nunca abra, ou feche diretamente Queries ligadas à ClientDataSets, elas nunca estarão abertas a menos que vc abra manualmente em algum momento... o correto é :
ClientDataSet.Close; Query.Close; // só feche se vc mesmo a tiver aberto em algum outro momento. // passe a SQL e os parametros para a query ClientDataSet.Open;
Bem, verifique estes pontos... mas quase posso garantir que seu problema é de código e não de SQL...
T+
GOSTEI 0
Aldus
03/01/2005
Olá Vinicius, gostaria de mais um esclarecimento seu.
Sempre que abro o form, abro tbm o sqldataset e o clientdataset, isso é o que faço hoje.
Quando for fazer um select, apenas fecho o client, passo o parâmetro ao sqldataset e ao abrir o client novamente, o client se encarrega de executar a nova query?
Como desabilito o providerflags do campo nomecliente que você mencionou.
Abri o dbgrid apenas com o select que você passou e funcionou bleza, só preciso desabilitar o providerflags pois não consigo gravar nada porque está tentando atualizar esse campo, que é virtual.
Obrigado pela atenção.
Mario
Sempre que abro o form, abro tbm o sqldataset e o clientdataset, isso é o que faço hoje.
Quando for fazer um select, apenas fecho o client, passo o parâmetro ao sqldataset e ao abrir o client novamente, o client se encarrega de executar a nova query?
Como desabilito o providerflags do campo nomecliente que você mencionou.
Abri o dbgrid apenas com o select que você passou e funcionou bleza, só preciso desabilitar o providerflags pois não consigo gravar nada porque está tentando atualizar esse campo, que é virtual.
Obrigado pela atenção.
Mario
GOSTEI 0
Vinicius2k
03/01/2005
Mario,
Exatamente. Não trabalhe diretamente no SQLDataSet, a não ser com os parametros. A midas (Provider+CDS) irá se encarregar de abrir uma transação, trazer os registros para o buffer do cliente e fechar a transação.
Existem outras opções, como CommandText e Parametros no próprio ClientDataSet, mas trabalhar corretamente com estas outras opções requer um pouco de estudo da Midas. O help do Delphi é a melhor referência que conheço sobre o assunto...
De imediato vc pode adotar este procedimento:
[b:d2d07878cf]-> Abra e feche apenas o ClientDataSet e mantenha a instrução e os parametros no SQLDataSet.[/b:d2d07878cf]
Adicione todos os TFields tanto no SQLDataSet, quanto no ClientDataSet.
Cada TField terá uma propriedade ´[b:d2d07878cf]ProviderFlags[/b:d2d07878cf]´, basta passar as Flags [b:d2d07878cf]pfInUpdate[/b:d2d07878cf] e [b:d2d07878cf]pfInWhere[/b:d2d07878cf] para [b:d2d07878cf]False[/b:d2d07878cf], tanto do SQLDataSet, quanto no ClientDataSet, dos TFields vindos do JOIN, no seu caso, [b:d2d07878cf]NomCli[/b:d2d07878cf].
Blz ?
T+
Sempre que abro o form, abro tbm o sqldataset e o clientdataset, isso é o que faço hoje.Quando for fazer um select, apenas fecho o client, passo o parâmetro ao sqldataset e ao abrir o client novamente, o client se encarrega de executar a nova query?
Exatamente. Não trabalhe diretamente no SQLDataSet, a não ser com os parametros. A midas (Provider+CDS) irá se encarregar de abrir uma transação, trazer os registros para o buffer do cliente e fechar a transação.
Existem outras opções, como CommandText e Parametros no próprio ClientDataSet, mas trabalhar corretamente com estas outras opções requer um pouco de estudo da Midas. O help do Delphi é a melhor referência que conheço sobre o assunto...
De imediato vc pode adotar este procedimento:
[b:d2d07878cf]-> Abra e feche apenas o ClientDataSet e mantenha a instrução e os parametros no SQLDataSet.[/b:d2d07878cf]
Como desabilito o providerflags do campo nomecliente que você mencionou.
Adicione todos os TFields tanto no SQLDataSet, quanto no ClientDataSet.
Cada TField terá uma propriedade ´[b:d2d07878cf]ProviderFlags[/b:d2d07878cf]´, basta passar as Flags [b:d2d07878cf]pfInUpdate[/b:d2d07878cf] e [b:d2d07878cf]pfInWhere[/b:d2d07878cf] para [b:d2d07878cf]False[/b:d2d07878cf], tanto do SQLDataSet, quanto no ClientDataSet, dos TFields vindos do JOIN, no seu caso, [b:d2d07878cf]NomCli[/b:d2d07878cf].
Blz ?
T+
GOSTEI 0