Pour le schéma et les exemples de données suivants
CREATE TABLE T
(
A INT NULL,
B INT NOT NULL IDENTITY,
C CHAR(8000) NULL,
UNIQUE CLUSTERED (A, B)
)
INSERT INTO T
(A)
SELECT NULLIF(( ( ROW_NUMBER() OVER (ORDER BY @@SPID) - 1 ) / 1003 ), 0)
FROM master..spt_values
Une application traite les lignes de cette table dans un ordre d'index cluster en 1 000 morceaux de ligne.
Les 1 000 premières lignes sont extraites de la requête suivante.
SELECT TOP 1000 *
FROM T
ORDER BY A, B
La dernière ligne de cet ensemble est ci-dessous
+------+------+
| A | B |
+------+------+
| NULL | 1000 |
+------+------+
Existe-t-il un moyen d'écrire une requête qui cherche simplement dans cette clé d'index composite, puis la suit pour récupérer le prochain bloc de 1000 lignes?
/*Pseudo Syntax*/
SELECT TOP 1000 *
FROM T
WHERE (A, B) is_ordered_after (@A, @B)
ORDER BY A, B
Le nombre de lectures le plus bas que j'ai réussi à obtenir jusqu'à présent est de 1020, mais la requête semble beaucoup trop compliquée. Existe-t-il un moyen plus simple d'efficacité égale ou meilleure? Peut-être que celui qui parvient à tout faire dans une gamme cherche?
DECLARE @A INT = NULL, @B INT = 1000
;WITH UnProcessed
AS (SELECT *
FROM T
WHERE ( EXISTS(SELECT A
INTERSECT
SELECT @A)
AND B > @B )
UNION ALL
SELECT *
FROM T
WHERE @A IS NULL AND A IS NOT NULL
UNION ALL
SELECT *
FROM T
WHERE A > @A
)
SELECT TOP 1000 *
FROM UnProcessed
ORDER BY A,
B
FWIW: Si la colonne A
est créée NOT NULL
et qu'une valeur sentinelle de -1
est utilisée à la place, le plan d'exécution équivalent semble certainement plus simple
Mais l'opérateur de recherche unique dans le plan effectue toujours deux recherches plutôt que de le réduire en une seule plage contiguë et les lectures logiques sont à peu près les mêmes, donc je soupçonne que c'est peut-être à peu près aussi bon que possible?
la source
NULL
valeurs sont toujours les premières. (supposé le contraire.) Condition corrigée à Fiddle(NULL, 1000 )
@A
soit nul ou non, il semble qu'il ne fasse pas de scan. Mais je ne peux pas comprendre si les plans sont meilleurs que votre requête. Fiddle-2Réponses:
Une de mes solutions préférées consiste à utiliser un
API
curseur:La stratégie globale est un balayage unique qui se souvient de sa position entre les appels. L'utilisation d'un
API
curseur signifie que nous pouvons retourner un bloc de lignes plutôt qu'une à la fois comme ce serait le cas avec unT-SQL
curseur:La
STATISTICS IO
sortie est:la source