如何寫出高效能的sql
首先要搞清楚什麼叫執行計劃?
執行計劃是資料庫根據SQL語句和相關表格的統計資訊所做的查詢方案,這個方案是由查詢最佳化器自動分析產生的,例如一條SQL語句如果用來從一個10萬筆記錄的表中查1筆記錄,那查詢優化器會選擇「索引查找」方式,如果表格進行了歸檔,目前只剩下5000筆記錄了,那查詢優化器就會改變方案,採用「全表掃描”方式。
可見,執行計劃並不是固定的,它是「個人化」的。產生一個正確的「執行計劃」有兩點很重要:
SQL語句是否清楚地告訴查詢最佳化器它想做什麼?
查詢優化器得到的資料庫統計資料是否是最新的、正確的?
推薦課程:MySQL教學。
統一SQL語句的寫法
#對於以下兩句SQL語句,程式設計師認為是相同的,資料庫查詢最佳化器認為是不同的。
select*from dual select*From dual
其實就是大小寫不同,查詢分析器就認為是兩句不同的SQL語句,必須進行兩次解析。產生2個執行計劃。所以身為程式設計師,應該要保證相同的查詢語句在任何地方都一致,多一個空格都不行!
不要把SQL語句寫得太複雜
我常常看到,從資料庫中捕捉到的一條SQL語句印出來有2張A4紙這麼長。一般來說這麼複雜的語句通常都是有問題的。我拿著這2頁長的SQL語句去請教原作者,結果他說時間太長,他一時也看不懂了。可想而知,連原作者都有可能看糊塗的SQL語句,資料庫也一樣會看糊塗。
一般,將一個Select語句的結果當作子集,然後從該子集中再進行查詢,這種一層嵌套語句還是比較常見的,但是根據經驗,超過3層嵌套,查詢優化器就很容易給出錯誤的執行計劃。因為它被繞暈了。像這種類似人工智慧的東西,終究比人的分辨力要差些,如果人都看暈了,我可以保證資料庫也會暈的。
另外,執行計劃是可以重複使用的,越簡單的SQL語句被重複使用的可能性越高。而複雜的SQL語句只要有一個字元改變就必須重新解析,然後再把這一大堆垃圾塞在記憶體裡。可想而知,資料庫的效率會何等低下。
使用「暫存表」暫存中間結果
#簡化SQL語句的重要方法就是採用暫存表暫存中間結果,但是,臨時表的好處遠遠不止這些,將臨時結果暫存在臨時表,後面的查詢就在tempdb中了,這可以避免程序中多次掃描主表,也大大減少了程序執行中“共享鎖”阻塞“更新鎖”,減少了阻塞,提高了並發性能。
OLTP系統SQL語句必須採用綁定變數
select*from orderheader where changetime >'2010-10-20 00:00:01' select*from orderheader where changetime >'2010-09-22 00:00:01'
以上兩句語句,查詢最佳化器認為是不同的SQL語句,需要解析兩次。如果採用綁定變數
select*from orderheader where changetime >@chgtime
@chgtime變數可以傳入任何值,這樣大量的類似查詢可以重複使用該執行計劃了,這可以大幅降低資料庫解析SQL語句的負擔。一次解析,多次重複使用,是提高資料庫效率的原則。
綁定變數窺測
事物都存在兩面性,綁定變數對大多數OLTP處理是適用的,但是也有例外。例如where條件中的欄位是「傾斜欄位」的時候。
「傾斜字段」指該列中的絕大多數的值都是相同的,例如一張人口調查表,其中「民族」這列,90%以上都是漢族。那麼如果一個SQL語句要查詢30歲的漢族人口有多少,那麼「民族」這列必然要放在where條件裡。這時候如果採用綁定變數@nation會存在很大問題。
試想如果@nation傳入的第一個值是“漢族”,那麼整個執行計劃必然會選擇表掃描。然後,第二個值傳入的是“布依族”,按理說“布依族”佔的比例可能只有萬分之一,應該採用索引查找。但是,由於重用了第一次解析的「漢族」的那個執行計劃,那麼第二次也將採用表格掃描方式。這個問題就是著名的“綁定變數窺測”,建議對於“傾斜欄位”不要採用綁定變數。
只在必要的情況下才使用begin tran
SQL Server中一句SQL語句預設就是一個事務,在該語句執行完成後也是預設commit的。其實,這就是begin tran的一個最小化的形式,好比在每句語句開頭隱含了一個begin tran,結束時隱含了一個commit。
有些情況下,我們需要明確聲明begin tran,例如做「插、刪、改」操作需要同時修改幾個表,要求要嘛幾個表都修改成功,要嘛都不成功。 begin tran 可以起到這樣的作用,它可以把若干SQL語句套在一起執行,最後再一起commit。好處是保證了數據的一致性,但任何事情都不是完美無缺的。 Begin tran付出的代價是在提交之前,所有SQL語句鎖住的資源都不能釋放,直到commit掉。
可見,如果Begin tran套住的SQL語句太多,那資料庫的效能就糟了。在該大事務提交之前,必然會阻塞別的語句,造成block很多。
Begin tran所使用的原則是,在保證資料一致性的前提下,begin tran 套住的SQL語句越少越好!有些情況下可以採用觸發器同步數據,不一定要用begin tran。
一些SQL查詢語句應加上nolock
在SQL語句中加上nolock是提高SQL Server並發效能的重要手段,在oracle中並不需要這樣做,因為oracle的結構更為合理,有undo表空間保存“數據前影”,該數據如果在修改中還未commit,那麼你讀到的是它修改之前的副本,該副本放在undo表空間中。這樣,oracle的讀、寫可以做到互不影響,這也是oracle 廣受稱讚的地方。 SQL Server 的讀取、寫入是會互相阻塞的,為了提高並發效能,對於一些查詢,可以加上nolock,這樣讀的時候可以允許寫,但缺點是可能讀到未提交的髒資料。使用 nolock有3個原則。
(1) 查詢的結果用於「插、刪、改」的不能加上nolock !
(2) 查詢的表格屬於頻繁發生頁分裂的,慎用nolock !
(3) 使用臨時表一樣可以保存“資料前影”,起到類似oracle的undo表空間的功能,
能採用臨時表提高並發性能的,不要用nolock 。
聚集索引沒有建在表的順序欄位上,該表容易發生頁分裂
例如訂單表,有訂單編號orderid,也有客戶編號contactid,那麼聚集索引應該加在哪個欄位上呢?對於該表,訂單編號是順序添加的,如果在orderid上加聚集索引,新增的行都是添加在末尾,這樣不容易經常產生頁分裂。然而,由於大多數查詢都是根據客戶編號來檢查的,因此,將聚集索引加在contactid上才有意義。而contactid對於訂單表而言,並非順序欄位。
例如「張三」的「contactid」是001,那麼「張三」的訂單資訊必須都放在這張表的第一個資料頁上,如果今天「張三」新下了一個訂單,那該訂單資訊不能放在表格的最後一頁,而是第一頁!如果第一頁放滿了呢?很抱歉,表格所有資料都要往後移動為這條記錄騰地方。
SQL Server的索引和Oracle的索引是不同的,SQL Server的聚集索引其實是對資料表依照聚集索引欄位的順序進行了排序,相當於oracle的索引組織表。 SQL Server的聚集索引就是表格本身的一種組織形式,所以它的效率是非常高的。也因為此,插入一筆記錄,它的位置不是隨便放的,而是要按照順序放在該放的資料頁,如果那個資料頁沒有空間了,就引起了頁分裂。所以很顯然,聚集索引沒有建在表的順序欄位上,該表容易發生頁分裂。
曾經碰到過一個情況,一位哥們的某張表重建索引後,插入的效率大幅下降了。估計情況大概是這樣的。該表的聚集索引可能沒有建在表的順序欄位上,該表經常被歸檔,所以該表的資料是以一種稀疏狀態存在的。例如張三下過20張訂單,而最近3個月的訂單只有5張,歸檔策略是保留3個月數據,那麼張三過去的15張訂單已經被歸檔,留下15個空位,可以在insert發生時重新被利用。在這種情況下由於有空位可以利用,就不會發生頁分裂。但是查詢效能會比較低,因為查詢時必須掃描那些沒有資料的空位。
重建聚集索引後情況改變了,因為重建聚集索引就是把表中的資料重新排列一遍,原來的空位沒有了,而頁的填充率又很高,插入資料經常要發生頁分裂,所以性能大幅下降。
對於聚集索引沒有建在順序欄位上的資料表,是否要給與比較低的頁填率?是否要避免重建聚集索引?是一個值得考慮的問題!
加上nolock後查詢常發生頁分割的表,容易產生跳讀或重複讀取
#加nolock后可以在“插、删、改”的同时进行查询,但是由于同时发生“插、删、改”,在某些情况下,一旦该数据页满了,那么页分裂不可避免,而此时nolock的查询正在发生,比如在第100页已经读过的记录,可能会因为页分裂而分到第101页,这有可能使得nolock查询在读101页时重复读到该条数据,产生“重复读”。同理,如果在100页上的数据还没被读到就分到99页去了,那nolock查询有可能会漏过该记录,产生“跳读”。
上面提到的哥们,在加了nolock后一些操作出现报错,估计有可能因为nolock查询产生了重复读,2条相同的记录去插入别的表,当然会发生主键冲突。
使用like进行模糊查询时应注意
有的时候会需要进行一些模糊查询比如
select*from contact where username like ‘%yue%’
关键词%yue%,由于yue前面用到了“%”,因此该查询必然走全表扫描,除非必要,否则不要在关键词前加%,
数据类型的隐式转换对查询效率的影响
sql server2000的数据库,我们的程序在提交sql语句的时候,没有使用强类型提交这个字段的值,由sql server 2000自动转换数据类型,会导致传入的参数与主键字段类型不一致,这个时候sql server 2000可能就会使用全表扫描。Sql2005上没有发现这种问题,但是还是应该注意一下。
以上是如何寫出高效能的sql的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

