TOP 1比不加TOP慢的疑惑
问题描述: 有一个查询如下,去掉 TOP 1 的时候,很快就出来结果了,但加上 TOP 1 的时候,一般要 2~3 秒才出数据,何解? SELECT TOP 1 ??? A . INVNO FROM A , B WHERE A . Item = B . ItemNumber ??? AND B . OwnerCompanyCode IS NOT NULL ? 问题原因分
问题描述:
有一个查询如下,去掉 TOP 1 的时候,很快就出来结果了,但加上 TOP 1 的时候,一般要 2~3 秒才出数据,何解?
SELECT TOP 1
??? A. INVNO
FROM A, B
WHERE A. Item = B. ItemNumber
??? AND B. OwnerCompanyCode IS NOT NULL
?
问题原因分析:
在使用 TOP 1 的时候, SQL Server 会尽力先找出这条 TOP 1 的记录,这就导致它采用了与不加 TOP 时不一致的扫描算法, SQL Server 查询优化器始终认为,应该可以比较快的找到匹配的第 1 条记录,所以一般是使用嵌套循环的联接,则不加 TOP 1 时, SQL Server 会根据结构和数据的统计信息决策出联接策略。 嵌套循环一般适用于联系的两个表,一个表的数据较大,而另一个表的数据较小的情况 ,如果查询匹配的值出现在扫描的前端,则在取 TOP 1 的情况下,是符合嵌套循环联系的使用条件的,但当匹配的数据出现在扫描的后端,或者是基本上没有匹配的数据时,则嵌套循环要扫描完成两个大表,这显然是不适宜的,也正是因为这种情况,导致了 TOP 1 比不加 TOP 1 的效率慢很多
?
关于此问题的模拟环境:
USE tempdb
GO
?
SET NOCOUNT ON
--======================================
-- 创建测试环境
--======================================
RAISERROR ( ' 创建测试环境 ' , 10, 1) WITH NOWAIT
-- Table A
CREATE TABLE [dbo]. A(
??? [TranNumber] [int] IDENTITY ( 1, 1) NOT NULL,
??? [INVNO] [char]( 8) NOT NULL,
??? [ITEM] [char]( 15) NULL DEFAULT ( '' ),
??? PRIMARY KEY ( [TranNumber])
)
?
CREATE INDEX [indexONinvno] ON [dbo]. A( [INVNO])
CREATE INDEX [indexOnitem] ON [dbo]. A ( [ITEM])
CREATE INDEX [indexONiteminnvo] ON [dbo]. A( [INVNO], [ITEM])
GO
?
-- Table B
CREATE TABLE [dbo]. B(
??? [ItemNumber] [char]( 15) NOT NULL DEFAULT ( '' ),
??? [CompanyCode] [char] ( 4) NOT NULL,
??? [OwnerCompanyCode] [char]( 4) NULL,
??? PRIMARY KEY ( [ItemNumber], [CompanyCode])
)
?
CREATE INDEX [ItemNumber] ON [dbo]. B( [ItemNumber])
CREATE INDEX [CompanyCode] ON [dbo]. B( [CompanyCode])
CREATE INDEX [OwnerCompanyCode] ON [dbo]. B( [OwnerCompanyCode])
GO
?
--======================================
-- 生成测试数据
--======================================
RAISERROR ( ' 生成测试数据 ' , 10, 1) WITH NOWAIT
INSERT [dbo]. A( [INVNO], [ITEM])
SELECT LEFT( NEWID (), 8), RIGHT( NEWID (), 15)
FROM syscolumns A, syscolumns B
?
INSERT [dbo]. B( [ItemNumber], [CompanyCode], [OwnerCompanyCode])
SELECT RIGHT( NEWID (), 15), LEFT( NEWID (), 4), LEFT( NEWID (), 4)
FROM syscolumns A, syscolumns B
GO
?
速度测试脚本:
--======================================
-- 进行查询测试
--======================================
RAISERROR ( ' 进行查询测试 ' , 10, 1) WITH NOWAIT
DECLARE @dt DATETIME , @id int , @loop int
DECLARE @ TABLE (
??? id int IDENTITY ,
??? [TOP 1] int ,
??? [WITHOUT TOP] int )
?
SET @loop = 0
WHILE @loop 10
BEGIN
??? SET @loop = @loop + 1
??? RAISERROR ( 'test %d' , 10, 1, @loop) WITH NOWAIT
??? SET @dt = GETDATE ()
??????? SELECT TOP 1
??????????? A. INVNO
??????? FROM A, B
??????? WHERE A. Item = B. ItemNumber
??????????? AND B. OwnerCompanyCode IS NOT NULL
??? INSERT @([TOP 1]) VALUES ( DATEDIFF ( ms, @dt, GETDATE ()))
??? SELECT @id = SCOPE_IDENTITY (), @dt = GETDATE ()
??????? SELECT --TOP 1
??????????? A. INVNO
??????? FROM A, B
??????? WHERE A. Item = B. ItemNumber
??????????? AND B. OwnerCompanyCode IS NOT NULL
??? UPDATE @ SET [WITHOUT TOP] = DATEDIFF ( ms, @dt, GETDATE ())
??? WHERE id = @id
END
SELECT * FROM @
UNION ALL
SELECT NULL, SUM ( [TOP 1]), SUM ( [WITHOUT TOP]) FROM @
GO
?
测试数据的变更脚本:
DECLARE @value char ( 15), @value1 char ( 15)
SELECT
??? @value = LEFT( NEWID (), 15),
??? @value1 = LEFT( NEWID (), 15)
?
UPDATE A
SET Item = @value
FROM A
??? INNER JOIN(
??????? SELECT TOP 1
??????????? [TranNumber]
??????? FROM (
??????????? SELECT TOP 20 PERCENT
??????????????? [TranNumber]
??????????? FROM A
??????????? ORDER BY [TranNumber]
??????? ) AA
??????? ORDER BY [TranNumber] DESC
??? ) B
??????? ON A. [TranNumber] = B. [TranNumber]
?
UPDATE B
SET ItemNumber = @value
FROM B
??? INNER JOIN(
??? ??? SELECT TOP 1
??????????? [ItemNumber], [CompanyCode]
??????? FROM (
??????????? SELECT TOP 20 PERCENT
??????????????? [ItemNumber], [CompanyCode]
??????????? FROM B
??????????? ORDER BY [ItemNumber], [CompanyCode]
??????? ) BB
??????? ORDER BY [ItemNumber] DESC , [CompanyCode] DESC
??? ) B1
??????? ON B. [ItemNumber] = B1. [ItemNumber]
??????????? AND B. [CompanyCode] = B1. [CompanyCode]
GO
?
测试说明:
1.?? 在刚建立好测试环境的时候,是没有任何匹配项的,这时候, TOP 1 会扫描两个表的所有数据,运行“速度测试脚本 ”可以看到此时有无 TOP 1 的效率差异: TOP 1 明显比不加 TOP 慢
2.?? 修改“测试数据的变更脚本 ”中,红色的 20 , 让匹配的数据出现在扫描的顶端、中间和尾端,分别使用 “速度测试脚本 ”测试,可以看到,匹配的值靠近扫描的前端的时候, TOP 1 比不加 TOP 快,随着匹配数据很后端的推移,这种效率差异会越来越小,到后面就变成 TOP 1 比不加 TOP 1 慢。
注意: 每次变更数据,并且完成“速度测试脚本 ”测试后,需要修改“测试数据的变更脚本 ”中,红色的 @ value 为 @value1 ,让刚才设置匹配的数据再变回为不匹配
?
附:联接的几种方式
1.???? 嵌套循环联接
嵌套循环联接也称为 “ 嵌套迭代 ” ,它将一个联接输入用作外部输入表(显示为图形执行计划中的顶端输入),将另一个联接输入用作内部(底端)输入表。外部循环逐行处理外部输入表。内部循环会针对每个外部行执行,在内部输入表中搜索匹配行。
最简单的情况是,搜索时扫描整个表或索引;这称为 “ 单纯嵌套循环联接 ” 。如果搜索时使用索引,则称为 “ 索引嵌套循环联接 ” 。如果将索引生成为查询计划的一部分(并在查询完成后立即将索引破坏),则称为 “ 临时索引嵌套循环联接 ” 。查询优化器考虑了所有这些不同情况。
如果外部输入较小而内部输入较大且预先创建了索引,则嵌套循环联接尤其有效。在许多小事务中(如那些只影响较小的一组行的事务),索引嵌套循环联接优于合并联接和哈希联接。但在大型查询中,嵌套循环联接通常不是最佳选择。
?
2.???? 合并联接
合并联接要求两个输入都在合并列上排序,而合并列由联接谓词的等效 (ON) 子句定义。通常,查询优化器扫描索引(如果在适当的一组列上存在索引),或在合并联接的下面放一个排序运算符。在极少数情况下,虽然可能有多个等效子句,但只用其中一些可用的等效子句获得合并列。
由于每个输入都已排序,因此 Merge Join 运算符将从每个输入获取一行并将其进行比较。例如,对于内联接操作,如果行相等则返回。如果行不相等,则废弃值较小的行并从该输入获得另一行。这一过程将重复进行,直到处理完所有的行为止。
合并联接操作可以是常规操作,也可以是多对多操作。多对多合并联接使用临时表存储行。如果每个输入中有重复值,则在处理其中一个输入中的每个重复项时,另一个输入必须重绕到重复项的开始位置。
如果存在驻留谓词,则所有满足合并谓词的行都将对该驻留谓词取值,而只返回那些满足该驻留谓词的行。
合并联接本身的速度很快,但如果需要排序操作,选择合并联接就会非常费时。然而,如果数据量很大且能够从现有 B 树索引中获得预排序的所需数据,则合并联接通常是最快的可用联接算法。
?
3.???? 哈希联接
哈希联接有两种输入:生成输入和探测输入。查询优化器指派这些角色,使两个输入中较小的那个作为生成输入。
哈希联接用于多种设置匹配操作:内部联接;左外部联接、右外部联接和完全外部联接;左半联接和右半联接;交集;联合和差异。此外,哈希联接的某种变形可以进行重复删除和分组,例如 SUM(salary) GROUP BY department 。这些修改对生成和探测角色只使用一个输入。
以下几节介绍了不同类型的哈希联接:内存中的哈希联接、 Grace 哈希联接和递归哈希联接。
内存中的哈希联接
哈 希联接先扫描或计算整个生成输入,然后在内存中生成哈希表。根据计算得出的哈希键的哈希值,将每行插入哈希存储桶。如果整个生成输入小于可用内存,则可以 将所有行都插入哈希表中。生成阶段之后是探测阶段。一次一行地对整个探测输入进行扫描或计算,并为每个探测行计算哈希键的值,扫描相应的哈希存储桶并生成 匹配项。
Grace 哈希联接
如果生成输入大于内存,哈希联接将分为几步进行。这称为 “Grace 哈希联接 ” 。 每一步都分为生成阶段和探测阶段。首先,消耗整个生成和探测输入并将其分区(使用哈希键上的哈希函数)为多个文件。对哈希键使用哈希函数可以保证任意两个 联接记录一定位于相同的文件对中。因此,联接两个大输入的任务简化为相同任务的多个较小的实例。然后将哈希联接应用于每对分区文件。
递归哈希联接
如果生成输入非常大,以至于标准外部合并的输入需要多个合并级别,则需要多个分区步骤和多个分区级别。如果只有某些分区较大,则只需对那些分区使用附加的分区步骤。为了使所有分区步骤尽可能快,将使用大的异步 I/O 操作以便单个线程就能使多个磁盘驱动器繁忙工作。
border: 1pt solid #dedfef; padding: 0cm |

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









