Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

MCoutinho

Problema com WHERE NOT EXISTS

Recommended Posts

Pessoal,

 

Alguém sabe me dizer se uma instrução NOT EXISTS é realizada com base em uma verificação inicial que não se atualiza ao longo da execução quando existe INNER JOIN no critério da consulta? Explico:

 

1 - Tenho uma tabela onde existem 2 registros iguais.

 

2 - Preciso transferir para a tabela principal estes registros, mas sem a duplicidade. Uso o NOT EXISTS para isso.

 

Se a consulta é simples, apenas com uma tabela, funciona normalmente. Se uso um INNER JOIN, não funciona.

 

O código é:

 

INSERT INTO veiculo
	SELECT DISTINCT
		cl.id,
		tv.chassi,
		tv.placa,
		tv.estado,
		tv.familia,
		tv.modelo,
		tv.ano_fab,
		tv.ano_mod,
		tv.cor,
		tv.combustivel,
		tv.acessorio,
		tv.taxi,
		tv.data_compra,
		tv.inclusao,
		tv.incluso_por,
		tv.alteracao,
		tv.alterado_por,
		tv.mediador_posvenda,
		tv.mediador_venda
	FROM cliente cl INNER JOIN temp_veiculo tv
		ON cl.cpf_cnpj = tv.cpf_cnpj
	WHERE 
		cl.id_concessao = @IdConcessao AND
		NOT EXISTS
	(
		SELECT
			vcl.id
		FROM cliente cln
			INNER JOIN veiculo vcl ON cln.id = vcl.id_cliente
		WHERE
			cln.id_concessao = @IdConcessao AND
			vcl.chassi = tv.chassi
	)

Consideremos que a tabela temp_veiculo tem registros duplicados (carros com chassi igual). O código acima gera o seguinte resultado:

 

- Se na tabela permanente (veiculo) já houver o chassi em questão, não ocorre nenhum erro e não é duplicado o registro, mas se não houver o chassi na tabela veiculo, ou seja, se fosse uma primeira migração, o comando grava o registro uma vez e ao encontrá-lo de novo ignora o NOT EXISTS e tenta inserir o mesmo chassi, o que gera bloqueio por constraint UNIQUE.

 

É como se o resultado do NOT EXISTS só fosse gerado uma vez e não fosse mais atualizado durante a instrução, causando a falsa resposta de que o chassi em questão ainda não existe, quando na verdade já foi inserido.

 

Onde estou errando?

 

Desde já, obrigado pela ajuda.

Compartilhar este post


Link para o post
Compartilhar em outros sites

A subquery parece igual a primeira sempre vai existir então, estou certo ?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Motta, obrigado pela resposta!

 

A primeira query é diferente da segunda. Note que na primeira, faço uma consulta pela tabela principal (veiculo) e na segunda pela tabela que uso somente durante a migração (temp_veiculo).

 

Vou tentar explicar melhor a consulta:

 

- Temos duas entidades permanentes: cliente e veículo (cliente possui veiculo)

- Temos uma tabela sem relacionamentos que serve para armazenar temporariamente registros que vêm de uma fonte externa (txt, csv, etc).

 

Gravo as informações nesta tabela temp_veiculo e então começo as instruções de inclusão nas tabelas principais.

 

Imagine a seguinte situação

Registros em temp_veiculo

- chassi: AAA1, cpf: 1234

- Chassi: AAA2, cpf: 5678

- chassi: AAA1, cpf: 1234 - (duplicidade do primeiro)

 

Na prática, executada a instrução tenho o seguinte:

1 - Se na tabela permanente (veiculo) já existir o chassi AAA1, tudo ocorre como deveria. Não duplica, não há erro, etc. Isso porque o NOT EXISTS detecta o chassi e a query principal não insere novo registro

 

2 - Se na tabela permanente ainda não existir o chassi AAA1 e não houvesse a duplicidade acima, também daria certo. O registro seria inserido normalmente

 