MySQL是一種開源的關係型數據庫管理系統,主要用於快速、可靠地存儲和檢索數據。其工作原理包括客戶端請求、查詢解析、執行查詢和返回結果。使用示例包括創建表、插入和查詢數據,以及高級功能如JOIN操作。常見錯誤涉及SQL語法、數據類型和權限問題,優化建議包括使用索引、優化查詢和分錶分區。

Apache 連接數據庫需要以下步驟:安裝數據庫驅動程序。配置 web.xml 文件以創建連接池。創建 JDBC 數據源,指定連接設置。從 Java 代碼中使用 JDBC API 訪問數據庫,包括獲取連接、創建語句、綁定參數、執行查詢或更新以及處理結果。

MySQL在數據庫和編程中的地位非常重要,它是一個開源的關係型數據庫管理系統,廣泛應用於各種應用場景。 1)MySQL提供高效的數據存儲、組織和檢索功能,支持Web、移動和企業級系統。 2)它使用客戶端-服務器架構,支持多種存儲引擎和索引優化。 3)基本用法包括創建表和插入數據,高級用法涉及多表JOIN和復雜查詢。 4)常見問題如SQL語法錯誤和性能問題可以通過EXPLAIN命令和慢查詢日誌調試。 5)性能優化方法包括合理使用索引、優化查詢和使用緩存,最佳實踐包括使用事務和PreparedStatemen

