SqlServer中的UNION操作符在合併數據時去重的原理以及UNION運算符查詢結果預設排序的問題

来源:http://www.cnblogs.com/wy123/archive/2017/11/23/7884986.html
-Advertisement-
Play Games

本文出處:http://www.cnblogs.com/wy123/p/7884986.html 周圍又有人在討論UNION和UNION ALL,對於UNION和UNION ALL,網上說的最多的就是性能問題(實在不想說出來這句話:UNION ALL比UNION快)其實根本不想炒UNION和UNIO ...


 

本文出處:http://www.cnblogs.com/wy123/p/7884986.html 

 

 

周圍又有人在討論UNION和UNION ALL,對於UNION和UNION ALL,網上說的最多的就是性能問題(實在不想說出來這句話:UNION ALL比UNION快)
其實根本不想炒UNION和UNION ALL這碗剩飯了,
每次看到網上說用這個不用那個,列舉的一條一條的那種文章,只要看到說UNION ALL比UNION性能好的就……

對於合併的結果集,UNION是去重的,UNION ALL是不去重的,去重與不去重是兩個目的,分別由UNION和UNION ALL實現
兩個作用(功能)不同的東西,放一起比性能有什麼意義?
這種問題真的是無聊至極,就好比“足球場上的某個中後衛和某個前腰哪個能力更強”一樣沒有可比性,
他們的作用本身就是不同的,難道說中後衛能力不行,把他撤下來,用一個牛逼的前腰球員替代中後衛,或者是前腰能力不行,撤下他用牛逼的中後衛替代?
這是在功能上的區別,至於性能,我個人認為對比起來沒有任何意義。
如果非要放一起比的話,做同樣的數據合併,
UNION因為要去重,相對UNION ALL來說,(相對)當然會耗費更多的資源(耗費的資源多少跟性能無關,做的事情多,當然需要更多的資源)
但是一定要弄清楚,合併數據的時候,到底要不要去掉重覆數據,這是最終結果對與錯的問題,不是性能問題!

這裡不討論UNION和UNION ALL的性能了,
從另外一個點入手來發起問題
UNION與UNION ALL最大的區別就是UNION會去重,那麼問題就來了,這個去重是怎麼實現的?去重會對查詢的預設順序集產生什麼影響?

 

UNION去重的實現

測試一下UNION運算符去重的實現原理

create table TestUnion1
(    
    
    Id1 INT PRIMARY KEY,
    Id2 tinyint,
    Name varchar(100)
);
create table TestUnion2
(
    Id1 INT PRIMARY KEY,
    Id2 tinyint,
    Name varchar(100)
);

insert into TestUnion1 values (500,9,'aaa')
insert into TestUnion1 values (700,3,'ccc')
insert into TestUnion1 values (200,7,'eee')


insert into TestUnion2 values (300,2,'bbb')
insert into TestUnion2 values (800,8,'ddd')
insert into TestUnion2 values (100,5,'fff')

--TestUnionALL1和TestUnionALL2中相同的數據
insert into TestUnion1 values (600,6,'xxx')
insert into TestUnion2 values (600,6,'xxx')

UNION在去重的過程中,使用的執行計劃是Merge Join,UNION ALL是不去重的,同樣步驟對應的執行計劃是Concatenation

這裡UNION的去重動作是通過merge實現,這裡的merge join並不是表與表之間的merge join
這裡可以看出來,UNION產生的merge與 inner join產生的Merge的作用是有差異的

對於UNION的去重的這一動作,去當然不是說只有merge join一種,這裡只不過是兩個結果的數據都剛好有序才採用merge join來去重罷了

如果查詢欄位的順序的第一個欄位是聚集索引(或者主鍵),,正如上文提到的,UNION的雙方就會以merge的方式區中
如果查詢欄位的順序非聚集索引,UNION的過程是現將兩個結果集合併起來(上文提到的Concatenation),然後再做sort排序去重

  


UNION之後結果集的最終排序結果

UNION之後結果集的最終排序結果跟查詢欄位的順序有關,
如果查詢欄位的順序的第一個欄位是聚集索引(或者主鍵),正如上文提到的,UNION的雙方就會以merge的方式區中
如果查詢欄位的順序的第一個欄位是非聚集索引欄位,UNION的過程是現將兩個結果集合併起來(上文提到的Concatenation),然後再做sort排序去重
如下的實例能說說明這個問題,當查詢欄位的順序發生變化之後,兩者的執行計劃完全不一致。

或者再看一個case,當Name在最前面的時候,最終的結果就是按照name排序。

   可能有人會懷疑是不是數據量太小了,是不是巧合,這裡可以加大測試資料庫,在查詢條件中,讓非聚集索引參與到運算之中

