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.)

Der Kommissar
fonte
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
    )
    <...>;
Max Vernon
fonte
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
)
Aaron Bertrand
fonte
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.

Thomas W
fonte
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