3 - MAS se não houver ainda o chassi AAA1 na tabela veiculo, é como se a instrução gravasse uma vez na linha 1 do exemplo acima, mas ao chegar na linha 3, gravasse novamente ignorando o resultado do NOT EXISTS.

 

Por isso a impressão de que o NOT EXISTS armazena o resultado inicial quando ainda não havia mesmo o registro em questão e depois não o atualiza...

 

Abs

Compartilhar este post


Link para o post
Compartilhar em outros sites

Entendi, mas a instrução talvez não possa ser feita via insert into select , talvez um cursor seja requerido.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Entendo. Já utilizo um cursor para outra finalidade nesta mesma instrução. O código completo está abaixo, mas não sou exatamente um conhecedor a fundo de SQL. Pode me dar um norte de como ficaria a instrução?

 

ALTER PROCEDURE [dbo].[ps_TempVeiculo_Import]
	@PathFileName varchar(200),
	@IdConcessao smallint
AS
DECLARE @SQL varchar(2000)
	BEGIN
		SET @SQL = "BULK INSERT temp_veiculo FROM '" +@PathFileName+"' WITH (FIELDTERMINATOR = ';',BATCHSIZE = 500)"
	END

EXEC (@SQL)


DECLARE @IdVeiculo int
DECLARE @IdCliente int
DECLARE my_cursor CURSOR FOR

SELECT
	vcl.id,
	cn.id
FROM cliente cl
	INNER JOIN veiculo vcl ON cl.id = vcl.id_cliente
	INNER JOIN temp_veiculo tv ON vcl.chassi = tv.chassi
	INNER JOIN cliente cn ON cn.cpf_cnpj = tv.cpf_cnpj
WHERE cl.id_concessao = @IdConcessao AND
	cl.cpf_cnpj <> tv.cpf_cnpj

OPEN my_cursor

FETCH NEXT FROM my_cursor
INTO @IdVeiculo,
	@IdCliente

WHILE @@FETCH_STATUS = 0
BEGIN
	UPDATE veiculo
		SET id_cliente = @IdCliente
		WHERE id = @IdVeiculo
	FETCH NEXT FROM my_cursor
	INTO @IdVeiculo,
		@IdCliente
END

CLOSE my_cursor
DEALLOCATE my_cursor


INSERT INTO veiculo
	SELECT DISTINCT
		cl.id,
		tv.chassi,
		tv.placa,
		tv.estado,
		tv.familia,
		tv.modelo,
		tv.ano_fab,
		tv.ano_mod,
		tv.cor,
		tv.combustivel,
		tv.acessorio,
		tv.taxi,
		tv.data_compra,
		tv.inclusao,
		tv.incluso_por,
		tv.alteracao,
		tv.alterado_por,
		tv.mediador_posvenda,
		tv.mediador_venda
	FROM cliente cl INNER JOIN temp_veiculo tv
		ON cl.cpf_cnpj = tv.cpf_cnpj
	WHERE 
		cl.id_concessao = @IdConcessao AND
		NOT EXISTS
	(
		SELECT
			vcl.id
		FROM cliente cln
			INNER JOIN veiculo vcl ON cln.id = vcl.id_cliente
		WHERE
			cln.id_concessao = @IdConcessao AND
			vcl.chassi = tv.chassi
	)
	
TRUNCATE TABLE temp_veiculo

Se eu estiver certo, acho que o select do NOT EXISTS ficaria no começo da instrução, depois eu declararia o cursor, associaria os campos, etc.

 

Por fim, no insert eu precisaria repetir o NOT EXISTS para que uma nova busca do chassi inexistente fosse feita. Do contrário, o problema permaneceria, já que o select no começo ainda não detectaria um chassi inserido, digamos, na metade da execução da query, correto?

 

Mais uma vez, obrigado.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Tem razão , teria de testar mas parece ter razão.

 

Inicialmente eu faria assim, Cursor e contralaria a duplicidade por Constraint (PK / UK).

 

