Le moyen le plus rapide pour compter le nombre exact de lignes dans une très grande table?

j'ai trouvé des articles qui disent que SELECT COUNT(*) FROM TABLE_NAME sera lent quand la table a beaucoup de lignes et beaucoup de colonnes.

j'ai un tableau qui pourrait contenir même des milliards de lignes [il a environ 15 colonnes]. Y a-t-il une meilleure façon d'obtenir le EXACT compte du nombre de lignes d'une table?

veuillez tenir compte de ce qui suit avant de répondre:

  • je suis recherche d'un vendeur de bases de données solution indépendante. C'est OK si c'est couvre MySQL , Oracle , MS SQL Server . Mais s'il y a vraiment pas de base de données solution indépendante du fournisseur se contentera de différentes solutions pour différents fournisseurs de bases de données.

  • Je ne peux utiliser aucun autre outil externe pour ce faire. Je suis principalement à la recherche d'un Solution basée sur SQL.

  • Je ne peux pas normaliser la conception de ma base de données toute autre. Il est déjà en 3NF et en outre un beaucoup de code a déjà été écrit autour de lui.

184
demandé sur Mike Woodhouse 2011-05-20 12:18:29

24 réponses

Simple réponse:

  • base de données solution indépendante du vendeur = utiliser la norme = COUNT(*)
  • Il y a approximative "de la 1519120920" SQL Server des solutions, mais ne pas utiliser COUNT(*) = hors de portée

Notes:

COUNT (1) = COUNT (*) = COUNT(PrimaryKey) just in case

Edit:

exemple de Serveur SQL (1,4 milliards de lignes, 12 colonnes)

SELECT COUNT(*) FROM MyBigtable WITH (NOLOCK)
-- NOLOCK here is for me only to let me test for this answer: no more, no less

1 s'exécute, 5:46 minutes, compteur = 1,401,659,700

--Note, sp_spaceused uses this DMV
SELECT
   Total_Rows= SUM(st.row_count)
FROM
   sys.dm_db_partition_stats st
WHERE
    object_name(object_id) = 'MyBigtable' AND (index_id < 2)

2 passages de moins d'une seconde, compte = 1,401,659,670

le second a moins de rows = wrong. Serait le même ou plus selon écrit (les suppressions sont faites en dehors des heures ici)

202
répondu gbn 2018-03-28 14:37:16

le chemin le plus rapide sur MySQL est de loin:

SHOW TABLE STATUS;

vous obtiendrez instantanément toutes vos tables avec le nombre de lignes (qui est le total) avec beaucoup d'informations supplémentaires si vous voulez.

24
répondu salbahra 2012-03-25 23:41:59

j'ai trouvé des articles qui indiquent que SELECT COUNT(*) de TABLE_NAME sera lent quand la table a beaucoup de lignes et beaucoup de colonnes.

cela dépend de la base de données. Certains accélèrent les dénombrements, par exemple en gardant la trace de savoir si les lignes sont vivantes ou mortes dans l'index, ce qui permet un scan de l'index seulement pour extraire le nombre de lignes. D'autres ne le font pas, et nécessitent donc de visiter la table entière et de compter les lignes en direct un par un. Soit sera lente pour une grande table.

notez que vous pouvez généralement extraire une bonne estimation en utilisant des outils d'optimisation des requêtes, des statistiques de table, etc. Dans le cas de PostgreSQL, par exemple, vous pouvez analyser la sortie de explain count(*) from yourtable et obtenir une estimation raisonnable du nombre de lignes. Ce qui m'amène à votre deuxième question.

j'ai une table qui pourrait contenir même des milliards de lignes [il a environ 15 colonne.] Y a-t-il un meilleur moyen d'obtenir le nombre EXACT de rangées d'une table?

sérieusement? :- ) Vous voulez vraiment dire le exact compte à partir d'une table avec des milliards de lignes? Êtes-vous vraiment sûr? :- )