12306 チケット予約アプリの最新バージョンをダウンロードします。誰もが非常に満足している旅行チケット購入ソフトウェアです。行きたい場所に行くのに非常に便利です。ソフトウェアには多くのチケット ソースが提供されています。本物のチケットを渡すだけで済みます。 - 氏名認証によるオンラインチケット購入 全ユーザー 旅行券や航空券を簡単に購入でき、さまざまな割引が受けられます。また、チケットを入手するための事前予約も開始できます。ホテルや特別な車の送迎も予約できます。これを使用すると、ワンクリックで行きたい場所に行き、チケットを購入できます。旅行がより簡単で便利になり、すべての人に旅行体験を提供します編集者はオンラインで詳細を説明するようになり、12306 人のユーザーに過去のチケット購入記録を表示する方法が提供されます。 1. Railway 12306 を開き、右下隅の [My] をクリックして、[My Order] をクリックします。 2. 注文ページで [Paid] をクリックします。 3. 有料ページにて

Xuexin.com で私の学歴を確認するにはどうすればよいですか? Xuexin.com で学歴を確認できますが、多くのユーザーは Xuexin.com で学歴を確認する方法を知りません。次に、エディターが Xuexin.com で学歴を確認する方法に関するグラフィック チュートリアルを提供します。興味のあるユーザーはぜひ見に来てください! Xuexin.com の使用方法チュートリアル: Xuexin.com で学歴を確認する方法 1. Xuexin.com の入り口: https://www.chsi.com.cn/ 2. Web サイトのクエリ: ステップ 1: Xuexin.com のアドレスをクリックします。上記をクリックしてホームページに入ります [教育クエリ]をクリックします; ステップ2: 最新のWebページで下図の矢印に示すように[クエリ]をクリックします; ステップ3: 新しいページで[学術単位ファイルにログイン]をクリックします; ステップ4: ログインページで情報を入力し、[ログイン]をクリックします。

