Mise à jour d'une clause WHERE pour vérifier si une valeur N'EST PAS dans une table séparée


8

J'ai une requête qui utilise une WHEREclause, et il se trouve que j'utilise exactement la même WHEREclause dans de nombreuses requêtes sur cette table (et al).

La requête est:

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 partie que je souhaite modifier est la WHEREclause, pour me permettre d'utiliser un tableau afin que si je dois ajouter un numéro de client à ignorer, je n'ai pas à mettre à jour toutes mes requêtes. (Et il y a pas mal de requêtes qui ont cette même WHEREclause.)


Si les exclusions client sont actuellement spécifiques à l'exécution de la requête, pourquoi les déplacer vers une table / table de travail partagée n'introduirait-il pas un faux partage? Dans une application normale, les clients seraient généralement arbitraires et donc spécifiques à une seule exécution de requête. Je suggérerais que cette question soit omet des faits importants quant à la généralité nécessaire pour que la solution fonctionne correctement, soit néglige la question du partage.
Thomas W

@ThomasW - quel est ce "faux partage" dont vous parlez? Avez-vous une référence pour cela? Je n'en ai jamais entendu parler auparavant.
Max Vernon

1
@ThomasW Les exigences pour cela sont que certains clients que nous avons (que nous utilisons beaucoup pour les tests) doivent être exclus de certains rapports, car ils faussent les résultats.
Der Kommissar

1
@MaxVernon - peut-être un terme mieux reconnu serait "portée incorrecte". Ce qui a été décrit impliquait de changer une entrée d'un paramètre complètement indépendant en une table de base de données partagée multi-utilisateurs et à invocation croisée. Ce changement traverse 2 limites de portée. Compte tenu du contexte supplémentaire, la portée décrite semble correcte, mais si ce n'était pas le cas, cela se manifesterait par un "partage erroné".
Thomas W

1
L'approche décrite rappelle également beaucoup d'implémentations de tables de travail héritées (~ 1000 tables) dans une application majeure dont j'ai la responsabilité. À cet égard, j'ai soulevé la nature possible d'une "table de travail" comme question :) Merci.
Thomas W

Réponses:


5

Créez un tableau pour contenir les numéros de client à exclure, puis excluez ces lignes à l'aide d'un NOT EXISTSdans la WHEREclause.

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

Maintenant, votre WHEREclause dans toutes les requêtes devient:

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

Malheureusement oui. Les numéros de client doivent être une chaîne pour la compatibilité croisée avec l'AS / 400. (Au moins pour l'instant, nous travaillons sur un correctif pour cela.)
Der Kommissar

3
@EBrown Uh, ugh.
Aaron Bertrand

-3

Il y a des questions importantes / des problèmes potentiels avec votre approche proposée. Bien sûr, vous pouvez exclure assez facilement via une table de travail «exclusion du numéro client»:

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

Mais maintenant, quels étaient les "paramètres de requête" - entièrement dynamiques et indépendants, par requête et par utilisateur - se transforment en "état persistant partagé dans la base de données".

Quelques questions et points pertinents:

  1. les informations d'exclusion de client doivent-elles être distinctes, par utilisateur ou par session? vous pouvez ajouter un paramètre 'SessionID' pour les distinguer, mais vous recréez essentiellement un ancien modèle de "table de travail".

  2. peut-être qu'une clause NOT IN (...) pourrait être préférable? qui peut être paramétré dynamiquement, jusqu'à la limite de 2100 paramètres.

  3. revisitez peut-être votre code / infrastructure pour créer des paramètres de requêtes et de liaisons, si vous comptez actuellement sur des numéros de paramètres fixes; l'amélioration de ceci permettra la modularité et l'utilisation d'une clause IN ou NOT IN (?,?,? ..) avec un nombre variable de paramètres.

Approche suggérée:

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

Avec les liaisons '1234', '5678', '6789', etc. aux paramètres NOT IN () et aux paramètres de requête logique ultérieurs liés dynamiquement à la numérotation appropriée.


1
L'utilisation de NOT IN (...) et / ou de la création dynamique de texte de requête est un anti-modèle et entraînera des performances inférieures aux approches basées sur les ensembles recommandées par Aaron et moi-même.
Max Vernon

Pour une excellente lecture sur les différences, consultez cet article.
Max Vernon

@MaxVernon - le remplacement des paramètres dynamiques par des données ou des tables de travail "partagées" peut introduire un faux partage, ce qui est beaucoup plus anti-modèle. Étant donné que personne d'autre n'a spécifiquement considéré ou établi que ce n'est pas un problème, il est absolument valable de soulever cette préoccupation; elle ne devrait pas non plus être décrétée trivialement.
Thomas W
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.