Atualizando uma cláusula WHERE para verificar se um valor NÃO está em uma tabela separada


8

Eu tenho uma consulta que usa uma WHEREcláusula e, por acaso, use exatamente a mesma WHEREcláusula em muitas consultas nesta tabela (et al).

A consulta é:

SELECT
    DATENAME(DW, [AtDateTime]) AS [Day of Week]
    ,COUNT(*) AS [Number of Searches]
    ,CAST(CAST(COUNT(*) AS DECIMAL(10, 2)) 
         / COUNT(DISTINCT CONVERT(DATE, [AtDateTime])) AS DECIMAL(10, 2)) 
       AS [Average Searches per Day]
    ,SUM(CASE WHEN [NumFound] = 0 THEN 1 ELSE 0 END) 
       AS [Number of Searches with no Results]
    ,CAST(CAST(SUM(CASE WHEN [NumFound] = 0 THEN 1 ELSE 0 END) 
         AS DECIMAL(10, 2)) / COUNT(*) AS DECIMAL(10, 4)) 
       AS [Percent of Searches with no Results]
FROM [DB].[dbo].[SearchHistory] 
WHERE 
    [CustomerNumber] <> '1234' AND [CustomerNumber] <> '5678'
GROUP BY DATENAME(DW, [AtDateTime]), DATEPART(DW, [AtDateTime])
ORDER BY DATEPART(DW, [AtDateTime])

A parte que desejo alterar é a WHEREcláusula, que permite que eu use uma tabela para que, se for necessário adicionar um número de cliente a ser ignorado, não seja necessário atualizar todas as minhas consultas. (E existem algumas consultas que possuem essa mesma WHEREcláusula.)


Se as exclusões de clientes atualmente são específicas para a execução da consulta, por que movê-las para uma tabela / mesa de trabalho compartilhada não introduziria o compartilhamento falso? Em um aplicativo normal, os clientes normalmente são arbitrários e, portanto, específicos para uma única execução de consulta. Sugiro que essa questão omita fatos importantes sobre a generalidade necessária para que a solução funcione corretamente ou ignore a questão do compartilhamento.
Thomas W

@ ThomasW - o que é esse "compartilhamento falso" de que você está falando? Você tem uma referência para isso? Eu nunca ouvi falar disso antes.
Max Vernon

11
@ThomasW Os requisitos para isso são que certos clientes que temos (que usamos muito para testes) devem ser excluídos de determinados relatórios, pois distorcem os resultados.
Der Kommissar

11
@ MaxVernon - talvez um termo mais reconhecido seja "escopo incorreto". O que foi descrito envolveu alterar uma entrada de um parâmetro completamente independente, para ser uma tabela de banco de dados compartilhada entre usuários e invocação cruzada. Essa alteração ultrapassa 2 limites de escopo. Dado o contexto adicional, o escopo descrito parece bom, mas, se não fosse, isso se manifestaria como "compartilhamento incorreto".
Thomas W

11
A abordagem descrita também lembra uma grande quantidade de implementações de tabelas de trabalho herdadas (~ 1000 tabelas) em um aplicativo principal pelo qual sou responsável. A este respeito, levantei a possível natureza da "mesa de trabalho" como uma pergunta :) Obrigado.
Thomas W

Respostas:


5

Crie uma tabela para manter os números de clientes a serem excluídos e exclua essas linhas usando a NOT EXISTSna WHEREcláusula

CREATE TABLE dbo.ExcludedCustomers
(
    CustomerNumber VARCHAR(255) NOT NULL
        CONSTRAINT PK_ExcludedCustomers
        PRIMARY KEY CLUSTERED
);

INSERT INTO dbo.ExcludedCustomers (CustomerNumber)
VALUES ('1234')
    , ('5678');


SELECT
    <....>
FROM [DB].[dbo].[SearchHistory] 
WHERE 
    NOT EXISTS (
        SELECT 1
        FROM dbo.ExcludedCustomers ec
        WHERE ec.CustomerNumber = SearchHistory.CustomerNumber
    )
    <...>;

7
CREATE TABLE dbo.CustomerExclusions
(
  CustomerNumber VARCHAR(32) PRIMARY KEY -- Is CustomerNumber *really* a string?
);

INSERT dbo.CustomerExclusions(CustomerNumber) VALUES('1234'),('5678');

Agora sua WHEREcláusula em todas as consultas se torna:

WHERE NOT EXISTS 
(
  SELECT 1 FROM dbo.CustomerExclusions AS c
  WHERE c.CustomerNumber = SearchHistory.CustomerNumber
)

Sim Infelizmente. Os números dos clientes devem ser uma cadeia de caracteres para compatibilidade cruzada com o AS / 400. (Pelo menos por agora, nós estamos trabalhando em uma correção para ele.)
Der Kommissar

3
@EBrown Uh, ugh.
Aaron Bertrand

-3

Existem questões importantes / problemas em potencial com sua abordagem proposta. Com certeza, você pode excluir facilmente com uma tabela de trabalho 'Exclusão de número de cliente':

WHERE NOT EXISTS (
  SELECT 1 FROM [dbo].Work_ExcludeCustomer
  WHERE CustomerNumber = SearchHistory.CustomerNumber
)

Mas agora, o que eram "parâmetros de consulta" - totalmente dinâmicos e independentes, por consulta e por usuário - estão se transformando em "estado persistente compartilhado no banco de dados".

Algumas perguntas e pontos relevantes:

  1. as informações de exclusão do cliente devem ser separadas, por usuário ou por sessão? você pode adicionar um parâmetro 'SessionID' para distingui-los, mas essencialmente você está recriando um antigo padrão "Tabela de trabalho".

  2. talvez uma cláusula NOT IN (...) possa ser preferível? que podem ser parametrizados dinamicamente, até o limite de 2100 parâmetros.

  3. talvez revisite seu código / infraestrutura para criar parâmetros de consultas e vinculações, se você atualmente depende de números de parâmetros fixos; melhorar isso permitirá modularidade e uso de uma cláusula IN ou NOT IN (?,?,? ..) com número variável de parâmetros.

Abordagem sugerida:

WHERE [CustomerNumber] NOT IN (?, ?, ?)

Com as ligações '1234', '5678', '6789' etc, para os parâmetros NOT IN () e os parâmetros de consulta lógica subsequentes vinculados à numeração apropriada dinamicamente.


11
O uso de NOT IN (...) e / ou a construção dinâmica de texto de consulta é um antipadrão e resultará em desempenho inferior às abordagens baseadas em conjunto recomendadas por Aaron e por mim.
Max Vernon

Para uma excelente leitura das diferenças, consulte esta postagem.
Max Vernon

@MaxVernon - substituir parâmetros dinâmicos por dados "compartilhados" ou tabelas de trabalho pode introduzir um compartilhamento falso, o que é muito mais um antipadrão. Como ninguém mais considerou ou estabeleceu especificamente que isso não é um problema, é absolutamente válido levantar essa preocupação; nem deve ser trivialmente rebaixado.
Thomas W
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.