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


738

誰かが使いすぎているのか、パフォーマンスに顕著な違いがあるのか​​、またはこれが過去の時代からCount(1)引き継がCount(*)れているレガシーの習慣にすぎないのかと疑問に思っているだけですか?

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


7
SQL Serverについては知りませんが、MySQLでは違いはありません。一方、COUNT(列)は異なります
グレッグ

118
違います。COUNT(SomeColumn)は、SomeColumnのnull以外の値を含む行の数のみを返します。COUNT(*)およびCOUNT( 'Foo')は、テーブル内の行の総数を返します。
スティーブブロ


4
すごいスティーブと私はcount(*)対Count(ColumnName)を知らずにTSQLに5年間入りました。ありがとう
Harindaka

3
COUNT(*)vs COUNT(1)vsCOUNT(pk)の回答にも注意してください—どちらが良いですか?COUNT(*)vsCOUNT(column-name)もあります—どちらが正しいですか?。他の重複がある場合もあります。
ジョナサンレフラー2014

回答:


598

違いはありません。

理由:

オンラインの本には「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)それ以外の場合は、Tの各行に<値式>を適用してnull値を削除した結果である単一列のテーブルをTXとします。1つ以上のnull値が削除されると、完了条件が発生します。


73

SQL Serverでは、これらのステートメントは同じ計画を生成します。

世論に反して、Oracleでも同様です。

SYS_GUID() Oracleでは、かなり計算集約的な機能です。

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

このクエリ:

SELECT  COUNT(SYS_GUID())
FROM    t_even

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

ただし、このクエリ:

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

2評価することすらしないので、ほんの数秒間実行されますSYS_GUID()(の*引数であるにもかかわらずCOUNT(*)


SYS_GUID()サブクエリが結果を返すには、少なくとも(つまり、正確に)1回評価する必要があります。
asgs

@asgs:なぜそう思いますか?COUNT(*)の値にどのように依存しますSYS_GUIDか?
Quassnoi、2018年

あなたが尋ねた今、私にはわかりません。私はのために考えたCOUNT(*)サブクエリが1のように行動しなければならないので、実行には、それは、テーブルを必要とします。そうしないとCOUNT(*)、意味のある値を返す方法がわかりません
asgs

1
@asgs:mapメソッドの機能を知っているt_even.map(() => sys_guid()).lengtht_even.lengthすると、これら2つの式がどのように表示されるかを確認できます。また、常に同じ値を返しますか?オラクルのオプティマイザーは、それを見て、map部品を最適化するのに十分スマートです。
Quassnoi、2018年

1
@asgs正確。ただ、マイナーな修正は:lengthかなり依存しませんどのようなだけで、その要素の数に、コレクションが構成されています。この数値がコレクションのメタデータに格納されている場合(これはOracleまたは他のほとんどの最新のRDBMSには当てはまりませんが、古いMySQLのストレージエンジンMyISAMには当てはまります)、COUNT(*)メタデータから値を取得するだけで済みます。
Quassnoi 2018年

65

明らかに、COUNT(*)COUNT(1)なり、常に同じ結果を返します。したがって、一方が他方より遅い場合は、オプティマイザのバグが原因です。両方の形式がクエリで非常に頻繁に使用されるため、DBMSがこのようなバグを修正せずにそのままにしておくことは意味がありません。したがって、両方の形式のパフォーマンスがすべての主要なSQL DBMSで(おそらく)同じであることがわかります。


count(1)がcount(*)より遅い場合は、バグとは見なしません。1を生成し、nullでないものをカウントするようにdbmsに要求した場合、そうです、つまり、レコードカウントになりますが、dbmsが記述したすべてのナンセンスを検出してそれを回避することは期待できません。
Thorsten Kettner、2014

1
まあ、オプティマイザは最適化することを意図しており、カウントについて考慮すべき2つのケースがあります:nullになる可能性のある式、nullになることのない式:count(1)は後者に該当するため、DBMSが実行する必要はありません質問に答えるために1を「生成」します。(ちなみに、見た目の理由から、count(*)以外は一切使用しません。)
Tony Andrews

46

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

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

次に、パフォーマンスに関して、SQL Server(およびSQL Azure)で重要となる2つの側面があります。コンパイル時の作業と実行時の作業です。コンパイル時の作業は、現在の実装ではごくわずかな追加作業です。一部のケースでは*がすべての列に拡張され、バインドと最適化で内部操作の一部が機能するため、出力が1列に削減されます。それが測定可能なテストで表示されることはないと思います。また、自動統計、xeventセッション、クエリストアのオーバーヘッド、トリガーなど、内部で発生する他のすべてのことのノイズで失われる可能性があります。おそらく数千の余分なCPU命令です。そう、count(1)は、コンパイル中に少し少ない作業を行います(通常、これは一度だけ行われ、プランは後続の複数の実行にわたってキャッシュされます)。実行時間については、計画が同じであると仮定すると、測定可能な違いはないはずです。(前の例の1つは違いを示しています-プランが同じである場合、マシンの他の要因が原因である可能性が最も高いです)。

計画がどのように異なる可能性があるかについて。これらが発生する可能性は非常に低いですが、現在のオプティマイザのアーキテクチャで可能性があります。SQL Serverのオプティマイザは検索プログラムとして機能します(コンピュータプログラムがチェスをプレイして、クエリのさまざまな部分に対してさまざまな代替案を検索し、妥当な時間内に最も安い計画を見つけるために代替案をコストをかけます)。この検索には、クエリのコンパイルを妥当な時間内に終了させるための動作にいくつかの制限があります。最も些細なクエリの場合、検索のフェーズがあり、オプティマイザがクエリを実行する可能性があると考えるコストに基づいてクエリのトランシェを処理します。3つの主要な検索フェーズがあり、各フェーズは、以前のどのソリューションよりも安価な計画を見つけようとする、より積極的な(高価な)ヒューリスティックを実行できます。最終的には、各フェーズの終わりに、これまでに見つかったプランを返すか、検索を続けるかを決定する決定プロセスがあります。このプロセスでは、これまでにかかった合計時間と、これまでに見つかった最適なプランの推定コストを使用します。したがって、異なる速度のCPUを使用する異なるマシンでは、計画は前のフェーズでタイムアウトするのと、次の検索フェーズに継続するので、異なる計画を取得することが可能です(まれです)。最後のフェーズのタイムアウトに関連するいくつかの同様のシナリオもあり、マシンのすべてのメモリを消費する非常に高価なクエリでメモリ不足になる可能性があります(通常、64ビットの問題ではありませんが、より大きな懸念事項でした) 32ビットサーバーに戻します)。最終的に、異なるプランを取得した場合、実行時のパフォーマンスは異なります。私はしません

