COMO SIMPLIFICAR CONSULTA MUITO COMPLEXA?

MARSOLIM 06/11/2014 20:51:47
#442340
pessoal queria dicas de como simplificar uma consulta em duas tabelas pra que ela seja mais rápida principalmente. o meu sistema tem duas tabelas principais sendo que para cada registro da tabela 1 pode ter até mais de 10 registros na tabela 2. quando a consulta envolve só a tabela 1 beleza vai de boa bem rapidinho mas quando tenho que combinar o filtro entre essa tabela 1 e a 2 aí demora muitos minutos e aparece mensagem de consulta muito complexa. é algo mais ou menos assim: eu tenho uma tabela de imoveis com os dados dos imoveis e tenho uma tabela de cômodos do imovel com suas quantidades vamos supor o imóvel de código 1000 tem 1 sala de estar, 1 sala de jantar, 1 suite, 2 quartos, etc. vamos supor que quero pesquisar imoveis do bairro fulano que custam de R$ 100.000,00 até R$ 150.000,00 e com área de 50m até 80m. até aí tudo bem porque esses dados estão todos na tabela de imoveis. mas aí vamos supor que alem desses dados eu quero um imovel que tenha 2 quartos e 1 suíte. aí eu tenho que pesquisar esses dados na tabela comodos aonde tem os campos codigo, codimovel, qtd e tipo. aí que o bicho pega porque a maneira que achei de fazer isso foi incluindo na sql uma pesquisa que vai em uma listbox aonde tema list a de comodos. aí tem que fazer um loop nessa listbox e pegar todas as linhas dela e ver qual imovel tem todos esses comodos nessas quantidades e depois ver qual deles satisfaz a pesquisa na tabela de imoveis. isso tá muito complexo. pensei em usar inner join mas fiquei na confusão de como fazer. alguma ideia? o banco é em access e a consulta está mais ou menos como abaixo

    Sub Filtro()

Dim con As OleDbConnection
Dim cmd As OleDbCommand
Dim r As OleDbDataReader
Dim Bairros As String = String.Empty
Dim lCodigos As String = String.Empty
Dim Status As String = String.Empty

If Me.lstBairros.Items.Count > 0 Then
For N As Integer = 0 To Me.lstBairros.Items.Count - 1
If Bairros = String.Empty Then
Bairros = [Ô] And (Bairro=[ô][Ô] & Me.lstBairros.Items(N).Text & [Ô][ô][Ô]
Else
Bairros = Bairros & [Ô] Or Bairro = [ô][Ô] & Me.lstBairros.Items(N).Text & [Ô][ô][Ô]
End If
Next
Bairros = Bairros & [Ô])[Ô]
End If

If Me.lstComodos.Items.Count > 0 Then
lCodigos = listaComodos()
Else
lCodigos = [Ô](IsNull(CodImovel) Or CodImovel LIKE [ô]%[Ô] & Me.CodImovel.Text & [Ô]%[ô])[Ô]
End If

If Me.cboStatus.Text <> [Ô]TODOS[Ô] Then
Status = [Ô] And (IsNull(Status) Or Status = [ô][Ô] & Me.cboStatus.Text & [Ô][ô])[Ô]
End If