1. 透かしの入った WPS ドキュメントを開き、[挿入] メニューの [透かし] をクリックすると、次のような効果が得られます: 2. 次に、[ドキュメント内の透かしを削除] をクリックします。 3. このようにして、文書の透かしが削除されます。

MySQL と PL/SQL は 2 つの異なるデータベース管理システムであり、それぞれリレーショナル データベースと手続き型言語の特性を表しています。この記事では、具体的なコード例を示しながら、MySQL と PL/SQL の類似点と相違点を比較します。 MySQL は、構造化照会言語 (SQL) を使用してデータベースを管理および操作する、一般的なリレーショナル データベース管理システムです。 PL/SQL は Oracle データベースに固有の手続き型言語であり、ストアド プロシージャ、トリガー、関数などのデータベース オブジェクトを記述するために使用されます。同じ

Apple の携帯電話を使用してアクティベーション日を確認する場合、携帯電話のシリアル番号から確認するのが最善の方法ですが、Apple の公式 Web サイトにアクセスし、コンピュータに接続して 3 番目のバージョンをダウンロードすることでも確認できます。 -party ソフトウェアを使用して確認します。 Apple 携帯電話のアクティベーション日を確認する方法 回答: シリアル番号のクエリ、Apple 公式 Web サイトのクエリ、コンピュータのクエリ、サードパーティ ソフトウェアのクエリ 1. ユーザーにとって最善の方法は、自分の携帯電話のシリアル番号を知ることです。シリアル番号を確認するには、[設定]、[一般]、[このマシンについて] を開きます。 2. シリアル番号を使用すると、携帯電話のアクティベーション日を知るだけでなく、携帯電話のバージョン、携帯電話の製造元、携帯電話の工場出荷日などを確認することもできます。 3. ユーザーは Apple の公式 Web サイトにアクセスしてテクニカル サポートを見つけ、ページの下部にあるサービスと修理の欄を見つけて、そこで iPhone のアクティベーション情報を確認します。 4. ユーザー

