2012-03-25 48 views
1

嗨,大家好,stackoverflow社區!我一直在訪問該網站多年,這纔是我的第一篇文章加快SQLite查詢,我可以做到沒有工會?

可以說我有三個表的數據庫:

  • 組(組ID,GroupType,MAX1,大小)
  • 糖果( candyID,姓名,選擇的)
  • 成員(組ID,填充NameID)

:將糖果工廠。
在糖果工廠中,10種糖果袋由80種不同的糖果製成。

所以:有10種獨特的團體類型(袋),有3種不同的尺寸:(4,5,6);一個組合是由80種獨特的糖果組合而成的。

在此之外,我製作了一個數據庫(有關糖果組合進入組的一些規則)。

在這一點上,我有一個數據庫與40791獨特的糖果袋。

現在我想比較一下糖果的一個集合與數據庫中的所有糖果袋,結果我想要比較集合中缺少3個或更少糖果的數據庫。

-- restore candy status 
update candies set selected = 0, blacklisted = 0; 

-- set status for candies to be selected 
update candies set selected = 1 where name in ('candy01','candy02','candy03','candy04'); 

select groupId, GroupType, max, count(*) as remainingNum, group_concat(name,', ') as remaining 
from groups natural join members natural join candies 
where not selected 
group by groupid having count(*) <= 3 

UNION -- Union with groups which dont have any remaining candies and have a 100% match 

select groupid, GroupType, max, 0 as remainingNum, "" as remaining 
from groups natural join members natural join candies 
where selected 
group by groupid having count(*) =groups.size; 

上述查詢執行此操作。但我試圖完成的事情是在沒有工會的情況下做到這一點,因爲速度是至關重要的。而且我也是sql新手,非常想學習/看看新的方法。

問候,魯特格爾

+2

請勿更新數據庫以進行選擇。它根本無法擴展(兩個用戶如何同時進行選擇?),它會降低性能,因爲寫入比讀取要慢。還要注意,UNION或尤其是UNION ALL通常速度非常快,通常會擊敗更復雜的單個查詢。 – GolezTrol 2012-03-25 08:47:21

+0

你的示例模式有些問題,因爲沒有什麼可以將'groups'或'members'連接到'candies',所以它將進行交叉連接,除非'members.memberID'應該是'members.candyID' – Seph 2012-03-25 09:00:56

回答

3

我不是100%地肯定你是通過這些查詢完成的事情,所以我沒有看過在完全不同的方法。如果你可以包含示例數據來展示你的邏輯,我可以看看這個。但是,就簡單地組合你的兩個查詢而言,我可以做到這一點。首先請注意,但是...

將SQL編譯到查詢計劃中。如果每個查詢的查詢計劃與另一個查詢的計劃顯着不同,則將它們組合成單個查詢可能是一個壞主意。你可能最終得到的是一個適用於這兩種情況的單一計劃,但對兩者都不是很有效。一個糟糕的計劃可能比兩個好的計劃糟糕得多=>更短,更緊湊的代碼並不總能提供更快的代碼。


你可以把selected到您的GROUP BY,而不是你的WHERE條款;事實上,你有兩個UNIONed查詢表明你已經將它們視爲兩個獨立的組。

然後,您的查詢之間的唯一區別是count(*)上的篩選器,您可以使用CASE WHEN聲明來調整篩選器...

SELECT 
    groups.groupID, 
    groups.GroupType, 
    groups.max, 
    CASE WHEN Candies.Selected = 0 THEN count(*) ELSE 0 END as remainingNum, 
    CASE WHEN Candies.Selected = 0 THEN group_concat(candies.name,', ') ELSE '' END as remaining 
FROM 
    groups 
INNER JOIN 
    members 
    ON members.GroupID = groups.GroupID 
INNER JOIN 
    candies 
    ON Candies.CandyID = members.CandyID 
GROUP BY 
    Groups.GroupID, 
    Groups.GroupType, 
    Groups.max, 
    Candies.Selected 
HAVING 
    CASE 
    WHEN Candies.Selected = 0 AND COUNT(*) <= 3   THEN 1 
    WHEN Candies.Selected = 1 AND COUNT(*) = Groups.Size THEN 1 
                  ELSE 0 
    END 
    = 
    1 

佈局變化僅僅是因爲我使用NATURAL JOIN維護的原因不同意。它們是最初建造的捷徑,也是後來發展的潛在災難。但這是一個不同的問題,如果你覺得你想要,你可以在線閱讀。

0

當您執行select操作時不要更新數據庫,您的第一個更新update candies set selected = 0, blacklisted = 0;將應用於整個表,並重寫每條記錄。您應該嘗試不使用selected並且將您的聯合更改爲UNION ALL。繼此,你試試內部聯接,而不是自然連接(但我不知道你的架構糖果成員)

select groupId, GroupType, max, count(*) as remainingNum, group_concat(name,', ') as remaining 
from groups 
    inner join members on members.groupid = groups.groupid 
    inner join candies on candies.candyid = member.candyid 
where name NOT in ('candy01','candy02','candy03','candy04') 
group by groups.groupid 
having count(*) <= 3 

UNION ALL -- Union with groups which dont have any remaining candies and have a 100% match 

select groupid, GroupType, max, 0 as remainingNum, "" as remaining 
from groups 
    inner join members on members.groupid = groups.groupid 
    inner join candies on candies.candyid = member.candyid 
where name in ('candy01','candy02','candy03','candy04') 
group by groupid 
having count(*) = groups.size; 

這應該至少比執行查詢之前更新表中的所有記錄較好。