Dim sql As String = [Ô]SELECT * FROM imoveis[Ô]
sql = sql & [Ô] WHERE [Ô] & lCodigos
sql = sql & [Ô] And (IsNull(NomeVendedor) Or NomeVendedor LIKE [ô]%[Ô] & Me.NomeVendedor.Text & [Ô]%[ô])[Ô]
sql = sql & [Ô] And (IsNull(NomeCadastrador) Or NomeCadastrador LIKE [ô]%[Ô] & Me.NomeCadastrador.Text & [Ô]%[ô])[Ô]
sql = sql & [Ô] And (IsNull(Tipo) Or Tipo LIKE [ô]%[Ô] & Me.TipoImovel.Text & [Ô]%[ô])[Ô]
sql = sql & [Ô] And (IsNull(Area) Or (Area >=[Ô] & Me.AreaInicial.Text & [Ô] And Area <=[Ô] & Me.AreaFinal.Text & [Ô]))[Ô]
sql = sql & [Ô] And (IsNull(Valor) Or (Valor >=[Ô] & Me.ValorInicial.Text & [Ô] And Valor <=[Ô] & Me.ValorFinal.Text & [Ô]))[Ô]
sql = sql & [Ô] And (IsNull(CEP) Or CEP LIKE [ô]%[Ô] & Me.CEP.Text & [Ô]%[ô])[Ô]
sql = sql & [Ô] And (IsNull(Cidade) Or Cidade LIKE [ô]%[Ô] & Me.Cidade.Text & [Ô]%[ô])[Ô]
sql = sql & [Ô] And (IsNull(Estado) Or Estado LIKE [ô]%[Ô] & Me.UF.Text & [Ô]%[ô])[Ô]
sql = sql & [Ô] And (IsNull(Endereco) Or Endereco LIKE [ô]%[Ô] & Me.Logradouro.Text & [Ô]%[ô])[Ô]
sql = sql & [Ô] And (IsNull(Quadra) Or Quadra LIKE [ô]%[Ô] & Me.Quadra.Text & [Ô]%[ô])[Ô]
sql = sql & [Ô] And (IsNull(Lote) Or Lote LIKE [ô]%[Ô] & Me.Lote.Text & [Ô]%[ô])[Ô]
sql = sql & [Ô] And (IsNull(Numero) Or Numero LIKE [ô]%[Ô] & Me.Numero.Text & [Ô]%[ô])[Ô]
sql = sql & [Ô] And (IsNull(Bairro) Or Bairro LIKE [ô]%[Ô] & Me.Bairro.Text & [Ô]%[ô])[Ô]
sql = sql & [Ô] And (IsNull(Obs) Or Obs LIKE [ô]%[Ô] & Me.Obs.Text & [Ô]%[ô])[Ô]
sql = sql & Bairros
sql = sql & Status
sql = sql & [Ô] ORDER BY CodImovel Asc[Ô]

con = New OleDbConnection([Ô]Provider=Microsoft.ACE.OLEDB.12.0;Data Source=X:\database\database.accdb;Jet OLEDB:Database Password=senhadobanco[Ô])

Try

con.Open()
cmd = New OleDbCommand(sql, con)
r = cmd.ExecuteReader()
Me.lstFiltro.Items.Clear()

Do While r.Read
Dim item0 As ListViewItem = New ListViewItem(New String() {[Ô][Ô] & r([Ô]CodImovel[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]NomeVendedor[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]TelefoneVendedor[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]NomeCadastrador[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]TelefoneCadastrador[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]Tipo[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]Area[Ô]) & [Ô][Ô], [Ô][Ô] & Format(r([Ô]Valor[Ô]), [Ô]Currency[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]CEP[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]Cidade[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]Estado[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]Endereco[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]Quadra[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]Lote[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]Numero[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]Bloco[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]Apto[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]Bairro[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]Obs[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]ID[Ô]) & [Ô][Ô], [Ô][Ô] & r([Ô]Status[Ô]) & [Ô][Ô]}, 0)
lstFiltro.Items.AddRange(New ListViewItem() {item0})
Loop

con.Close()

Catch ex As OleDbException
If ex.ErrorCode <> -2147217900 Then
MsgBox(ex.Message, MsgBoxStyle.Critical, [Ô]Erro Oledb[Ô] & ex.ErrorCode)
End If
Catch ex As Exception
If Err.Number <> 13 Then
MsgBox(ex.Message & vbNewLine & vbNewLine & Err.Number, MsgBoxStyle.Critical, [Ô]Erro geral[Ô])
End If
End Try

End Sub


alem disso tenho uma sub para cuidar da pesquisa dos comodos para incluir no filtro. é aqui que está o problema maior porque o jeito que achei de fazer foi assim incluir a quantidade e tipo de comodo numa listbox e depois fazer um loop por todos os registros da tabela comodos e verificar cada linha da listbox para ver se o imovel tem todos os itens da listbox e se tiver vai adicionar o codigo do imovel na pesquisa. em uma tabela com menos de 1000 registros até vai mas imagina fazer um loop desse numa tabela de mais de 50 mil registros. a sub é essa

    Function listaComodos() As String

