2017-10-28 117 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

添加了另一个更新。 –