Ou ainda em cada passo do Loop refaer o Select.

 

Meio doido isto.

 

:)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olá Motta,

 

A instrução ficou mesmo da forma como tínhamos comentado. Posto o código abaixo para o caso de alguém mais precisar de algo parecido.

 

Novamente, muito obrigado pela ajuda!

 

CREATE PROCEDURE [ps_TempVeiculo_Import]
	@PathFileName varchar(200),
	@IdConcessao smallint
AS
DECLARE @SQL varchar(2000)
	BEGIN
		SET @SQL = "BULK INSERT temp_veiculo FROM '" +@PathFileName+"' WITH (FIELDTERMINATOR = ';',BATCHSIZE = 500)"
	END

EXEC (@SQL)


DECLARE @IdVeiculo int
DECLARE @IdCliente int
DECLARE @Chassi varchar(17)
		
DECLARE MyCursor1 CURSOR FOR

SELECT
	vcl.id,
	cn.id
FROM cliente cl
	INNER JOIN veiculo vcl ON cl.id = vcl.id_cliente
	INNER JOIN temp_veiculo tv ON vcl.chassi = tv.chassi
	INNER JOIN cliente cn ON cn.cpf_cnpj = tv.cpf_cnpj
WHERE cl.id_concessao = @IdConcessao AND
	cl.cpf_cnpj <> tv.cpf_cnpj

OPEN MyCursor1

FETCH NEXT FROM MyCursor1
INTO
	@IdVeiculo,
	@IdCliente

WHILE @@FETCH_STATUS = 0
BEGIN
	UPDATE veiculo
		SET id_cliente = @IdCliente
		WHERE id = @IdVeiculo
	FETCH NEXT FROM MyCursor1
	INTO @IdVeiculo,
		@IdCliente
END

CLOSE MyCursor1
DEALLOCATE MyCursor1

DECLARE myCursor2 CURSOR FOR

SELECT DISTINCT
		tv.chassi
	FROM cliente cl INNER JOIN temp_veiculo tv
		ON cl.cpf_cnpj = tv.cpf_cnpj
	WHERE 
		cl.id_concessao = 1 AND
		NOT EXISTS
	(
		SELECT
			vcl.id
		FROM cliente cln
			INNER JOIN veiculo vcl ON cln.id = vcl.id_cliente
		WHERE
			cln.id_concessao = 1 AND
			vcl.chassi = tv.chassi
	)

OPEN myCursor2

FETCH NEXT FROM myCursor2
INTO
	@Chassi

WHILE @@FETCH_STATUS = 0
BEGIN
	INSERT INTO veiculo
	SELECT DISTINCT TOP 1
		cl.id,
		tv.chassi,
		tv.placa,
		tv.estado,
		tv.familia,
		tv.modelo,
		tv.ano_fab,
		tv.ano_mod,
		tv.cor,
		tv.combustivel,
		tv.acessorio,
		tv.taxi,
		tv.data_compra,
		tv.inclusao,
		tv.incluso_por,
		tv.alteracao,
		tv.alterado_por,
		tv.mediador_posvenda,
		tv.mediador_venda
	FROM cliente cl INNER JOIN temp_veiculo tv
		ON cl.cpf_cnpj = tv.cpf_cnpj
	WHERE 
		cl.id_concessao = @IdConcessao AND
		tv.chassi = @Chassi AND
		NOT EXISTS
	(
		SELECT
			vcl.id
		FROM cliente cln
			INNER JOIN veiculo vcl ON cln.id = vcl.id_cliente
		WHERE
			cln.id_concessao = @IdConcessao AND
			vcl.chassi = @Chassi
	)
	FETCH NEXT FROM myCursor2
	INTO @Chassi
END

CLOSE myCursor2
DEALLOCATE myCursor2

TRUNCATE TABLE temp_veiculo

Dúvida: Uma instrução como esta pesaria muito em questão de desempenho? Ela deve rodar normalmente com cerca de 2 mil registros por vez...

 

abs

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.