web-dev-qa-db-ja.com

カウント(*)とカウント(1) - SQL Server

あなたの誰かがCount(1)の上にCount(*)を使っているのか、それともパフォーマンスに顕著な違いがあるのか​​、あるいはこれが過去の過去から持ち越されてきた単なるレガシー習慣なのか?

(特定のデータベースはSQL Server 2005です。)

669
super9

違いはありません。

理由:

オンライン上の本 は "COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )"と言います

"1"はnull以外の式です。つまり、COUNT(*)と同じです。オプティマイザは、それが何であるかを認識します。

EXISTS (SELECT * ...またはEXISTS (SELECT 1 ...と同じ

例:

SELECT COUNT(1) FROM dbo.tab800krows
SELECT COUNT(1),FKID FROM dbo.tab800krows GROUP BY FKID

SELECT COUNT(*) FROM dbo.tab800krows
SELECT COUNT(*),FKID FROM dbo.tab800krows GROUP BY FKID

同じIO、同じ計画、作品

編集、2011年8月

DBA.SEについての同様の質問

編集、2011年12月

COUNT(*)は、 ANSI-92 で特に言及されています( "Scalar expressions 125"を探してください)

場合:

a)COUNT(*)が指定されている場合、結果はTの基数です。

つまり、ANSI規格では、これが明らかな出血として認識しています。 COUNT(1)はRDBMSベンダーによって最適化されています この迷信の 。それ以外の場合は、ANSIに従って評価されます。

b)それ以外の場合、TXを<value expression>をTの各行に適用し、NULL値を削除した結果である1列のテーブルとします。 1つ以上のNULL値が削除されると、完了条件が発生します。

557
gbn

SQL Serverでは、これらのステートメントは同じ計画になります。

一般的な意見に反して、Oracleでもそうしています。

OracleのSYS_GUID()はかなり計算集約的な関数です。

私のテストデータベースでは、t_even1,000,000行を持つテーブルです。

このクエリ:

SELECT  COUNT(SYS_GUID())
FROM    t_even

関数は、返される各SYS_GUID()を評価して、それがNULLではないことを確認する必要があるため、48秒間実行します。

しかし、このクエリ:

SELECT  COUNT(*)
FROM    (
        SELECT  SYS_GUID()
        FROM    t_even
        )

2SYS_GUID()への引数であるにもかかわらず)COUNT(*)を評価しようとさえしないので、*秒だけ実行します。

70
Quassnoi

明らかに、COUNT(*)とCOUNT(1)は 常に 同じ結果を返します。したがって、一方が他方よりも遅い場合、それは事実上最適化のバグによるものです。どちらの形式もクエリで頻繁に使用されるので、DBMSがそのようなバグを未修正のままにしておくことは意味がありません。したがって、両方の形式のパフォーマンスは、(おそらく)すべての主要なSQL DBMSで同じであることがわかります。

58
Tony Andrews

