有很多方法可以通過不同級別的難度和性能來實現這一點。
對我來說,第一個也是最明顯的方法是簡單地指望過濾器的性能相當好,並且不難實現。另一種類似的方法是按價值分組並進行計數。
這裏有一個小提琴爲這兩種方法的例子:http://sqlfiddle.com/#!15/0cdcb/26
select
count(product.id) total,
sum((v0.value = 'spam')::int) v0_is_spam,
sum((v0.value != 'spam')::int) v0_not_spam,
sum((v1.value = 'spam')::int) v1_is_spam,
sum((v1.value != 'spam')::int) v1_not_spam
from product
left join specification_value v0 on v0.product_id = product.id and v0.specification_id = 1
left join specification_value v1 on v1.product_id = product.id and v1.specification_id = 2;
select specification.id, value, count(*)
from specification
left join specification_value on specification.id = specification_value.specification_id
group by specification.id, value;
稍微更困難的方式做這樣的事情正在使用的窗口功能,很多更靈活,但是不容易掌握。文檔是在這裏:http://www.postgresql.org/docs/9.3/static/tutorial-window.html
示例查詢和結果:
SELECT depname, empno, salary, avg(salary) OVER (PARTITION BY depname) FROM empsalary;
depname | empno | salary | avg
-----------+-------+--------+-----------------------
develop | 11 | 5200 | 5020.0000000000000000
develop | 7 | 4200 | 5020.0000000000000000
develop | 9 | 4500 | 5020.0000000000000000
develop | 8 | 6000 | 5020.0000000000000000
develop | 10 | 5200 | 5020.0000000000000000
personnel | 5 | 3500 | 3700.0000000000000000
personnel | 2 | 3900 | 3700.0000000000000000
sales | 3 | 4800 | 4866.6666666666666667
sales | 1 | 5000 | 4866.6666666666666667
sales | 4 | 4800 | 4866.6666666666666667
(10 rows)
最後,迄今爲止最快也最不準確,難以實施。使用數據庫統計信息猜測行數。除非在過濾器集合中有數百萬行,並且無法進一步減少它,否則我不會選擇此選項。另外,除非性能非常糟糕以至於需要,否則不要這樣做。
我覺得窗口函數可能是解決方案,但我的擔心主要是關於性能和多餘的查詢。比如說,我們拿到了手機規格的DB。在用戶設置cpu:arm,ram> 2Gb,防水和NFC之後,他應該得到這樣的數字 - 在第一次(cpu)過濾1000個手機之後,在ram-250之後,在防水之後 - 10以及在NFC之後 - 數據庫2之後。所以,如果我沒有弄錯,所有的方法(除了最後一個)都需要4個非常相似的查詢。最初,我甚至想通過解析「EXPLAIN」來從Query Planner獲取信息。 – 2014-09-13 15:51:55
請注意,它可能會使一切都比簡單的查詢困難:)只要看看更新的版本,你可以在一個單一的查詢(除了實際的結果是)除外。 – Wolph 2014-09-13 16:23:20