Si vous vraiment faire, vous pouvez garder une trace de la total à l'aide de déclencheurs, mais l'esprit de la simultanéité et de blocages si vous le faites.

10
répondu Denis de Bernardy 2011-05-20 08:49:16

vous pouvez essayer ce sp_spaceused (Transact-SQL)

affiche le nombre de lignes, disque espace réservé et espace disque utilisé par une table, une vue indexée ou un Service File d'attente des courtiers dans la base de données actuelle, ou affiche l'espace disque réservé et utilisé par l'ensemble de la base de données.

8
répondu jams 2011-05-20 08:26:40

y a-t-il un meilleur moyen d'obtenir le nombre EXACT de rangées d'une table?

pour répondre À votre question, tout simplement, Non .

si vous avez besoin d'une façon indépendante de faire DBMS, le plus rapide voie sera toujours:

SELECT COUNT(*) FROM TableName

certains vendeurs de SGBD peuvent avoir des moyens plus rapides qui ne fonctionneront que pour leurs systèmes. Certaines de ces options sont déjà posté dans d'autres réponses.

COUNT(*) doit être optimisé par le SGBD (au moins n'importe quel SGBD PROD) de toute façon, donc n'essayez pas de contourner leurs optimisations.

sur une note latérale:

Je suis sûr que beaucoup de vos autres requêtes prennent aussi beaucoup de temps à finir en raison de la taille de votre table. Tout problème de performance devrait probablement être résolu en pensant à votre conception de schéma avec la vitesse à l'esprit. Je me rends compte que tu as dit que c'était ce n'est pas une option à changer, mais il se peut que les requêtes de plus de 10 minutes ne soient pas une option non plus. 3rd NF n'est pas toujours la meilleure approche quand vous avez besoin de vitesse, et parfois les données peuvent être partitionnées dans plusieurs tableaux si les enregistrements ne ont à stocker ensemble. Quelque chose à penser...

6
répondu Jesse Webb 2011-05-26 19:46:51

j'utilise

select /*+ parallel(a) */  count(1) from table_name a;
5
répondu Mainsh S 2011-07-03 09:55:13

je suis loin d'être aussi expert que d'autres qui ont répondu, mais j'avais un problème avec une procédure que j'utilisais pour sélectionner une rangée aléatoire à partir d'une table (pas trop pertinente), mais j'avais besoin de connaître le nombre de rangées dans ma table de référence pour calculer l'index aléatoire. En utilisant le travail traditionnel de Count (*) ou Count (1), mais j'obtenais parfois jusqu'à 2 secondes pour lancer ma requête. Donc à la place (pour ma table nommée 'tbl_HighOrder') j'utilise:

Declare @max int

Select @max = Row_Count
From sys.dm_db_partition_stats
Where Object_Name(Object_Id) = 'tbl_HighOrder'

It fonctionne bien et les temps de requête dans le Studio de gestion sont zéro.

5
répondu john rains 2013-06-30 01:43:37

si SQL Server edition est 2005/2008, vous pouvez utiliser DMVs pour calculer le nombre de lignes dans une table:

-- Shows all user tables and row counts for the current database 
-- Remove is_ms_shipped = 0 check to include system objects 
-- i.index_id < 2 indicates clustered index (1) or hash table (0) 
SELECT o.name, 
 ddps.row_count 
FROM sys.indexes AS i 
 INNER JOIN sys.objects AS o ON i.OBJECT_ID = o.OBJECT_ID 
 INNER JOIN sys.dm_db_partition_stats AS ddps ON i.OBJECT_ID = ddps.OBJECT_ID 
 AND i.index_id = ddps.index_id 
WHERE i.index_id < 2 
 AND o.is_ms_shipped = 0 
ORDER BY o.NAME 

pour le moteur de base de données SQL Server 2000, sysindexes fonctionnera, mais il est fortement conseillé d'éviter de l'utiliser dans les futures éditions de SQL Server car il peut être supprimé dans un proche avenir.

exemple de code tiré de: Comment obtenir des comptes de lignes de Table rapidement et sans douleur

4
répondu Alireza Maddah 2011-05-20 09:01:29

bien, en retard de 5 ans et incertain si cela aide:

j'essayais de compter le non. des lignes dans une table de serveur SQL en utilisant MS SQL Server Management Studio et j'ai rencontré une erreur de débordement, puis j'ai utilisé ce qui suit :

sélectionner count_big (1) à partir de [dbname].[dbo].[FactSampleValue];

le résultat:

24296650578 lignes

4
répondu Kaliyug Antagonist 2016-05-19 09:01:50

Je ne pense pas qu'il y ait une solution générale toujours la plus rapide: certaines versions/RDBMS ont une optimisation spécifique pour SELECT COUNT(*) qui utilisent des options plus rapides tandis que d'autres numérisent simplement la table. Vous aurez besoin d'aller sur les sites de documentation/support pour le second jeu, qui aura probablement besoin d'une requête plus spécifique pour être écrit, généralement celui qui frappe un index d'une certaine manière.

EDIT:

Voici une pensée qui pourrait fonctionner, selon votre schéma et distribution des données: Avez-vous une colonne indexée qui fait référence à une valeur croissante, un ID croissant numérique, par exemple, ou même un horodatage ou une date? Ensuite, en supposant que les suppressions ne se produisent pas, il devrait être possible de stocker le compte jusqu'à une valeur récente (date d'hier, valeur ID la plus élevée à un point d'échantillonnage récent) et ajouter le compte au-delà de cela, ce qui devrait résoudre très rapidement dans l'indice. Très dépendant des valeurs et des indices, bien sûr, mais applicable à peu près à n'importe quelle version de N'importe quel SGBD.

3
répondu Mike Woodhouse 2011-05-20 10:50:37

ce n'est pas exactement une solution DBMS-agnostique, mais au moins votre code client ne verra pas la différence...

créer une autre table T avec juste une ligne et un champ entier N 1 , et créer INSERT TRIGGER qui exécute juste:

UPDATE T SET N = N + 1

crée aussi un déclencheur de suppression qui exécute:

UPDATE T SET N = N - 1

un SGBD valant son sel garantira l'atomicité des opérations au-dessus de 2 , et N contiendra le nombre exact de lignes à tout moment, qui est alors super-rapide pour obtenir par simplement:

SELECT N FROM T

bien que les déclencheurs soient spécifiques aux SGBD, choisir à partir de T n'est pas et votre code client n'aura pas besoin de changer pour chaque SGBD pris en charge.

cependant, cela peut avoir des problèmes d'évolutivité si la table est à forte intensité D'insertion ou de suppression, surtout si vous ne commettez pas immédiatement après INSERT/DELETE.


1 ces noms ne sont que des espaces réservés - utilisez quelque chose de plus significatif dans la production.

2 c'est-à-dire: N ne peut pas être modifié par une transaction simultanée entre la lecture et l'écriture à N, tant que la lecture et l'écriture se font dans une seule instruction SQL.

2
répondu Branko Dimitrijevic 2013-06-12 11:02:55

une réponse littéralement folle, mais si vous avez une sorte de système de réplication mis en place (pour un système avec un milliard de lignes, j'espère que vous le faites), vous pouvez utiliser un estimateur approximatif (comme MAX(pk) ), diviser cette valeur par le nombre d'esclaves que vous avez, exécuter plusieurs requêtes en parallèle.

pour la plupart, vous répartissez les requêtes entre les esclaves en fonction de la meilleure clé (ou de la clé primaire, je suppose), d'une telle manière (nous allons utiliser 250000000 comme nos lignes / esclaves):

-- First slave
SELECT COUNT(pk) FROM t WHERE pk < 250000000
-- Ith slave where 2 <= I <= N - 1
SELECT COUNT(pk) FROM t WHERE pk >= I*250000000 and pk < (I+1)*250000000
-- Last slave
SELECT COUNT(pk) FROM t WHERE pk > (N-1)*250000000

mais vous avez besoin de SQL seulement. Ce buste. OK, alors disons que tu es un sadomasochiste. Sur le maître (ou l'esclave le plus proche) vous aurez très probablement besoin de créer une table pour cela:

CREATE TABLE counter_table (minpk integer, maxpk integer, cnt integer, slaveid integer)

donc, au lieu d'avoir seulement les sélections dans vos esclaves, vous devez faire un insert, semblable à ceci:

INSERT INTO counter_table VALUES (I*25000000, (I+1)*250000000, (SELECT COUNT(pk) FROM ... ), @@SLAVE_ID)

vous pouvez rencontrer des problèmes avec des esclaves écrivant à une table sur le maître. Vous aurez peut - être besoin d'encore plus de sadis-I moyenne, de la créatrice:

-- A table per slave!
INSERT INTO counter_table_slave_I VALUES (...)

vous devriez à la fin avoir un esclave qui existe en dernier dans le chemin traversé par le graphe de réplication, par rapport au premier esclave. Cet esclave devrait maintenant avoir toutes les autres contre-valeurs, et devrait avoir ses propres valeurs. Mais au moment où vous avez terminé, il y a probablement des lignes ajoutées, donc vous devez insérer une autre compensation pour le max PK enregistré dans votre counter_table et le max PK actuel.

à ce point, vous devriez faire une fonction agrégée pour comprendre ce que sont les lignes totales, mais c'est plus facile puisque vous l'exécuteriez sur au plus le "nombre d'esclaves que vous avez et changez" les lignes.

si vous êtes dans la situation où vous avez des tables séparées dans les esclaves, vous pouvez UNION pour obtenir toutes les lignes que vous avez besoin.

SELECT SUM(cnt) FROM (
    SELECT * FROM counter_table_slave_1
      UNION
    SELECT * FROM counter_table_slave_2
      UNION
    ...
  )

ou vous savez, être un peu moins fou et migrer vos données vers un système de traitement distribué, ou peut-être utiliser une Solution d'entreposage (qui vous donnera génial données croquant dans le futur).

notez, cela dépend de la façon dont votre réplication est configurée. Puisque le goulot d'étranglement principal sera très probablement le stockage persistant, si vous avez le stockage cruddy ou des magasins de données mal séparées avec le bruit lourd voisin, ce sera probablement vous exécuter plus lent que juste en attendant un seul SELECT COUNT(*) ...

mais si vous avez une bonne réplication, alors votre vitesse augmente devrait être directement liée au nombre des esclaves. En fait, s'il faut 10 minutes pour lancer la requête de comptage seul, et que vous avez 8 esclaves, vous réduiriez votre temps à moins de quelques minutes. Peut-être une heure pour régler les détails de cette solution.

bien sûr, vous n'obtiendriez jamais vraiment une réponse étonnamment précise puisque cette résolution distribuée introduit un peu de temps où les lignes peuvent être supprimées et insérées, mais vous pouvez essayer d'obtenir un verrou distribué de lignes à la même exemple et obtenir un compte précis des lignes dans le tableau pour un moment particulier dans le temps.

en fait, cela semble impossible, puisque vous êtes essentiellement coincé avec une solution SQL-seulement, et je ne pense pas que vous êtes fourni un mécanisme pour exécuter une requête partagée et verrouillé à travers plusieurs esclaves, instantanément. Peut-être que si vous aviez le contrôle du fichier journal de réplication... ce qui veut dire que vous seriez littéralement des esclaves tourneurs dans ce but, ce qui est sans doute plus lent que juste courir le comptez la requête sur une seule machine de toute façon.

voilà donc mes deux pennies 2013.

2
répondu Yangmun Choi 2013-09-17 08:45:25

si insert trigger est trop cher à utiliser, mais un supprimer trigger pourrait être accordée, et il ya un auto-incrément id , puis après avoir compté toute la table une fois, et en se rappelant le compte comme last-count et le last-counted-id ,

puis chaque jour il suffit de compter pour id > last-counted-id , ajouter à last-count , et stocker le nouveau last-counted-id .

le déclencheur de suppression décréterait le dernier décompte, si l'id de l'enregistrement supprimé <= last-count-id.

2
répondu ToolmakerSteve 2016-09-02 14:50:18

je suis en retard à cette question, Mais voici ce que vous pouvez faire avec MySQL (comme J'utilise MySQL). Je partage ici mes observations:

1) SELECT COUNT(*) AS TOTAL_ROWS FROM <TABLE_NAME>

résultat

Nombre De Lignes: 508534

Sortie de la Console: lignes touchées: 0 lignes trouvées: 1 Avertissements: 0 Durée d'une requête: 0.125 sec.

Prend du temps pour que d'une table avec un grand nombre de lignes, mais le nombre de lignes est très exacte.

2) SHOW TABLE STATUS or SHOW TABLE STATUS WHERE NAME="<TABLE_NAME>"

résultat

Nombre de lignes: 511235

Sortie de la Console: lignes touchées: 0 lignes trouvées: 1 Avertissements: 0 Durée d'une requête: 0.250 sec Résumé: le nombre de lignes N'est pas exact.

3) SELECT * FROM information_schema.tables WHERE table_schema = DATABASE();

résultat

Nombre de lignes: 507806

Console sortie: lignes touchées: 0 lignes trouvées: 48 Avertissements: 0 Durée d'une requête: 1.701 sec.

Le nombre de rangs n'est pas exact.

Je ne suis pas un expert MySQL ou de base de données, mais j'ai trouvé que pour les très grandes tables, vous pouvez utiliser l'option 2 ou 3 et obtenir une 'idée juste' du nombre de lignes présentes.

j'avais besoin de ces nombres de lignes pour afficher des statistiques sur L'UI. Avec les requêtes ci-dessus, je savais que le total des lignes étaient plus de 500.000, donc je suis venu avec des statistiques comme "plus de 500.000 rangées" sans montrer le nombre exact de rangées.

peut-être que je n'ai pas vraiment répondu à la question de L'OP, mais je partage ce que j'ai fait dans une situation où de telles statistiques étaient nécessaires. Dans mon cas, le fait de montrer les rangées approximatives était acceptable et cela a fonctionné pour moi.

2
répondu sunitkatkar 2017-09-18 16:40:02

si vous avez une structure de table typique avec une colonne clé primaire auto-incrémentante dans laquelle les lignes ne sont jamais supprimées, ce qui suit sera le moyen le plus rapide pour déterminer le nombre d'enregistrements et devrait fonctionner de manière similaire dans la plupart des bases de données conformes à L'ANSI:

SELECT TOP(1) <primarykeyfield> FROM <table> ORDER BY <primarykeyfield> DESC;

je travaille avec des tables MS SQL contenant des milliards de lignes qui nécessitent des temps de réponse inférieurs à la seconde pour les données, y compris le nombre d'enregistrements. Un comptage SELECT similaire (*) prendrait quelques minutes à traiter par comparaison.

1
répondu KevinS 2013-11-13 04:22:21

j'ai trouvé ce bon article SQL Server–COMMENT: récupérer rapidement précise le nombre de lignes de la table à partir de martijnh1 qui donne un bon résumé de chacun des scénarios.

j'ai besoin que cela soit élargi où j'ai besoin de fournir un compte basé sur une condition spécifique et quand je chiffre cette partie, je vais mettre à jour cette réponse plus loin.

En attendant, voici les détails de l'article:

Méthode 1:

Requête:

SELECT COUNT(*) FROM Transactions 

commentaires:

effectue un balayage complet de la table. Lent sur les grandes tables.

Méthode 2:

Requête:

SELECT CONVERT(bigint, rows) 
FROM sysindexes 
WHERE id = OBJECT_ID('Transactions') 
AND indid < 2 

commentaires:

moyen Rapide de récupérez le nombre de rangées. Dépend des statistiques et est inexacte.

exécuter DBCC UPDATEUSAGE(base de données) avec COUNT_ROWS, ce qui peut prendre beaucoup de temps pour les grandes tables.

Méthode 3:

Requête:

SELECT CAST(p.rows AS float) 
FROM sys.tables AS tbl 
INNER JOIN sys.indexes AS idx ON idx.object_id = tbl.object_id and
idx.index_id < 2 
INNER JOIN sys.partitions AS p ON p.object_id=CAST(tbl.object_id AS int) 
AND p.index_id=idx.index_id 
WHERE ((tbl.name=N'Transactions' 
AND SCHEMA_NAME(tbl.schema_id)='dbo')) 

commentaires:

la façon dont le studio de gestion de SQL compte les lignes (regardez propriétés de la table, de stockage, le nombre de lignes). Très rapide, mais encore un nombre approximatif de lignes.

méthode 4:

Requête:

SELECT SUM (row_count) 
FROM sys.dm_db_partition_stats 
WHERE object_id=OBJECT_ID('Transactions')    
AND (index_id=0 or index_id=1); 

commentaires:

fonctionnement rapide (mais pas aussi rapide que la méthode 2) et tout aussi important, fiable.

1
répondu Thierry 2018-04-06 14:41:24

sélectionner des lignes à partir de sysindexes où id = Object_ID ('TableName') et indid <2

0
répondu Enzero 2011-11-03 09:00:23

mettez un index sur une colonne. Cela devrait permettre à l'optimiseur d'effectuer une analyse complète de l'index de blocs, au lieu d'un balayage complet de la table. Ça va réduire les coûts de ton IO. Regardez le plan d'exécution avant et après. Mesurez ensuite l'Heure de l'horloge murale dans les deux sens.

0
répondu EvilTeach 2013-06-30 02:05:47

si vous utilisez Oracle, Que diriez-vous de ceci (en supposant que les statistiques de table sont mises à jour):

select <TABLE_NAME>, num_rows, last_analyzed from user_tables

last_analysed indiquera l'heure à laquelle les statistiques ont été recueillies pour la dernière fois.

0
répondu Diogo Ferreira 2015-04-23 15:58:43

Pour Sql server essayez cette

SELECT T.name, 
       I.rows AS [ROWCOUNT] 
FROM   sys.tables AS T 
       INNER JOIN sys.sysindexes AS I 
               ON T.object_id = I.id AND I.indid < 2 
WHERE T.name = 'Your_Table_Name'
ORDER  BY I.rows DESC 
0
répondu Abhishek B Patel 2017-09-22 06:36:34

j'ai eu ce script d'une autre question/réponse de StackOverflow:

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'YourTableNameHere'
  AND s.name = N'dbo'
  AND p.index_id IN (0,1);

mon tableau contient 500 millions d'enregistrements et les retours ci-dessus en moins de 1m. Pendant ce temps,

SELECT COUNT(id) FROM MyTable

prend 39 minutes, 52 secondes!

ils donnent le même nombre exact de lignes (dans mon cas, exactement 519326012).

je ne sais pas si ce serait toujours le cas.

0
répondu JakeJ 2018-05-31 15:09:19

Avec PostgreSQL:

SELECT reltuples AS approximate_row_count FROM pg_class WHERE relname = 'table_name'
0
répondu Dorian 2018-06-30 21:21:35

si vous avez une clé primaire (valeur unique) quelque part sur votre table, vous pouvez utiliser MAX(yourId) pour vous donner essentiellement le nombre de lignes totales. Voici un extrait d'échantillon:

SELECT MAX(yourId)
FROM YourTable
0
répondu Brendan 2018-10-06 06:17:04

peut-être un peu tard, mais cela pourrait aider d'autres pour MSSQL

; avec RecordCount AS (SELECT ROW_NUMBER() OVER (ORDER BY COLONN_NAME ) AS [RowNumber] FROM TABLE_NAME) SELECT MAX (RowNumber) FROM RecordCount

-3
répondu Justus Swanevelder 2013-08-30 14:46:11