Net-net:実用的な形式では問題にならないため、2つのうちどちらを使用してもかまいません。(正直なところ、このトピックを超えてSQLのパフォーマンスに影響を与えるはるかに大きな要因があります)。

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


2
私の信念は1、同じ拡大を経験しているということです。私はここにperfのテストでこれをベースstackoverflow.com/questions/1597442/... 使用して、クエリのその答えに例を見ても1、列レベルの権限がプレイしているとき、予期せずに失敗する
マーティン・スミス

21

SQL-92標準では、COUNT(*)特に「テーブル式のカーディナリティ」を意味します(ベーステーブル、 `VIEW、派生テーブル、CTEなどである可能性があります)。

COUNT(*)解析が簡単だと思っていたと思います。他の式を使用して(これは、任意の列を参照しない保証するためにパーサーが必要COUNT('a')ここでaリテラルとされるCOUNT(a)場合a、カラムは、異なる結果をもたらすことができるです)。

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

また、特殊なケースSELECT COUNT(*) FROM MyPersistedTable;では、DBMSはテーブルのカーディナリティの統計を保持する可能性が高いと考えられています。

したがって、理由COUNT(1)COUNT(*)意味的に等価であり、I使用COUNT(*)


1
DBA.SEに私の答えからリンクされているSQL-92テキスト:dba.stackexchange.com/questions/2511/...
GBN


12

私はオプティマイザが奇妙なエッジのケースの外で実際の違いがないことを保証することを期待します。

何でもそうですが、実際に伝える唯一の方法は、特定のケースを測定することです。

とは言っても、私はいつも使ってきましたCOUNT(*)


受け入れられた回答によれば、これはMS SQLには当てはまりません-実際には2つの間に違いはありません。
デビッドマンハイム

10

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

SELECT COUNT(*) FROM something 簡単な作業であるレコードをカウントします。

SELECT COUNT(1) FROM something レコードごとに1を取得し、nullでない1をカウントします。これは基本的にレコードをカウントするものであり、さらに複雑です。

いいdbmsは、2番目のステートメントが最初のステートメントと同じ数になることに気づき、不必要な作業を行わないようにそれに応じて再解釈します。そのため、通常、両方のステートメントで同じ実行プランが生成され、同じ時間がかかります。

ただし、読みやすさの点から、最初のステートメントを使用する必要があります。レコードを数えたいので、式ではなくレコードを数えます。COUNT(式)は、何かのnull以外の出現をカウントする場合にのみ使用してください。


8

SQL Server 2012の8 GB RAMハイパーvボックスでクイックテストを実行しました。自分で結果を確認できます。これらのテストを実行している間、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億7,800万行)

最初のクエリ:

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エンジンによるパフォーマンスの差がないと簡単に結論付けることができます。

:両方の実行で、RAMヒットは100%に近い使用率です。両方の実行を開始する前に、SQL Serverサービスを再起動しました。


7
SET STATISTICS TIME ON

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

SQL Server実行時間:
CPU時間= 31ミリ秒、経過時間= 36ミリ秒。

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

SQL Server実行時間:
CPU時間= 46ミリ秒、経過時間= 37ミリ秒。

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


14
これは再現できません。SQL 2008インスタンスで、450万行のテーブルを数える場合でも、数ms以内に結果count(*)count(1)返します。
Jeff Atwood、2010

2
場合によっては、一部のシステムでは、最初に実行されたステートメントが常により速く実行されます...それらが実行される順序をランダム化しましたか?
JosephDoggie 2018年

@JosephDoggieは、そのような測定/統計を取得しながらすべてのクエリを実行する前に、常にSQL Serverサービスを再起動する必要があります。SQL Serverサービスを開始した直後は、すべての実行が完全に独立しているため、クエリの順序は重要ではありません。一方、SQL Serverサービスを再起動せず、エンジンが実行プランのキャッシュを実行する場合、後で実行されるクエリは最初のクエリではなく高速に実行されます。
RBT 2018年

実行時間は、比較を行うときに正確なクエリプランを調べる必要があります。それらが異なる場合(たとえば、ハッシュ集計と並べ替え+ストリーム集計)、結果は比較できません。したがって、ここにデータを追加せずに結論を引き出すことを注意してください。
Conor Cunningham MSFT

3

on Oracleがの単なるエイリアスであることを示す記事があり、その証拠があります。COUNT(1)COUNT(*)

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

「オプティマイザ」と呼ばれるデータベースソフトウェアの一部があり、これは公式ドキュメントで「SQLステートメントを実行する最も効率的な方法を決定する組み込みのデータベースソフトウェア」として定義されています。

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

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

ALTER SESSION特権を持つユーザーを使用して、を配置しtracefile_identifier、オプティマイザのトレースを有効にしてCOUNT(1)、次のように選択を実行できます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つの重要なコメント:20年前、Oracle 7.3より前のOracle COUNT(*)は本当に高速でした。

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

20年前、count(*)はより高速でした

SQL Serverとしての別のデータベースについては、データベースごとに個別に調査する必要があります。

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


1

すべてのRDBMSでは、2つのカウント方法は、それらが生成する結果に関して同等です。パフォーマンスに関しては、SQL Serverでのパフォーマンスの違いは確認していませんが、PostgreSQL 11などのCOUNT(1)一部のRDBMSでは、この投稿にあるように、引数式のnull可能性をチェックするための実装が最適ではないことを指摘する価値があります。

実行すると、100万行で10%のパフォーマンスの違いが見つかりました。

-- Faster
SELECT COUNT(*) FROM t;

-- 10% slower
SELECT COUNT(1) FROM t;

0

COUNT(1)は、COUNT(*)と大きく異なることはありません。NULL可能列をカウントする問題については、これは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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.