反饋已提交
網絡繁忙
設計器拼出最終的 SQL,將 SQL 語句傳給資料庫,資料庫執行,將資料傳回給設計器。
由於計算過程首先要透過 SQL 語句從資料庫中取資料,我們可以透過控制資料量的大小和對資料的提前預處理來提高報表的效能。
報表的核心是資料,資料集是否合理決定報表的質量。
每張報表都應該有一個主資料集,為了降低維護時的工作量,盡量將所有欄位置於主資料集,除非在某些情況下,不使用多源資料集會導緻主資料集異常複雜。
在製作報表之前,盡量考慮到所有需要展示的資料欄位,在資料庫軟體中,合理編寫 SQL 語句,大數據情況盡量對 SQL 做優化,以及新增索引。
本文將詳細介紹如何優化報表取數。
FineReport 報表的資料集採用的是表模型,也就是說透過 SQL 這種 DSL 語言,從資料庫透過簡單查詢或各種組合聯動查詢得到一個關系表,而這部分 SQL 查詢根據各種資料庫廠商長時間的優化(比如建立索引),已經非常成熟。
資料集一般要透過 FineReport 報表模型的複雜處理才能生成最終的表樣。因此,從資料庫 SQL 查詢取出資料量越少,FineReport 報表模型需要做的複雜處理和計算就越少,所花的時間和記憶體就少,進而可以提高效能。
我們一般會用【select * from】 這樣的形式將一個資料庫表中所有的欄位都取出來,而其中一些欄位是報表中不需要用到的,例如報表中只需要用到三個欄位,但是資料庫中實際的表有十個欄位,一些初學者習慣性的用【select * from table1】,這樣相當於把十個欄位的資料都取到報表伺服器端,增加了報表伺服器端的記憶體佔用以及減慢了運算速度,所以 SQL 語句中盡量不要用“*”號,而是寫上具體的欄位,能夠減少報表伺服器端的記憶體佔用,加快報表的運算速度。
一些匯總型別的報表,例如製作一張訂單總額的表,可能會從訂單明細表中取出大量的資料記錄,然後進行資料匯總,即進行分組聚集運算,報表計算過程中我們可以在 SQL 中提前進行一次分組聚集,能夠大大減少取到的報表伺服器的記錄數,加快取數和報表運算的速度。
SQL 語句:【SELECT 成本價,類別ID FROM 產品】從資料庫中選擇如上兩個欄位,然後根據類別 ID 進行成本價的匯總,此時資料庫傳回給報表處理的資料就有 79 筆。如下圖1所示。
優化的 SQL 語句:【SELECT sum(成本價),類別ID FROM 產品 group by 類別ID】;經過 SQL 優化後,報表需要處理的資料就只剩 8 筆了。如下圖2所示。
優化分析:
第一種做法,不僅僅取到報表伺服器上記錄數多了,取數速度慢,而且報表模型需要對表資料欄進行分組運算,增加了報表運作時間。
第二種做法,資料庫雖然要進行分組運算,但是資料庫中有索引,運算速度快,且取到報表伺服器端的記錄數大大減少,取數速度大大加快,因此在報表模型進行分組運算的時候只要對很少的記錄數進行,報表的運算速度大大加快了。
實驗結果以及分析表明,第二種做法的效能遠優於第一種。所以,分組應該盡量在 SQL 裏進行。
報表計算過程中很多時候需要對資料進行排序,雖然排序運算可以在報表端進行,不過我們還是建議在 SQL 中提前將資料排序,這是因為資料庫中的索引功能,通常是 C/C++ 語言(往往在效率上比 Java 好)寫的,會使得排序運算的速度更快。
報表計算過程中很多時候並不需要對表中的所有記錄進行操作,而只是需要對部分滿足條件的記錄進行操作,雖然可以在報表設計器中對資料過濾,不過我們建議在 SQL 中對資料提前過濾,這樣資料庫傳回的資料就減少了,既加快了取數速度,也加快了報表的運算速度。
檢視表是指計算機資料庫中的檢視表,是一個虛擬表,其內容由查詢定義。同真實的表一樣,檢視表包含一系列帶有名稱的欄和列資料。但是,檢視表並不在資料庫中以儲存的資料值集形式存在。列和欄資料來自由定義檢視表的查詢所引用的表,並且在引用檢視表時動態生成。
儲存過程透過流控制與 SQL 語句,可以對資料進行強大的運算與處理,對於業務比較複雜的應用,常常需要將原始資料透過儲存過程處理後再供報表使用。另外儲存過程運作前,資料庫會對其進行文法和文法的分析,並進行優化,這種已經編譯好的儲存過程極大地改善 SQL 語句的效能。在報表端也只需要書寫較短的呼叫語句來獲得結果,進而降低網路的通訊量。
所以表與表的連結、複雜的 SQL 盡量在資料庫中使用檢視表或者儲存過程直接進行,這樣將複雜的 SQL 語句直接儲存於資料庫伺服器端(資料庫本身會對 SQL 語句進行文法分析並進行優化),在報表設計器端就不需要寫大段的 SQL 語句而是直接呼叫檢視表或儲存過程了,一方面減少網路傳輸量,減輕資料庫的壓力,另一方面加快了報表的運算速度。
當你想在 SELECT 子句中列出所有的 columns 時,使用動態 SQL 欄引用‘*’是一個方便的方法。不幸的是,這是一個非常低效的方法。 實際上,Oracle 在解析的過程中, 會將“*” 依次轉換成所有的欄名, 這個工作是透過查詢資料字典完成的, 這意味着將耗費更多的時間。
最高效的刪除重複記錄方法 ( 因為使用了 ROWID)【DELETE FROM EMP E WHERE E.ROWID > (SELECT MIN(X.ROWID) FROM EMP X WHERE X.EMP_NO = E.EMP_NO)】。
當刪除表中的記錄時,在通常情況下,回滾段(rollback segments)用來存放可以被恢複的資訊。如果你沒有 COMMIT 交易,Oracle 會將資料恢複到刪除之前的狀態(準確地說是恢複到執行刪除命令之前的狀況),而當運用 TRUNCATE 時, 回滾段不再存放任何可被恢複的資訊。當命令運作後,資料不能被恢複。因此很少的資源被呼叫,執行時間也會很短。
和一般的觀點相反【count(*)】 比【count(1)】稍快,當然如果可以透過索引檢索,對索引欄的計數仍舊是最快的。例如【 COUNT(EMPNO) 】。
在許多基於基礎表的查詢中,為了滿足一個條件,往往需要對另一個表進行聯接。在這種情況下,使用 EXISTS 或 NOT EXISTS 可以提高查詢的效率。例如:
低效:【SELECT * FROM EMP WHERE EMPNO > 0 AND DEPTNO IN (SELECT DEPTNO FROM DEPT WHERE LOC ='MELB')】;
高效:【SELECT * FROM EMP WHERE EMPNO > 0 AND EXISTS (SELECT ‘X’ FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = 'MELB')】。
當提交一個包含一對多表資訊( 比如部門表和僱員表 )的查詢時,避免在 SELECT 子句中使用 DISTINCT, 一般可以考慮用 EXIST 更換。例如:
低效:【SELECT DISTINCT DEPT_NO,DEPT_NAME FROM DEPT D,EMP E WHERE D.DEPT_NO = E.DEPT_NO 】;
高效:【SELECT DEPT_NO,DEPT_NAME FROM DEPT D WHERE EXISTS ( SELECT ‘X’ FROM EMP E WHERE E.DEPT_NO = D.DEPT_NO)】。
例如 DEPTNO 上有一個索引;
高效:【SELECT * FROM EMP WHERE DEPTNO >=4 】;
低效:【SELECT * FROM EMP WHERE DEPTNO >3】;
兩者的差別在於, 前者 DBMS 將直接跳到第一個 DEPT 等於 4 的記錄。而後者將首先定位到 DEPTNO=3 的記錄並且向前掃瞄到第一個 DEPT 大於 3 的記錄。
例如:【select id from t where num is null】;
可以在 num 上設定預設值 0,確定表中 num 欄沒有 null 值,然後這樣查詢:【select id from t where num=0】。
將引擎放棄使用索引而進行全表掃瞄。優化器將無法透過索引來確定將要命中的列數,因此需要搜尋該表的所有列。
否則將導緻引擎放棄使用索引而進行全表掃瞄,例如:【select id from t where num=10 or num=20】;
可以這樣查詢:【select id from t where num=10 union all select id from t where num=20】。
因為 IN 會使系統無法使用索引,而只能直接搜尋表中的資料。例如:【select id from t where num in(1,2,3)】;
對於連續的數值,能用 between 就不要用 in 了:【select id from t where num between 1 and 3】。
這將導緻引擎放棄使用索引而進行全表掃瞄。例如:
【SELECT * FROM T1 WHERE F1/2=100應改為:SELECT * FROM T1 WHERE F1=100*2】;
【SELECT * FROM RECORD WHERE SUBSTRING(CARD_NO,1,4)='5378'】應改為:【SELECT * FROM RECORD WHERE CARD_NO LIKE '5378%'】;
【SELECT member_number, first_name, last_name FROM members WHERE DATEDIFF(yy,datofbirth,GETDATE()) > 21】應改為:【SELECT member_number, first_name, last_name FROM members WHERE dateofbirth < DATEADD(yy,-21,GETDATE())】。
注:任何對欄的操作都將導緻表掃瞄,它包括資料庫函式、計算表式等等,查詢時要盡可能將操作移至等號右邊。
name 以 abc 開頭的 id:【select id from t where substring(name,1,3)='abc'】應改為:【select id from t where name like 'abc%'】;
'2005-11-30'生成的 id:【select id from t where datediff(day,createdate,'2005-11-30')=0】應改為:【select id from t where createdate>=’2005-11-30′ and createdate<'2005-12-1'】。
進行函式、算術運算或其他表式運算,系統將可能無法正確使用索引。
若資料量過大,應該考慮相應需求是否合理。
例如 float 和 int、char 和 varchar、binary 和 varbinary 是不相容的。資料型別的不相容可能使優化器無法執行一些本來可以進行的優化操作。例如:【SELECT name FROM employee WHERE salary > 60000】在這筆語句中,如 salary 欄位是 money 型的,則優化器很難對其進行優化,因為 60000 是個整數型態數。我們應當在編程時將整數型態轉化成為錢幣型,而不要等到運作時轉化。
在某種情況下,兩個表之間可能不只一個的連結條件,這時在 WHERE 子句中將連結條件完整的寫上,有可能大大提高查詢速度。例如:
【SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO】;
【SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO AND A.ACCOUNT_NO=B.ACCOUNT_NO】
第二句將比第一句執行快得多。
【SELECT DISTINCT OrderID FROM Details WHERE UnitPrice > 10】;可改為:【SELECT OrderID FROM Details WHERE UnitPrice > 10 GROUP BY OrderID】。
UNION ALL 不執行 SELECT DISTINCT 函式,這樣就會減少很多不必要的資源。
SELECT INTO 語句會導緻表鎖定,阻止其他使用者訪問該表。
滑鼠選中內容,快速回饋問題
滑鼠選中存在疑惑的內容,即可快速回饋問題,我們將會跟進處理。
不再提示
10s後關閉
反馈已提交
网络繁忙