SQL-92規格では、COUNT(*)は "テーブル式の基数"を意味します(ベーステーブル、 `VIEW、派生テーブル、CTEなど)。

私は、COUNT(*)を解析するのは簡単だと考えました。他の式を使用すると、どの列も参照しないようにパーサーで確認する必要があります(COUNT('a')aはリテラル)、COUNT(a)aは列)は異なる結果になる可能性があります)。

同様に、COUNT(*)は、SQL標準に精通している人間のコーダーによって簡単に選ぶことができます。これは、複数のベンダのSQL製品を扱うときに役立つスキルです。

また、特別なケースSELECT COUNT(*) FROM MyPersistedTable;では、DBMSはテーブルの濃度の統計を保持する可能性が高いと考えます。

したがって、COUNT(1)COUNT(*)は意味的に等価なので、私はCOUNT(*)を使います。

20
onedaywhen

COUNT(*)COUNT(1)は結果とパフォーマンスの点で同じです。

16
Nakul Chaudhary

私は、オプティマイザが奇妙なEdgeのケース以外に本当の違いがないことを保証することを期待するでしょう。

他のものと同様に、伝える唯一の本当の方法はあなたの特定のケースを測定することです。

とは言っても、私はいつもCOUNT(*)を使っています。

12
Richard

私は、8 GBのRAM hyper-vボックスでSQL Server 2012のクイックテストを実行しました。あなたは自分で結果を見ることができます。これらのテストを実行している間、私はSQL Server Management Studio以外のウィンドウアプリケーションを実行していませんでした。

私のテーブルスキーマ:

CREATE TABLE [dbo].[employee](
    [Id] [bigint] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_employee] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

Employeeテーブルの総レコード数:178090131(約1億7800万行)

最初のクエリ:

Set Statistics Time On
Go    
Select Count(*) From Employee
Go    
Set Statistics Time Off
Go

最初のクエリの結果:

 SQL Server parse and compile time: 
 CPU time = 0 ms, elapsed time = 35 ms.

 (1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 10766 ms,  elapsed time = 70265 ms.
 SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

2番目のクエリ:

    Set Statistics Time On
    Go    
    Select Count(1) From Employee
    Go    
    Set Statistics Time Off
    Go

2番目のクエリの結果:

 SQL Server parse and compile time: 
   CPU time = 14 ms, elapsed time = 14 ms.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 11031 ms,  elapsed time = 70182 ms.
 SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

83(= 70265 - 70182)ミリ秒の違いがあることに気付くでしょう。これは、クエリが実行された時点の正確なシステム状態に簡単に起因する可能性があります。また、私は1回実行したので、数回実行して平均化すると、この差はより正確になります。このような巨大なデータセットの場合、違いが100ミリ秒未満になる場合、2つのクエリのパフォーマンスにSQL Server Engineによるパフォーマンスの違いはないと簡単に判断できます。

:RAMは、両方の実行で100%近く使用されています。両方の実行を開始する前に、SQL Serverサービスを再開しました。

8
RBT

この質問が何度も出てくるので、もう1つ答えがあります。ここで「ベストプラクティス」について疑問に思う初心者のための何かを追加したいと思います。

SELECT COUNT(*) FROM somethingはレコードを数えるのは簡単な作業です。

SELECT COUNT(1) FROM somethingはレコードごとに1を取得し、次にnullではない1をカウントします。これは本質的にレコードをカウントするため、より複雑なだけです。

これを言ったこと:良いdbmsは、2番目のステートメントが最初のステートメントと同じ数になることに気付き、それに応じてそれを再解釈します。不要な作業をしないためです。したがって、通常、両方のステートメントは同じ実行計画になり、同じ時間がかかります。

しかし、読みやすさの観点から、あなたは最初のステートメントを使うべきです。レコードを数えたいので、式ではなくレコードを数えます。 COUNT(expression)は、null以外のものを数えたい場合にのみ使用してください。

8
SET STATISTICS TIME ON

select count(1) from MyTable (nolock) -- table containing 1 million records. 

SQL Serverの実行時間
CPU時間= 31 ms、経過時間= 36 ms。

select count(*) from MyTable (nolock) -- table containing 1 million records. 

SQL Serverの実行時間
CPU時間= 46 ms、経過時間= 37 ms。

毎回キャッシュをクリアして、この何百回も実行しました。サーバーの負荷が変わると結果も時々変わりますが、ほとんどの場合count(*)の方がCPU時間が長くなります。

7
Eyal Z.

私はSQL Serverチームに取り組んでいて、うまくいけばこのスレッドのいくつかの点を明確にすることができます(私は以前にそれを見たことがなかったので、エンジニアリングチームが以前にそうしなかったことを残念に思います)。

まず、select count(1) from tableselect count(*) from tableの間に意味的な違いはありません。それらはすべての場合で同じ結果を返します(そうでなければバグです)。他の答えで述べたように、select count(column) from tableは意味的に異なり、count(*)と同じ結果を返すとは限りません。

次に、パフォーマンスに関して、SQL Server(およびSQL Azure)には2つの側面があります。コンパイル時の作業と実行時の作業です。コンパイル時の作業は、現在の実装ではごくわずかな追加作業です。一部のケースでは*がすべての列に拡張され、その後にバインドおよび最適化でいくつかの内部操作がどのように機能するかによって、出力される1列に戻ります。私はそれがどんな測定可能なテストでも現れることを疑い、そしてそれはカバーの下で起こる他のすべてのことのノイズ(例えば自動統計、xeventセッション、クエリストアオーバーヘッド、トリガーなど)で迷子になるでしょう。それはおそらく数千の余分なCPU命令です。そのため、count(1)のコンパイル時の作業量はわずかに少なくなります(通常は1回行われ、計画はその後の複数の実行にわたってキャッシュされます)。実行時間については、計画が同じであれば、測定可能な違いはないはずです。 (前の例の1つは違いを示しています - 計画が同じであれば、それはおそらくマシン上の他の要因によるものです)。

計画がどのように異なる可能性があるかについて。これらが起こる可能性は極めて低いですが、現在のオプティマイザのアーキテクチャでは潜在的に可能です。 SQL Serverのオプティマイザは検索プログラムとして機能します(コンピュータプログラムがさまざまな選択肢をクエリのさまざまな部分で検索して、妥当な時間内に最も安い計画を見つけるためのコストを見積もる)。この検索で​​は、クエリのコンパイルを合理的な時間内に終了させるための動作方法にいくつかの制限があります。最も些細なことを超えたクエリについては、検索のフェーズがあり、オプティマイザがクエリが潜在的に実行することであると考えるコストに基づいてクエリのトランシェを扱います。 3つの主な検索フェーズがあり、各フェーズは以前のどのソリューションよりも安いプランを見つけようとしてより積極的な(高価な)ヒューリスティックを実行することができます。最終的には、各フェーズの終わりに、それまでに見つかった計画を返すべきか、それとも検索を続けるべきかを判断しようとする決定プロセスがあります。このプロセスでは、これまでに要した合計時間と、これまでに見つかった最良の計画の見積もりコストを使用します。したがって、異なる速度のCPUを搭載した異なるマシンでは(計画はめったにありませんが)計画の早い段階でタイムアウトし、次の検索段階に進むことで異なる計画を立てることができます。最後のフェーズのタイムアウトや、マシン上のすべてのメモリを消費する非常に非常に高価なクエリでのメモリ不足の可能性に関する同様のシナリオもいくつかあります(通常は64ビットの問題ではありませんが、大きな懸念事項でした)。 32ビットサーバーに戻します。最終的に、別の計画を立てた場合、実行時のパフォーマンスは異なります。私は、コンパイル時間の違いがこれらの状況のいずれかが起こることにつながる可能性があるとは限りません。

Net-net:これは問題にならないので、実用的な方法で使用してください。 (正直なところ、このトピックを超えてSQLのパフォーマンスに影響を与える、はるかに大きな要因があります)。

これが役に立つことを願っています。私はオプティマイザがどのように機能するかについての本の章を書きましたが、それをここに投稿するのが適切かどうかわかりません(それでも私はまだ少額のロイヤリティを得ていると信じています)。だから、私が英国のSQLBitsで行った、オプティマイザがどのように高レベルで機能するかについての講演へのリンクを投稿する代わりに、必要に応じてもう少し詳細に検索のさまざまなメインフェーズを見ることができます。それについて学ぶために。これがビデオリンクです: https://sqlbits.com/Sessions/Event6/inside_the_sql_server_query_optimizer

articleOracleCOUNT(1)COUNT(*)の単なるエイリアスであることを示しています。これについてはproofが付いています。

私はいくつかの部分を引用します:

「The Optimizer」と呼ばれるデータベースソフトウェアの一部があります。これは公式文書では「SQL文を実行するための最も効率的な方法を決定する組み込みデータベースソフトウェア」と定義されています。

オプティマイザのコンポーネントの1つは「トランスフォーマ」と呼ばれ、その役割は、元のSQL文をより効率的な意味的に同等のSQL文に書き換えることが有利かどうかを判断することです。

COUNT(1)を使用してクエリを作成したときにオプティマイザが何をするのかを確認しますか?

ALTER SESSION権限を持つユーザーでは、tracefile_identifierを入力してオプティマイザトレースを有効にし、COUNT(1) selectを実行することができます(例:SELECT /* test-1 */ COUNT(1) FROM employees;)。

その後、トレースファイルをローカライズする必要があります。これはSELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Diag Trace';で実行できます。ファイルの後に、あなたは見つけるでしょう:

SELECT COUNT(*) “COUNT(1)” FROM “COURSE”.”EMPLOYEES” “EMPLOYEES”

ご覧のとおり、これはCOUNT(*)の単なるエイリアスです。

もう1つの重要なコメント:OracleのCOUNT(*)は、Oracle 7.3より前の、本当に速い 20年前 です。

Oracleはmythicステートメントを自動調整することを好むため、Count(1)は7.3以降count(*)に書き直されました。以前のOracle7では、DETERMINISTICとNON-DETERMINISTICが存在する前に、Oracleは関数として各行に対して(1)を評価する必要がありました。

だから20年前、count(*)はもっと速い

Sql Serverのような他のデータベースの場合は、データベースごとに個別に調査する必要があります。

この質問はSql Serverに固有のものであることを私は知っていますが、データベースについての言及なしに同じ主題に関するSOに関する他の質問は閉じられ、この回答からの重複としてマークされました。

3
Dherik

COUNT(1)は、COUNT(*)と実質的に違いはありません。 NULL可能COLUMNのCOUNTの問題に関しては、COUNT(*)とCOUNT(<some col>)の違いをデモするのは簡単です。

USE tempdb;
GO

IF OBJECT_ID( N'dbo.Blitzen', N'U') IS NOT NULL DROP TABLE dbo.Blitzen;
GO

CREATE TABLE dbo.Blitzen (ID INT NULL, Somelala CHAR(1) NULL);

INSERT dbo.Blitzen SELECT 1, 'A';
INSERT dbo.Blitzen SELECT NULL, NULL;
INSERT dbo.Blitzen SELECT NULL, 'A';
INSERT dbo.Blitzen SELECT 1, NULL;

SELECT COUNT(*), COUNT(1), COUNT(ID), COUNT(Somelala) FROM dbo.Blitzen;
GO

DROP TABLE dbo.Blitzen;
GO
0
Graeme