2017-10-28 220 views
2

使用下面的查詢我要去基於選定tagscategories篩選結果:如何在動態搜索查詢中使用關係部門篩選結果?

DECLARE @categories NVARCHAR(MAX), 
     @tags NVARCHAR(MAX); 

SELECT @categories = '1,2,4', -- comma separated category ids 
     @tags = '2,3'   -- comma separated tag ids 

SELECT p.id, 
     p.title, 
     p.price 
FROM tbl_products p 
    LEFT JOIN tbl_product_categories pc ON @categories IS NOT NULL AND pc.product_FK = p.id 
    LEFT JOIN tbl_product_tags pt ON @tags IS NOT NULL AND pt.product_FK = p.id 
WHERE (p.price >= @min_price OR @min_price IS NULL) 
    AND (p.price <= @max_price OR @max_price IS NULL) 
    AND (pc.category_FK IN (SELECT value FROM STRING_SPLIT(@categories, ',')) OR @categories IS NULL) 
    AND (pt.tag_FK IN (SELECT value FROM STRING_SPLIT(@tags, ',')) OR @tags IS NULL) 
GROUP BY p.id 
HAVING COUNT(p.id) = ((SELECT COUNT(*) FROM STRING_SPLIT(@categories, ',')) + (SELECT COUNT(*) FROM STRING_SPLIT(@tags, ','))) 

但它不會產生預期的結果!我懷疑HAVING部分沒有正確使用,因爲它每次都不會根據傳遞的標籤和類別ID生成正確的計數。

有誰知道我們如何能夠實現這樣的情況下,適用關係部門來提取裏面有所有這些通過共同@categories@tags產品???有更好的方法嗎?

- 更新: 例如使用下面的示例日期:

tbl_products: 
id title  price 
=================== 
1 mouse  10 
2 keyboard 18 
3 iphone 8 100 
4 note 8  90 

tbl_product_categories: 
product_FK category_FK 
====================== 
1   1 
2   1 
3   2 
4   2 

tbl_product_tags: 
product_FK tag_FK 
================= 
1   1 
3   1 
3   2 
4   2 

所以如果我們通過@categories = '2'@tags = '1,2'min_price = 50那麼,我們應該得到一個iphone 8

+0

我們可以幫助更多的,如果你可以添加表的一些示例數據和您的查詢的預期輸出。 –

+0

@AbdullahDibas,當然;更新。 – dNitro

回答

1

而不是試圖從你的變量添加計數,可以使用count(distinct [tags|categories])等於計數像這樣的各個參數:

declare @categories nvarchar(max), @tags nvarchar(max), @min_price int, @max_price int; 
select 
    @categories = '2' -- comma separated category ids 
    , @tags = '1,2'  -- comma separated tag ids 
    , @min_price = 0 
    , @max_price = power(2,30) 

select 
    p.id 
    , p.title 
    , p.price 
from tbl_products p 
    left join tbl_product_categories pc 
    on @categories is not null and pc.product_fk = p.id 
    left join tbl_product_tags pt 
    on @tags is not null and pt.product_fk = p.id 
where (p.price >= @min_price or @min_price is null) 
    and (p.price <= @max_price or @max_price is null) 
    and (pc.category_fk in (select value from string_split(@categories, ',')) or @categories is null) 
    and (pt.tag_fk in (select value from string_split(@tags, ',')) or @tags is null) 
group by p.id, p.title, p.price 
having (count(distinct pc.category_fk) = (select count(*) from string_split(@categories, ',')) or @categories is null) 
    and (count(distinct pt.tag_fk) = (select count(*) from string_split(@tags, ',')) or @tags is null) 

演示:dbfiddle.uk demo

返回:

+----+----------+-------+ 
| id | title | price | 
+----+----------+-------+ 
| 3 | iphone 8 | 100 | 
+----+----------+-------+ 

當涉及到性能,你將受益於重寫這個與動態SQL執行的過程中,或至少option (recompile)在這些引用所示:


下面是一個使用的exists ...having count()...代替left join... where... having count(distinct ...),簡化計劃有點(plan comparison demo)您所查詢的動​​態SQL搜索過程的例子:

create procedure product_search (
    @categories nvarchar(max) 
    , @tags nvarchar(max) 
    , @min_price int 
    , @max_price int 
) as 
begin; 
set nocount, xact_abort on; 
declare @sql nvarchar(max); 
declare @params nvarchar(256); 
set @params = '@categories nvarchar(max), @tags nvarchar(max), @min_price int, @max_price int'; 
set @sql = '; 
select 
    p.id 
    , p.title 
    , p.price 
