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.