タイトル: Oracle を使用してテーブルがロックされているかどうかをクエリする方法Oracle データベースでは、テーブル ロックとは、トランザクションがテーブルに対して書き込み操作を実行しているときに、他のトランザクションがテーブルに対して書き込み操作を実行したり、テーブルに構造変更 (列の追加、行の削除など) を加えたりするときにブロックされることを意味します。 、など)。実際の開発プロセスでは、トラブルシューティングを改善し、関連する問題に対処するために、テーブルがロックされているかどうかをクエリする必要があることがよくあります。この記事では、Oracle ステートメントを使用してテーブルがロックされているかどうかをクエリする方法と、具体的なコード例を紹介します。テーブルがロックされているかどうかを確認するには、

フォーラムはインターネット上で最も一般的な Web サイト形式の 1 つで、ユーザーに情報を共有し、交換し、議論するためのプラットフォームを提供します。 Discuz は一般的に使用されているフォーラム プログラムであり、多くのウェブマスターはすでによく知っていると思います。 Discuz フォーラムの開発および管理中に、分析または処理のためにデータベース内のデータをクエリすることが必要になることがよくあります。この記事では、Discuz データベースの場所をクエリするためのヒントをいくつか紹介し、具体的なコード例を示します。まず、Discuz のデータベース構造を理解する必要があります。

Linux SSH サービスの開始に失敗する場合の一般的な問題の分析 Linux システムでは、SSH (SecureShell) サービスは安全なリモート アクセスと管理を実現できる一般的に使用されるリモート管理ツールです。ただし、SSH サービスの開始時にさまざまな問題が発生する場合があります。この記事では、SSH サービスの一般的な起動エラーを分析し、対応する解決策と具体的なコード例を提供します。 1. SSH サービスがインストールされていない システムに SSH サービスがインストールされていない場合、当然ながら SSH サービスは起動しません。
