2010-09-24 10 views
1

Dans SQL Server 2005, compte tenu de l'ensemble de résultats suivantnombre Soustraction de lignes en sous-requête de requête en cours

ID | InstanceNumber  | IsArchived 

5000 |   1   |  True 
8347 |   2   |  True 
9343 |   3   |  False 
11048 |   4   |  False 

Ce que je voudrais revenir est la suivante:

ID | InstanceNumber  | IsArchived 

9343 |   1   |  False 
11048 |   2   |  False 

où les lignes avec « IsArchived "false est renvoyé, mais en soustrayant la colonne Max InstanceNumber du resultset.

Voici un exemple instruction SQL qui retourne le comportement que je suis à la recherche:

DECLARE @tbl TABLE 
(ID INT NOT NULL, InstanceNumber INT NOT NULL, IsArchived BIT NOT NULL) 

INSERT INTO @tbl VALUES (5000, 1, 1) 
INSERT INTO @tbl VALUES (8347, 2, 1) 
INSERT INTO @tbl VALUES (9343, 3, 0) 
INSERT INTO @tbl VALUES (11048, 4, 0) 

SELECT ID, InstanceNumber - (SELECT MAX(InstanceNumber) FROM @tbl WHERE IsArchived = 1), IsArchived 
FROM @tbl 
WHERE IsArchived = 0 

Est-ce la façon la plus efficace de le faire ou est-il une autre façon qui peut être atteint ce même comportement? J'ai des clauses where supplémentaires qui doivent aller dans la déclaration complète (comme 5-6 déclarations) et je veux éviter d'avoir à les déclarer 2X, une fois pour retourner l'instance max qui est archivée, et pour le filtrage du jeu de résultats. Pour clarifier l'exigence de la requête, la colonne "InstanceNumber" pourrait ignorer les nombres. donc il pourrait y avoir un enregistrement pour InstanceNumber = 6 sans en retourner un pour 5, donc tous les enregistrements retournés ne seront pas séquentiels.

Répondre

1

Dans mon test, l'expliquer plan était identique entre votre version à l'aide d'un sous-sélection et ma version à l'aide d'un CROSS JOIN:

SELECT x.id, 
      x.instancenumber - y.max_num AS instancenumber, 
      x.isarchived 
     FROM @tbl x 
CROSS JOIN (SELECT MAX(InstanceNumber) AS max_num 
       FROM @tbl 
      WHERE IsArchived = 1) y 
    WHERE x.isarchived = 0 
0

Essayez ceci:

SELECT 
    ID, 
    InstanceNumber = Row_Number() OVER (ORDER BY InstanceNumber), 
    IsArchived 
FROM @tbl 
WHERE IsArchived = 0 

Une question, cependant, est Et s'il y a une lacune dans les données archivées?

INSERT INTO @tbl VALUES (5000, 1, 1) 
INSERT INTO @tbl VALUES (8347, 2, 0) 
INSERT INTO @tbl VALUES (9343, 3, 1) 
INSERT INTO @tbl VALUES (11048, 4, 0) 

Quels sont les résultats que vous souhaitez dans ce cas? Votre requête actuelle donne le numéro d'instance -1 et 1. Ma requête ci-dessus donne InstanceNumber 1 et 2. Une autre réponse possible est de renvoyer InstanceNumber 1 et 3 (représentant l'étape InstanceNumber de 2 entre le 8347 et 11048).

Mise à jour

Donc, si je comprends bien la possibilité de lacunes, vous devez changer votre requête pour traiter le cas suivant:

INSERT INTO @tbl VALUES (5000, 1, 1) 
INSERT INTO @tbl VALUES (8347, 2, 1) 
INSERT INTO @tbl VALUES (9343, 4, 0) 
INSERT INTO @tbl VALUES (11048, 5, 0) 

SELECT 
    ID, 
    NewInstanceNumber = InstanceNumber + 1 
     - (SELECT Min(InstanceNumber) FROM @tbl WHERE IsArchived = 0), 
    IsArchived 
FROM @tbl 
WHERE IsArchived = 0 

Alors que la numérotation commence toujours à 1 Vous pouvez également essayer ceci:

SELECT 
    ID, 
    NewInstanceNumber = InstanceNumber + 1 - Min(InstanceNumber) OVER(), 
    IsArchived 
FROM @tbl 
WHERE IsArchived = 0 

Mais je ne sais pas si ce sera meilleur ou pire que votre actuel ry.

+0

Heureusement, il ne devrait pas y avoir d'espace dans l'archive de manière séquentielle. Les chiffres les plus bas seront toujours les enregistrements archivés potentiels. J'ai oublié de mentionner une chose avec les enregistrements est qu'il pourrait y avoir une rupture dans le "numéro d'instance" qui est retourné. –

+0

Donc, ma solution ne fonctionnera pas car elle ne va pas préserver les lacunes? – ErikE

0
SELECT id, 
     instancenumber - 
      MAX(CASE IsArchived WHEN true THEN instancenumber ELSE 0 END) 
      OVER() as NewInstanceNumber, 
     false AS IsArchived 
FROM @tbl 
WHERE IsArchived = false 

Avertissement. Je n'ai pas testé cela du tout.

Bit d'un avertissement sur les autres réponses:
Utilisation d'un CROSS JOIN ou une sous-sélection peut potentiellement causer des problèmes dans un cas rare, quand un nouveau record avec IsArchived = true est inséré mi requête (ou un enregistrement existant est changé de IsArchived = false à IsArchived = true).

Si SELECT MAX(InstanceNumber) partie de la requête est d'abord traitée, la partie principale de la requête peut être soustraire une valeur qui n'est plus MAX(InstanceNumber) à ce moment-là.

Avec une fonction de fenêtre agrégée MAX() OVER(), les données réelles sont analysées une seule fois, ce qui évite ce problème.

Questions connexes