listaComodos = String.Empty

If Me.lstComodos.Items.Count > 0 Then

Dim con As OleDbConnection
Dim cmd As OleDbCommand
Dim cmdb As OleDbCommand
Dim r As OleDbDataReader
Dim rd As OleDbDataReader
Dim sql As String = [Ô]SELECT DISTINCT CodImovel FROM comodos[Ô]
Dim lDep As String = String.Empty
Dim Quantidade As Integer = 0

con = New OleDbConnection([Ô]Provider=Microsoft.ACE.OLEDB.12.0;Data Source=X:\database\database.accdb;Jet OLEDB:Database Password=senhadobanco[Ô])

Try
con.Open()
cmd = New OleDbCommand(sql, con)
r = cmd.ExecuteReader()

Do While r.Read
Quantidade = 0
lDep = [Ô]SELECT * FROM comodos WHERE CodImovel=[Ô] & r([Ô]CodImovel[Ô])
cmdb = New OleDbCommand(lDep, con)
rd = cmdb.ExecuteReader()
Do While rd.Read()
For N As Integer = 0 To Me.lstComodos.Items.Count - 1
If rd([Ô]Quantidade[Ô]) = Me.lstComodos.Items(N).SubItems(0).Text And rd([Ô]Tipo[Ô]) = Me.lstComodos.Items(N).SubItems(1).Text Then
Quantidade = Quantidade + 1
End If
Next
Loop
If Quantidade >= Me.lstComodos.Items.Count Then
If listaComodos = String.Empty Then
listaComodos = [Ô](CodImovel = [Ô] & r([Ô]CodImovel[Ô])
Else
listaComodos = listaComodos & [Ô] Or CodImovel = [Ô] & r([Ô]CodImovel[Ô])
End If
End If
Loop

If listaComodos <> String.Empty Then
listaComodos = listaComodos & [Ô])[Ô]
Else
listaComodos = [Ô](CodImovel = 0)[Ô]
End If

con.Close()

Catch ex As OleDbException
MsgBox(ex.Message, MsgBoxStyle.Critical, [Ô]Erro Oledb[Ô])
Catch ex As Exception
If Err.Number <> 13 Then
MsgBox(ex.Message & vbNewLine & vbNewLine & Err.Number, MsgBoxStyle.Critical, [Ô]Erro geral[Ô])
End If
End Try

End If

End Function


eu sei que isso tá a coisa mais horrível e ridícula que vocês já viram. que ideias vocês podem me dar para melhorar isso de forma a ficar trabalhável? agradeço.
TUNUSAT 07/11/2014 08:15:45
#442347
MARSOLIM,

Podemos usar o Inner Join? Veja:

http://office.microsoft.com/pt-br/access-help/operacao-inner-join-HA001231487.aspx

O exemplo:

SELECT CategoryName, ProductName
FROM Categories INNER JOIN Products
ON Categories.CategoryID = Products.CategoryID


Você tem que juntar a Chave Primária (PK - Primary Key) da tabela principal (no seu caso: [Ô]imoveis[Ô]) com a Chave Estrangeira (FK - Foreign Key) que faz o relacionamento com a PK (no seu caso: [Ô]comodos[Ô]). Pelo que seu estou vendo o campo de relacionamento é o [Ô]CodImovel[Ô]. Então ficaria assim:

Exemplo:

SELECT imoveis .CodImovel
FROM imoveis INNER JOIN comodos
ON imoveis.CodImovel = comodos.CodImovel


Lembre-se de dizer quem é a tabela na qual pertence o campo que você quer pesquisar.
Para melhorar o tempo de resposta, NÃO use a [Ô]*[Ô], descreva os campos que você precisa e os que você não precisa não coloque.

