2016-12-15 83 views
0

我有一些MySQL視圖,它們根據一些相對直接的子查詢定義了許多額外的列。數據庫也是多租戶的,所以每行都有一個公司ID。懶洋洋地評價MySQL視圖

我遇到的問題是我的視圖在被公司ID篩選之前對每一行進行評估,給出了巨大的性能問題。有什麼方法可以懶惰地評估視圖,以便外部查詢中的'where'子句適用於視圖中的子查詢。或者是否有類似於我可以用來添加額外字段的視圖。我想在SQL中計算它們,以便計算字段可用於過濾/搜索/排序/分頁。

我已經看過解釋可用算法的MySQL文檔,並且知道視圖不能作爲'merge'被處理,因爲它們包含子查詢。

視圖

create view companies_view as 
select *, 
(
    select count(id) from company_user where company_user.company_id = companies.id 
) as user_count, 
(
    select count(company_user.user_id) 
    from company_user join users on company_user.user_id = users.id 
    where company_user.company_id = companies.id 
     and users.active = 1 
) as active_user_count, 
(
    select count(company_user.user_id) 
    from company_user join users on company_user.user_id = users.id 
    where company_user.company_id = companies.id 
     and users.active = 0 
as inactive_user_count 
from companies; 

查詢

select * from companies_view where company_id = 123; 

我想在視圖中的子查詢到之後施加待評估的 '其中COMPANY_ID = 123' 從主查詢範圍。我無法在視圖中硬編碼公司ID,因爲我希望該視圖可用於任何公司ID。

回答

2

您無法更改由MySQL服務器設置的評估順序。

然而,在這種特殊情況下,你可以重寫整個SQL語句使用連接和有條件的計數,而不是子查詢:

select c.*, 
     count(u.id) as user_count, 
     count(if(u.active=1, 1, null)) as active_user_count, 
     count(if(u.active=0, 1, null)) as inactive_user_count 
from companies c 
left join company_user cu on c.id=cu.company_id 
left join users u on cu.user_id = u.id 
group by c.company_id, ... 

如果你有MySQL的V5.7,那麼您可能不需要添加任何由於companies表中的其他字段在功能上依賴於company_id,因此可以使用group by子句中的其他字段。在早期版本中,您可能必須列出companies表中的所有字段(取決於sql模式設置)。

優化此類查詢的另一種方法是使用非規範化。您的userscompany_user表可能比您的companies表有更多的記錄。你可以添加一個user_count,一個active_user_countinactive_user_count場到companies表,插入/更新後添加/刪除觸發器到company_user表和更新到users表後,有更新這些2場。這樣你就不需要在視圖中進行連接和條件計數。

1

是可能的說服優化器使用MERGE算法用標量子查詢處理視圖......你只需要在自己的遊戲中擊敗優化器。

這對某些人來說看起來相當不正統,但它是一種我需要的情況下成功使用的模式。

創建一個存儲的函數來封裝每個子查詢,然後在視圖中引用存儲的函數。優化器保持幸福不知道這些函數將調用子查詢。

CREATE FUNCTION user_count (_cid INT) RETURNS INT 
DETERMINISTIC 
READS SQL DATA 
RETURN (SELECT count(id) FROM company_user WHERE company_user.company_id = _cid); 

注意,使用單個語句的存儲功能並不需要BEGIN/ENDDELIMITER的變化。

然後在視圖中,替換爲子查詢:

user_count(id) AS user_count, 

並重復此過程爲每個子查詢。

然後優化器將視圖視爲MERGE視圖,根據外部WHERE從公司表中選擇一個合適的行,調用函數,並...解決問題。

相關問題