Aggiornamento di una clausola WHERE per verificare se un valore NON è in una tabella separata


8

Ho una query che utilizza una WHEREclausola e mi capita di usare la stessa identica WHEREclausola in molte query su questa tabella (et al).

La query è:

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

La parte che desidero modificare è la WHEREclausola, per consentirmi invece di utilizzare una tabella in modo che se devo aggiungere un numero cliente per essere ignorato, non devo aggiornare tutte le mie query. (E ci sono alcune query che hanno questa stessa WHEREclausola.)


Se le esclusioni del cliente sono attualmente specifiche per l'esecuzione della query, perché spostarle in una tabella / tabella di lavoro condivisa non introdurrebbe una condivisione errata? In un'applicazione normale, i clienti sarebbero in genere arbitrari e quindi specifici per l'esecuzione di una singola query. Suggerirei che questa domanda ometta fatti importanti quanto alla generalità necessaria per il corretto funzionamento della soluzione o trascura il problema della condivisione.
Thomas W,

@ThomasW - di cosa stai parlando questa "falsa condivisione"? Hai un riferimento per questo? Non ne ho mai sentito parlare prima.
Max Vernon,

1
@ThomasW I requisiti per questo sono che determinati clienti che abbiamo (che usiamo molto per i test) devono essere esclusi da alcuni report, poiché distorcono i risultati.
Der Kommissar,

1
@MaxVernon - forse un termine meglio riconosciuto sarebbe "ambito errato". Ciò che è stato descritto ha comportato la modifica di un input da un parametro completamente indipendente, in una tabella DB condivisa tra utenti e invocazione incrociata. Questa modifica supera 2 limiti dell'ambito. Dato un contesto aggiuntivo, l'ambito descritto sembra OK, ma in caso contrario si manifesterebbe come "condivisione errata".
Thomas W,

1
L'approccio descritto ricordava anche un sacco di implementazione di tabelle di lavoro legacy (~ 1000 tabelle) in un'applicazione principale di cui ho la responsabilità. A questo proposito ho sollevato la possibile natura del "tavolo da lavoro" come domanda :) Grazie.
Thomas W,

Risposte:


5

Crea una tabella per contenere i numeri dei clienti da escludere, quindi escludi quelle righe usando un NOT EXISTSnella WHEREclausola.

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');

Ora la tua WHEREclausola su tutte le query diventa:

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

Sì purtroppo. I numeri dei clienti devono essere una stringa per la compatibilità incrociata con l'AS / 400. (Almeno per ora, stiamo lavorando a una soluzione per questo.)
Der Kommissar il

3
@Brown Uh, ugh.
Aaron Bertrand

-3

Ci sono domande importanti / potenziali problemi con il tuo approccio proposto. Di sicuro, puoi escludere abbastanza facilmente tramite una tabella di lavoro "Esclusione numero cliente":

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

Ma ora, quelli che erano "parametri di query" - completamente dinamici e indipendenti, per query e per utente - si stanno trasformando in "stato persistente condiviso nel database".

Alcune domande e punti rilevanti:

  1. le informazioni sull'esclusione del cliente devono essere separate, per utente o per sessione? puoi aggiungere un parametro 'SessionID' per distinguerli, ma essenzialmente stai ricreando un vecchio modello "Tavolo da lavoro".

  2. forse una clausola NOT IN (...) potrebbe essere preferibile? che può essere parametrizzato in modo dinamico, fino al limite di 2100 parametri.

  3. magari rivisitare il codice / la propria infrastruttura per creare parametri di query e associazione, se attualmente si fa affidamento su numeri di parametri fissi; il miglioramento di ciò consentirà la modularità e l'uso di clausole IN o NOT IN (?,?,? ..) con numero variabile di parametri.

Approccio suggerito:

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

Con i collegamenti '1234', '5678', '6789' ecc. Ai parametri NOT IN () e ai successivi parametri della query logica associati dinamicamente alla numerazione appropriata.


1
L'uso di NOT IN (...) e / o la creazione dinamica del testo della query è un anti-pattern e comporterà prestazioni inferiori rispetto agli approcci basati su set consigliati da Aaron e da me stesso.
Max Vernon,

Per una lettura eccellente delle differenze, controlla questo post.
Max Vernon,

@MaxVernon - la sostituzione di parametri dinamici con dati "condivisi" o tabelle di lavoro può introdurre una falsa condivisione, che è molto più di un anti-pattern. Poiché nessun altro ha specificamente considerato o stabilito che questo non è un problema, è assolutamente valido sollevare questa preoccupazione; né dovrebbe essere banalmente sotto votato.
Thomas W,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.