Fórum O que há de errado no meu select com left join? #263941

03/01/2005

0

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

Aldus

Responder

Posts

03/01/2005

Vinicius2k

Mario,

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+


Responder

Gostei + 0

03/01/2005

Aldus

Olá Vinicius, o erro sumiu mas apenas o cliente numero 1 é colocado no dbgrid.

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


Responder

Gostei + 0

03/01/2005

Vinicius2k

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+


Responder

Gostei + 0

03/01/2005

Aldus

Concordo plenamente Vinicius, só que eu retirei o codemp e mesmo assim só me mostra o cliente 1

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


Responder

Gostei + 0

03/01/2005

Vinicius2k

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 :
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+


Responder

Gostei + 0

04/01/2005

Aldus

Olá Vinicius, o que está ocorrendo é o seguinte:

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


Responder

Gostei + 0

04/01/2005

Vinicius2k

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+


Responder

Gostei + 0

04/01/2005

Aldus

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:

      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


Responder

Gostei + 0

04/01/2005

Aldus

Vinicius, olha que estranho. Mudei o select e o coloquei como normal, assim:

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


Responder

Gostei + 0

05/01/2005

Vinicius2k

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 :
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+


Responder

Gostei + 0

06/01/2005

Aldus

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


Responder

Gostei + 0

06/01/2005

Vinicius2k

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?

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+


Responder

Gostei + 0

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

Aceitar