Veja também:
http://msdn.microsoft.com/pt-br/library/office/ff197346(v=office.15).aspx
https://social.msdn.microsoft.com/Forums/pt-BR/31f1ed14-c413-413d-959b-957d96df3fb3/select-com-mais-de-um-inner-join?forum=accesspt
http://www.ehow.com/how_5537516_create-inner-joins-access.html
http://stackoverflow.com/questions/3031589/sql-select-multiple-inner-joins
http://www.databasedev.co.uk/query_joins.html


[][ô]s,
Tunusat.
MARSOLIM 07/11/2014 17:38:00
#442366
olá tunusat agradeço pela resposta. eu testei aqui e funciona quando incluo apenas um comodo mas se eu incluo mais de um ja não funciona como deveria. estou tentando algo assim

[Ô]Select imoveis.* from imoveis INNER JOIN comodos ON imoveis.CodImovel = comodos.CodImovel WHERE (imoveis.tipo=[ô]apartamento[ô]) And (imoveis.bairro=[ô]pacaembu[ô]) And (comodos.qtd=2 And comodos.tipo=[ô]quarto[ô]);[Ô]


ok com apenas uma condição nos comodos funciona mas o problema é quando quero pesquisar imoveis que tenha por exemplo 2 quartos, 1 suite e 1 salão de jogos. nesse caso preciso de todos os imóveis que sejam apartamento e que estejam no bairro pacaembu mas que satisfaça as três condições de comodos ou seja que tenha 2 quartos, 1 suite e 1 salão de jogos. aí não dá certo porque o AND não vai funcionar para incluir valores diferentes das mesmas colunas e o OR não vai dar certo porque eu não quero que satisfaça apenas um ou outro. preciso que satisfaça a todos os critérios de comodos desejados. a clausula IN não funciona em mais de uma coluna. tentei dar mais de um JOIN mas também não consegui um resultado.

a tabela de comodos é algo assim

id   CodImovel   qtd   tipo
1 1000 1 sala de estar
2 1000 1 sala de jantar
3 1000 1 cozinha
4 1000 2 quarto
5 2150 1 suite
6 2150 2 quarto
7 2150 1 cozinha
8 2150 2 banheiro
9 2150 1 sala de estar


agradeço muito se tiver uma luz para me indicar. pesquisei bastante e não achei nada que funcionasse.
TUNUSAT 10/11/2014 09:35:05
#442384
MARSOLIM,

Testei mais aqui ... não está certa... traz o código do imóvel errado.
Basicamente eu coloquei 1 [Ô]IN[Ô] para cada cômodo necessário que seja obrigatório na consulta.

SELECT distinct imoveis.CodImovel
FROM imoveis INNER JOIN comodos ON imoveis.CodImovel = comodos.CodImovel
WHERE (((imoveis.tipo)=[ô]apartamento[ô]) AND ((imoveis.bairro)=[ô]pacaembu[ô])
AND ((imoveis.CodImovel) In (SELECT imoveis.CodImovel
FROM comodos
WHERE ((comodos.qtd)=2) AND ((comodos.tipo)=[ô]quarto[ô])))
AND ((imoveis.CodImovel) In (SELECT imoveis.CodImovel
FROM comodos
WHERE ((comodos.qtd)=1) AND ((comodos.tipo)=[ô]suite[ô])))
AND ((imoveis.CodImovel) In (SELECT imoveis.CodImovel
FROM comodos
WHERE ((comodos.qtd)=1) AND ((comodos.tipo)=[ô]salao de jogos[ô]))))


[][ô]s,
Tunusat.
MARSOLIM 10/11/2014 13:03:57
#442390
parece que deu certo. só adicionei os outros campos da tabela de imoveis poque vou precisar de todos eles para preencher a grid

SELECT distinct imoveis.CodImovel, endereco, cidade, estado, tipo, area, valor
FROM imoveis INNER JOIN comodos ON imoveis.CodImovel = comodos.CodImovel
...