from tbl_products p 
where 1=1' 
if @min_price is not null 
set @sql = @sql + ' 
    and p.price >= @min_price'; 
if @max_price is not null 
set @sql = @sql + ' 
    and p.price <= @max_price'; 
if @categories is not null 
set @sql = @sql + ' 
    and exists (
     select 1 
     from tbl_product_categories ic 
     where ic.product_fk = p.id 
     and ic.category_fk in (select value from string_split(@categories, '','')) 
     having count(*) = (select count(*) from string_split(@categories, '','')) 
    )'; 
if @tags is not null 
set @sql = @sql + ' 
    and exists (
     select 1 
     from tbl_product_tags it 
     where it.product_fk = p.id 
     and it.tag_fk in (select value from string_split(@tags, '','')) 
     having count(*) = (select count(*) from string_split(@tags, '','')) 
    )'; 

exec sp_executesql @sql, @params, @categories, @tags, @min_price, @max_price; 
end; 
像這樣執行

declare @categories nvarchar(max), @tags nvarchar(max), @min_price int, @max_price int; 
select 
    @categories = null -- comma separated category ids 
    , @tags = '1,2'  -- comma separated tag ids 
    , @min_price = null 
    , @max_price = power(2,30) 

exec product_search @categories, @tags, @min_price, @max_price 

demo:​​個

回報:

+----+----------+-------+ 
| id | title | price | 
+----+----------+-------+ 
| 3 | iphone 8 | 100 | 
+----+----------+-------+ 
+0

美妙。奇蹟般有效。從我讀過的鏈接[動態搜索條件](http://www.sommarskog.se/dyn-search.html),並且必須讀取其他三個。 Tanx人。 – dNitro

+0

@DNitro樂意幫忙! – SqlZim

0

從你的樣本數據我想你'加入錯誤的列tag_FK而不是product_FK,所以表tbl_product_tags上的LEFT JOIN應該是:

LEFT JOIN tbl_product_tags pt ON @tags IS NOT NULL AND pt.product_FK = p.id 

此外,我不認爲有必要在您的查詢中使用HAVING聲明,在我看來,您使用它作爲額外的檢查;因爲你的查詢已經在做篩選工作。然而,在HAVING語句中的條件是不正確的,而且證明,最好的例子就是你們的榜樣本身:

1. count of p.Id = 1 (p.Id = 3 ... iPhone 8) 
2. count of categories = 1 (category: 2) 
3. count of tags = 2 (tags: 1, 2) 

那麼在這種情況下p.Id計數不等於通過分類和標籤的數。

UPDATE :基於@dtNiro查詢應該如下:

DECLARE @categories NVARCHAR(MAX), 
     @tags NVARCHAR(MAX); 

SELECT @categories = '1,2,4', -- comma separated category ids 
     @tags = '2,3'   -- comma separated tag ids 

SELECT p.id, 
     p.title, 
     p.price 
FROM tbl_products p 
    LEFT JOIN tbl_product_categories pc ON @categories IS NOT NULL AND pc.product_FK = p.id 
    LEFT JOIN tbl_product_tags pt ON @tags IS NOT NULL AND pt.product_FK = p.id 
WHERE (p.price >= @min_price OR @min_price IS NULL) 
    AND (p.price <= @max_price OR @max_price IS NULL) 
    AND (pc.category_FK IN (SELECT value FROM STRING_SPLIT(@categories, ',')) OR @categories IS NULL) 
    AND (pt.tag_FK IN (SELECT value FROM STRING_SPLIT(@tags, ',')) OR @tags IS NULL) 
GROUP BY p.id 
HAVING (@tags IS NULL OR (COUNT(p.id) = (SELECT COUNT(*) FROM STRING_SPLIT(@tags, ',')))) 
+0

連接問題是一個錯字,糾正:)'有''是因爲我們需要同時具有'標籤'1和2。像這裏提出的解決方案:https://stackoverflow.com/a/7774879/5048383。如果我們沒有檢查計數,那麼它會與'tag 1'或'tag 2'匹配產品,但我們同時需要兩個產品 – dNitro

+0

@dNitro產品可以分爲多個類別? –

+0

添加了另一個更新。 –