create table TestUnion1
(    
    
    Id1 INT PRIMARY KEY,
    Id2 tinyint,
    Name varchar(100),
    CreateDate datetime
);


create table TestUnion2
(
    Id1 INT PRIMARY KEY,
    Id2 tinyint,
    Name varchar(100),
    CreateDate datetime
);


begin tran
    declare @i int = 0
    while @i<1000000
    begin
        insert into TestUnion1 values (@i,rand()*200,newid(),getdate()-rand()*1000)
        insert into TestUnion2 values (@i,rand()*200,newid(),getdate()-rand()*1000)
        set @i=@i+1
    end
commit


create index idx_CreateDate on TestUnion1(CreateDate)
create index idx_CreateDate on TestUnion2(CreateDate)

參考下圖,一旦查詢結果集不是按照查詢欄位聚集索引排序的話,
比如這裡走的是createDate時間欄位的索引,執行計劃都是先按照普通的方式合併結果集,也即Concatenation
然後在利用Sort(Distinct)的方式排序去重,對於去重的結果的最終的排序,跟查詢結果的第一個欄位有關,且結果總是按照查詢的第一個欄位排序的。

  換一種查詢欄位的順序方式,看一下結果,仍舊是按照查序列的第一個欄位排序的

  UNION運算符在去重的時候,
  如果查詢欄位的第一個欄位是聚集索引,那麼會用merge join的方式合併+去重。
  如果查詢欄位的第一個欄位不是非聚集索引,那麼首先會將兩個(或者多個)結果集進行普通的合併,最後通過Sort Distinct的方式去重。  
  且UNION運算之後的預設排序方式,受查詢欄位前後的方式影響。 

 

總結:

  UNION和UNION ALL的作用是不一樣的,放在一起比性能沒有任何意義,真不想趟這趟渾水。
  合併結果集,需要去重就用UNION,不需要去重就用UNION ALL,如果兩個結果集中沒有重覆的結果集,就用UNION ALL,
  這純粹是需求驅動的,而不是UNION和UNION ALL的性能問題。

 

多撤一句:
曾經大晚上接到一個面試電話,沒有任何開場白,第一句話是“我們電話面試一下可以嗎”,答曰可以,第二句話就是“UNION和UNION ALL的區別是什麼,有沒有性能差異”。
真的不希望再去對UNION和UNION ALL的性能上做討論。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 繼承是一種節省記憶體,復用代碼,使代碼組織更高效的編程思想。在本篇文章中,我將詳細解釋JavaScript中繼承的實現方式,相信通過閱讀本篇文章,你將徹底掌握JavaScript繼承技術,希望你能在工作中找準時機,發揮這一技術的價值。 ...
  • 在學習廖雪峰前輩的JavaScript教程中,遇到了一些需要註意的點,因此作為學習筆記列出來,提醒自己註意! 如果大家有需要,歡迎訪問前輩的博客https://www.liaoxuefeng.com/學習。 在一個對象中綁定函數,我們稱這個函數是這個對象的方法。 在前面的學習中,對象的定義如下: 如 ...
  • 什麼是動作呢?顧名思義它是管理運行環境中所有動作的中樞,通俗點講就是動畫。它也是總個框架核心組件之一,它提供動作的創建、刪除、插入,以及提供對關鍵幀與過渡的諸多操作 ...
  • Bootstrap FileInput中文API整理 這段時間做項目用到bootstrap fileinput插件上傳文件,在用的過程中,網上能查到的api都不是很全,所以想著整理一份比較詳細的文檔,方便自己今後使用,也希望能給大家帶來幫助,如有錯誤,希望大家積極指正。 一、 引入文件 <link ...
  • 效果實現圖 ...
  • [20171124]bbed的使用問題2.txt--//bbed 是探究oracle數據塊的好工具,有時候不用轉儲,直接可以它看oracle內部塊的內部結構.--//在使用中要註意一些問題,昨天又犯類似錯誤,通過例子來說明:SCOTT@book> select rowid,dept.* from d ...
  • 1.索引無法存儲null值 a.單列索引無法儲null值,複合索引無法儲全為null的值。 b.查詢時,採用is null條件時,不能利用到索引,只能全表掃描。 為什麼索引列無法存儲Null值? a.索引是有序的。NULL值進入索引時,無法確定其應該放在哪裡。(將索引列值進行建樹,其中必然涉及到諸多 ...
  • 下載安裝包或者壓縮包 添加db存儲和日誌存儲文件夾 添加服務、配置環境變數、啟動Mongo 本例:安裝路徑:D:Program Files/MongoDB 配置文件的路徑:D:MongoDB 一、安裝,可選自定義安裝路徑 二、建立配置文件 logs文件夾內建立mongodb.log;etc文件夾內建 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...