Le comptage des tables contenant une grande quantité de données peut être très lent, parfois quelques minutes; il peut également générer un blocage sur un serveur occupé. Je veux afficher des valeurs réelles, NOLOCK n'est pas une option.
Les serveurs que j'utilise sont SQL Server 2005 ou 2008 Standard ou Enterprise - si cela est important. Je peux imaginer que SQL Server conserve les comptes pour chaque table et s'il n'y a pas de clause WHERE, je pourrais obtenir ce nombre assez rapidement, non?
Par exemple:
SELECT COUNT(*) FROM myTable
devrait immédiatement retourner avec la valeur correcte. Dois-je me fier aux statistiques pour être mis à jour?
Une approximation très proche (en ignorant toutes les transactions en vol) serait:
SELECT SUM(p.rows) FROM sys.partitions AS p
INNER JOIN sys.tables AS t
ON p.[object_id] = t.[object_id]
INNER JOIN sys.schemas AS s
ON s.[schema_id] = t.[schema_id]
WHERE t.name = N'myTable'
AND s.name = N'dbo'
AND p.index_id IN (0,1);
Cela retournera beaucoup, beaucoup plus rapidement que COUNT(*)
, et si votre table change assez rapidement, ce n'est pas vraiment moins précis - si votre table a changé entre le moment où vous avez commencé votre COUNT (et les verrous ont été pris) et quand il a été retourné (lorsque les verrous ont été libérés et que toutes les transactions d'écriture en attente étaient désormais autorisées à écrire dans la table), est-ce bien plus précieux? Je ne pense pas.
Si vous avez un sous-ensemble de la table que vous souhaitez compter (par exemple, WHERE some_column IS NULL
), Vous pouvez créer un index filtré sur cette colonne et structurer la clause where d'une manière ou d'une autre, selon qu'il s'agit de la l'exception ou la règle (créez donc l'index filtré sur l'ensemble plus petit). Donc, l'un de ces deux index:
CREATE INDEX IAmTheException ON dbo.table(some_column)
WHERE some_column IS NULL;
CREATE INDEX IAmTheRule ON dbo.table(some_column)
WHERE some_column IS NOT NULL;
Ensuite, vous pouvez obtenir le nombre d'une manière similaire en utilisant:
SELECT SUM(p.rows) FROM sys.partitions AS p
INNER JOIN sys.tables AS t
ON p.[object_id] = t.[object_id]
INNER JOIN sys.schemas AS s
ON s.[schema_id] = t.[schema_id]
INNER JOIN sys.indexes AS i
ON p.index_id = i.index_id
WHERE t.name = N'myTable'
AND s.name = N'dbo'
AND i.name = N'IAmTheException' -- or N'IAmTheRule'
AND p.index_id IN (0,1);
Et si vous voulez savoir le contraire, il vous suffit de soustraire de la première requête ci-dessus.
(Quelle est la taille de "grande quantité de données"? - aurait dû commenter cela en premier, mais peut-être que l'exéc ci-dessous vous aide déjà)
Si j'exécute une requête sur une table statique (cela signifie que personne d'autre n'est ennuyeux avec la lecture/l'écriture/les mises à jour depuis un bon moment, donc la contention n'est pas un problème) table avec 200 millions de lignes et COUNT (*) en 15 secondes sur ma machine de développement ( Oracle). Compte tenu de la quantité pure de données, c'est encore assez rapide (du moins pour moi)
Comme vous l'avez dit, NOLOCK n'est pas une option, vous pourriez envisager
exec sp_spaceused 'myTable'
ainsi que.
Mais cela correspond presque à la même chose que NOLOCK (en ignorant les conflits + supprimer/mettre à jour afaik)
Je travaille avec SSMS depuis plus d'une décennie et ce n'est que l'année dernière que j'ai découvert qu'il pouvait vous fournir ces informations rapidement et facilement, grâce à cette réponse .
Notez que la prise en charge de cela dans les bases de données Azure SQL semble au mieux un peu inégale - je suppose que les requêtes de SSMS arrivent à expiration, donc il ne renvoie qu'une poignée de tables à chaque actualisation, mais celle en surbrillance semble toujours être retournée.
Count effectuera soit une analyse de table soit une analyse d'index. Donc, pour un nombre élevé de lignes, ce sera lent. Si vous effectuez cette opération fréquemment, la meilleure façon est de conserver l'enregistrement de comptage dans une autre table.
Si toutefois vous ne voulez pas faire cela, vous pouvez créer un index factice (qui ne sera pas utilisé par votre requête) et interroger son nombre d'éléments, quelque chose comme:
select
row_count
from sys.dm_db_partition_stats as p
inner join sys.indexes as i
on p.index_id = i.index_id
and p.object_id = i.object_id
where i.name = 'your index'
Je suggère de créer un nouvel index, car celui-ci (s'il ne sera pas utilisé) ne sera pas verrouillé lors des autres opérations.
Comme l'a dit Aaron Bertrand, la maintenance de la requête pourrait être plus coûteuse que l'utilisation d'une requête déjà existante. Donc, c'est à vous de choisir.