選擇MySQL的原因是其性能、可靠性、易用性和社區支持。 1.MySQL提供高效的數據存儲和檢索功能,支持多種數據類型和高級查詢操作。 2.採用客戶端-服務器架構和多種存儲引擎,支持事務和查詢優化。 3.易於使用,支持多種操作系統和編程語言。 4.擁有強大的社區支持,提供豐富的資源和解決方案。

MySQL在Web應用中的主要作用是存儲和管理數據。 1.MySQL高效處理用戶信息、產品目錄和交易記錄等數據。 2.通過SQL查詢,開發者能從數據庫提取信息生成動態內容。 3.MySQL基於客戶端-服務器模型工作,確保查詢速度可接受。

在 Docker 中啟動 MySQL 的過程包含以下步驟:拉取 MySQL 鏡像創建並啟動容器,設置根用戶密碼並映射端口驗證連接創建數據庫和用戶授予對數據庫的所有權限

Laravel 是一款 PHP 框架,用於輕鬆構建 Web 應用程序。它提供一系列強大的功能,包括:安裝: 使用 Composer 全局安裝 Laravel CLI,並在項目目錄中創建應用程序。路由: 在 routes/web.php 中定義 URL 和處理函數之間的關係。視圖: 在 resources/views 中創建視圖以呈現應用程序的界面。數據庫集成: 提供與 MySQL 等數據庫的開箱即用集成,並使用遷移來創建和修改表。模型和控制器: 模型表示數據庫實體,控制器處理 HTTP 請求。

優雅安裝 MySQL 的關鍵在於添加 MySQL 官方倉庫。具體步驟如下:下載 MySQL 官方 GPG 密鑰,防止釣魚攻擊。添加 MySQL 倉庫文件:rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm更新 yum 倉庫緩存:yum update安裝 MySQL:yum install mysql-server啟動 MySQL 服務:systemctl start mysqld設置開機自啟動