só uma pergunta esses parênteses são todos necessário mesmo? porque queria simplificar isso pois a montagem dos comodos será feita por um loop em uma listbox aonde o usuário irá inserir a quantidade e o tipo de comodo. para isso tenho uma listbox, um campo para a quantidade, uma combo com a lista de comodos existentes na base de dados e um botão. o usuário escreve a quantidade e escolhe o tipo de comodo na combo e clica no botão para adicionar na listbox que fica assim

qtd | tipo
------------------------
1 | suite
1 | sala de estar
2 | quarto
2 | banheiro


então para montar a parte dos comodos tenho que fazer um loop nessa listbox conforme modelo do primeiro post desse tópico.

mais uma vez agradeço pela atenção
TUNUSAT 10/11/2014 14:45:58
#442393
MARSOLIM,

A minha consulta meu pareceu errada aqui. Veja se ele traz o código do imóvel correto, faça alguns testes antes. Exemplo: Eu coloquei: [Ô]salão de jogos[Ô] e o código [Ô]1000[Ô] não tem esse tipo de cômodo...
Sobre os parênteses ... quem coloca eles é o Access. Você só precisa tomar cuidado quando coloca [Ô]OR[Ô] misturado com [Ô]AND[Ô], então se colocar o parênteses em lugar errado pode ter problemas com o resultado.

[][ô]s,
Tunusat.
MARSOLIM 10/11/2014 15:42:17
#442395
é não está funcionando direito. testei alterando o filtro por exemplo para 3 quartos e outros mas sempre retorna os mesmos registros. tentei usar um count também mas acho que não dá certo ou então não consegui usar direito. minha ideia era contar a quantidade de registros que atende o filtro dos comodos. por exemplo se eu adicionar três comodos como por exemplo

2 quarto
1 suite
2 banheiro

nesse caso fazer um count que deveria retornar 3. algo como

... and count(o filtro aqui) = 3
FFCOUTO 11/11/2014 09:27:09
#442406
Marsolim, se você puder, poste uma cópia do seu banco com alguns dados assim ficará mais fácil pra te ajudar na montagem dessa consulta.
MARSOLIM 11/11/2014 11:15:17
#442418
pessoal consegui encontrar a solução usando inner joins. nos meus testes deu certo mais ou menos assim

SELECT distinct imoveis.CodImovel, imoveis.ID, imoveis.vendedor, imoveis.cadastrador, imoveis.data_cadastro, imoveis.hora_cadastro, imoveis.tipo, imoveis.area, imoveis.valor, imoveis.cep, imoveis.Cidade, imoveis.estado, imoveis.endereco, imoveis.numero, imoveis.bairro
FROM ((imoveis
INNER JOIN comodos As c1 ON imoveis.CodImovel = c1.CodImovel)
INNER JOIN comodos As c2 ON imoveis.CodImovel = c2.CodImovel)
INNER JOIN comodos As c3 ON imoveis.CodImovel = c3.CodImovel
WHERE imoveis.tipo=[ô]apartamento[ô] And imoveis.bairro=[ô]pacaembu And
(c1.qtd=2 And c1.tipo=[ô]quarto[ô]) And
(c2.qtd=1 And c2.tipo=[ô]suite[ô])
And (c3.qtd=1 And c3.tipo=[ô]garagem[ô]);

a paradinha toda está nos parênteses que envolve os tais inner joins. para bancos access sapinoia é diferente mas pelos meus muitos testes aqui está funcionando bacana. agora só fazer a rotina de pegar as linhas da listbox de comodos e criar o sql.
TUNUSAT 11/11/2014 11:52:22
#442422
FFCOUTO,

Eu montei uma cópia e disponibilizei no meu post acima ...

MARSOLIM,

Tá certo meu arquivo Access? (o [ô].MDB[ô], não a query...)

[][ô]s,
Tunusat.
MARSOLIM 11/11/2014 12:05:04
#442424
tunusat é esse modelo mesmo. a única diferença é que não coloquei nenhum relacionamento no banco access e cada tabela tem apenas uma chave primária.
Página 1 de 2 [16 registro(s)]
Tópico encerrado , respostas não são mais permitidas