Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

SQL Server Count ist langsam

Sehr nahe Annäherung (ohne Berücksichtigung von In-Flight-Transaktionen) wäre:

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);

Dies wird viel, viel schneller als COUNT(*) zurückgegeben , und wenn sich Ihre Tabelle schnell genug ändert, ist sie nicht wirklich weniger genau - wenn sich Ihre Tabelle zwischen dem Start von COUNT (und dem Aufnehmen von Sperren) und dem Zurückgeben (als die Sperren freigegeben wurden und alle wartenden Schreibtransaktionen) geändert hat durften jetzt an den Tisch schreiben), ist es so viel wertvoller? Ich glaube nicht.

Wenn Sie eine Teilmenge der Tabelle haben, die Sie zählen möchten (z. B. WHERE some_column IS NULL ), könnten Sie einen gefilterten Index für diese Spalte erstellen und die where-Klausel auf die eine oder andere Weise strukturieren, je nachdem, ob es sich um die Ausnahme oder die Regel handelt (erstellen Sie also den gefilterten Index für den kleineren Satz). Also einer dieser beiden Indizes:

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;

Dann könnten Sie die Anzahl auf ähnliche Weise mit:

abrufen
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);

Und wenn Sie das Gegenteil wissen wollen, subtrahieren Sie einfach von der ersten Abfrage oben.