Basé sur ces questions et les réponses données:
SQL 2008 Server - Perte de performance éventuellement connectée avec une très grande table
J'ai une table dans une base de données supervisionP définie comme ceci:
CREATE TABLE [dbo].[PenData](
[IDUkazatel] [smallint] NOT NULL,
[Cas] [datetime2](0) NOT NULL,
[Hodnota] [real] NULL,
[HodnotaMax] [real] NULL,
[HodnotaMin] [real] NULL,
CONSTRAINT [PK_Data] PRIMARY KEY CLUSTERED
(
[IDUkazatel] ASC,
[Cas] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[PenData] WITH NOCHECK ADD CONSTRAINT [FK_Data_Ukazatel] FOREIGN KEY([IDUkazatel])
REFERENCES [dbo].[Ukazatel] ([IDUkazatel])
ALTER TABLE [dbo].[PenData] CHECK CONSTRAINT [FK_Data_Ukazatel]
Il contient des rangées CCA 211 milion.
Je cours après la déclaration suivante:
DECLARE @t1 DATETIME;
DECLARE @t2 DATETIME;
SET @t1 = GETDATE();
SELECT min(cas) from PenData p WHERE IDUkazatel=24
SELECT min(cas) from PenData p WHERE IDUkazatel=25
SET @t2 = GETDATE();
SELECT DATEDIFF(millisecond,@t1,@t2) AS elapsed_ms;
SET @t1 = GETDATE();
SELECT min(cas) from PenData p WHERE IDUkazatel=24 OR IDUkazatel=25
SET @t2 = GETDATE();
SELECT DATEDIFF(millisecond,@t1,@t2) AS elapsed_ms;
Le résultat est montré ici:
Le troisième sélection est également chargé beaucoup plus de données dans le cache de mémoire SQL Server.
Pourquoi le troisième sélection est-il tellement plus lent (8,5 s) alors les deux premiers sélectionneurs (16 ms)? Comment puis-je améliorer les performances du troisième sélection avec ou? Je veux courir après la commande SQL, mais il me semble que la création de curseur et de faire fonctionner des requêtes distinctes est beaucoup plus rapide qu'une seule sélection dans ce cas.
SELECT MIN(cas) from PenData p WHERE IDUkazatel IN (SELECT IDUkazatel FROM ...)
[~ # ~] Edit [~ # ~ #]
Comme David suggérait que j'ai survolé la flèche de la graisse:
Pour les deux premières requêtes, tout ce qu'il faut faire est de numériser dans l'index en cluster sur la première entrée de cette valeur de IDUkazatel
- en raison de l'ordre de l'index que la rangée sera la valeur la plus basse pour la valeur de cette valeur de IDUkazatel
.
Dans la deuxième requête, cette optimisation n'est pas une valeur et elle cherche probablement à la première rangée pour IDUkazatel=24
puis scanner l'index jusqu'à la dernière ligne avec IDUkazatel=25
Pour trouver la valeur minimale de cas
sur toutes ces lignes.
Si vous survolez la flèche de la graisse, vous verrez qu'il lise de nombreuses lignes (certainement toutes celles de 24, probablement toutes celles de 25), tandis que les flèches minces dans la sortie du plan pour les deux autres montrent le top
Action le faisant prendre en compte seulement une ligne.
Vous pouvez essayer d'exécuter chaque requête puis obtenir le minimum pour les minimums trouvés:
SELECT MIN(cas)
FROM (
SELECT cas=MIN(cas) FROM PenData p WHERE p.IDUkazatel = 24
UNION ALL
SELECT cas=MIN(cas) FROM PenData p WHERE p.IDUkazatel = 25
) AS minimums
Cela dit, il semble que vous ayez une table avec IDUkazatel
valeurs plutôt qu'une clause explicite OR
. Le code ci-dessous fonctionnera avec cet arrangement, remplacez simplement le nom de la table @T
avec le nom de la table contenant IDUkazatel
valeurs:
SELECT
MinCas = MIN(CA.PartialMinimum)
FROM @T AS T
CROSS APPLY
(
SELECT
PartialMinimum = MIN(PD.Cas)
FROM dbo.PenData AS PD
WHERE
PD.IDUkazatel = T.IDUkazatel
) AS CA;
Dans un monde idéal, l'optimiseur SQL Server Query effectuerait cette réécriture pour vous, mais elle ne considère pas toujours cette option aujourd'hui.