2010-02-23 4 views
3

J'essaie de décider quelle approche prendre dans une base de données que je conçois. Je vais ajouter une colonne ProcessedDate datetime null dans un tableau. Il sera nullable lorsque l'enregistrement n'a pas été traité. Alors, vaut-il mieux avoir une colonne Processed bit not null default 0? Quelle est la différence de performance entre les deux versionsDans SQL Server, qu'est-ce qui fonctionne mieux: où ProcessedDate est null ou où Processed = 0

select * from tablename where ProcessedDate is null 

et

select * from tablename where Processed = 0 

Toutes choses étant égales par ailleurs *,:

Avec les requêtes suivantes?

*: Les indices appropriés sont appliqués à la table dans chaque version. Je ne cherche pas de conseil sur les indices à créer. Je veux seulement des informations sur la performance du filtre appliqué à une seule ligne. Si toutes les lignes de la table doivent être scannées, ou si une recherche est effectuée, cela n'a aucun rapport avec la question en question. Je sais que l'argument Processed est plus explicite et donc plus lisible, mais il soulève aussi des problèmes de synchronisation des colonnes (qui pourraient être traitées en utilisant une colonne calculée). Quoi qu'il en soit, je veux limiter la portée de cette question à la performance.

+0

Je sais que la différence de performance sera faible, et probablement négligeable par rapport à d'autres problèmes tels que l'indexation et la sélection de toutes les colonnes, etc. Je connais aussi les modèles de domaine. Je veux juste savoir laquelle des deux options fonctionne le plus rapidement. – David

+0

Pourquoi ne l'essayez-vous pas? Je vais vous acheter une bière virtuelle si vous pouvez mesurer n'importe quelle différence – erikkallen

Répondre

1

Un avantage du prédicat ProcessedDate is null est que vous ne risquez pas d'être mordu par paramétrage (explicite ou implicite).

Si vous utilisez où Processed = 0, SQL Server peut en interne paramètrer cela Processed = @p et la génération du plan de requête basée sur l'hypothèse que @p = 0. Cependant, si vous faites quelque part la même requête mais where Processed = 1, vous pourriez obtenir un plan pour @p = 1, et puisque les données sont susceptibles d'être faussées, il est probable que les plans ne seront pas les mêmes.

+0

Cela signifie-t-il qu'il y a un surcoût potentiel supplémentaire dans la deuxième génération de plan d'exécution pour la version binaire? Est-ce que la version null-checking génère un plan d'exécution plus grand? Je ne suis pas sûr que je vois ce qui se passerait si la requête était exécutée avec ProcessedDate n'est pas null. – David

+0

IS NULL/IS NOT NULL ne peut pas être paramétré. Mais ne vous souciez pas de savoir si le plan est «plus grand» (peu importe ce que cela signifie) ou s'il doit être régénéré. De toutes les choses qui prennent du temps dans un appel de base de données, la génération de plan de requête n'en fait pas partie. – erikkallen

2

Vous ne devriez pas avoir les deux.

Choisissez celui qui correspond le mieux à votre modèle de domaine. Avec le bon index, toute différence de performance sera négligeable (en supposant que la distribution relative de «traité» à «non traité» signifie que l'optimiseur choisira un index en premier lieu).

2

Comme pour la plupart des questions de performances SQL, la réponse dépend de votre schéma de table et non de votre texte SQL. Comme c'est le cas actuellement, les deux versions nécessitent une analyse de table complète, donc elles sont également mauvaises. Un index non clusterisé sur ProcessedDate peut aider la première version, mais puisque vous demandez * dans la projection, le point de basculement d'index peut démarrer et continuer à effectuer une analyse complète. D'autre part, un index clusterisé avec la clé la plus à gauche traitée fonctionnera toujours, et une telle structure d'index clusterisée est commune dans le traitement de la file d'attente (et une colonne 'ProcessedDate' indique clairement que votre table est utilisée comme une queue). Mais alors une colonne la plus à gauche sur ProcessedDate pour l'index clusterisé fonctionnerait également comme le bit traité. Leçon à ramener à la maison: n'est pas la question, c'est le schéma. Toujours.

+0

@Remus Rusanu: +1. très belle réponse. Était juste en train de vérifier votre site web. Quelques informations très utiles ... –

3

Ils sont identiques autant que je peux dire avec des ensembles de données identiques. Vous pouvez exécuter mon exemple ci-dessous et regarder les plans d'exécution à vérifier. Vous verrez que l'élément de filtre où il teste le prédicat occupe la même proportion de temps dans chaque élément. Même si des index sont ajoutés, ils ne sont pas plus efficaces que les autres, car ils font la même chose.


WITH Test (MyInt, MyNull) 
AS 
(
SELECT 1 AS MyInt, Null AS MyNull 

UNION ALL 

SELECT 
    MyInt + 1, 
    CASE 
     WHEN MyInt % 2 = 0 THEN Null 
     ELSE MyInt 
    END 
FROM Test 
WHERE MyInt < 10000 
) 

SELECT * FROM Test 
WHERE MyNull IS NULL 
OPTION (MAXRECURSION 32767); 

WITH Test (MyInt, MyBit) 
AS 
(
SELECT 1 AS MyInt, 0 AS MyBit 

UNION ALL 

SELECT 
    MyInt + 1, 
    CASE 
     WHEN MyInt % 2 = 0 THEN 0 
     ELSE 1 
    END 
FROM Test 
WHERE MyInt < 10000 
) 

SELECT * FROM Test 
WHERE MyBit = 0 
OPTION (MAXRECURSION 32767); 

Je sais que mon exemple est contraint, mais il utilise un nombre égal de valeurs nulles et des 0. Même si vous réécrivez le CTE pour chaque exemple dans les variables de table, vous devriez voir la même performance.