我正在开展一个电子商务项目。现在我必须为产品列表页面构建一个过滤器。 我的桌子在下面。
产品
id title | description | Etc.
-- ---------- | --------------------- | -----------
1 Product 1 | Product 1 description | xxx
2 Product 2 | Product 2 description | xxx
3 Product 3 | Product 3 description | xxx
4 Product 4 | Product 4 description | xxx
5 Product 5 | Product 5 description | xxx
规格
id title | Etc.
-- ---------- | ------
1 Color | xxx
2 Display | xxx
ProductSpecifications
id | productId | specificationId | value
----------- | ----------- | --------------- | -----
1 | 1 | 1 | Red
2 | 1 | 2 | LED
3 | 2 | 1 | Red
4 | 2 | 2 | OLED
5 | 3 | 1 | Blue
6 | 3 | 2 | LED
7 | 4 | 1 | Blue
8 | 4 | 2 | OLED
电子商务用户必须能够同时过滤多个选项。我的意思是,用户可能想要搜索“(红色或蓝色)和OLED”电视。
我尝试了一些东西,但我无法编写正确的存储过程。我想,我被困在这里,我需要一些帮助。
编辑:
经过一些回答后,我需要在此处更新一些其他信息。
规格是动态的。因此过滤器也是动态的。我使用名为allowFilter的位列生成过滤器。所以我无法使用强类型参数,例如@color
或@display
用户不得使用过滤器。或者他们可以使用一个或多个过滤器。你可以在这里找到我正在处理的查询:
ALTER PROCEDURE [dbo].[ProductsGetAll]
@categoryId int,
@brandIds varchar(max),
@specIds varchar(max),
@specValues varchar(max),
@pageNo int,
@pageSize int,
@status smallint,
@search varchar(255),
@sortOrder smallint
as
/*
TODO: Modify query to use sortOrder
*/
select * into #products
from
(
select ROW_NUMBER() OVER (order by p.sortOrder) as rowId,p.*
from Products p left join ProductSpecifications ps on ps.productId = p.id
where
(@status = -1
or (@status = -2 and (p.status = 0 or p.status = 1))
or (p.status = @status)
)
and (@categoryId = -1 or p.categoryId = @categoryId)
and (@brandIds = '' or p.brandId in (select ID from fnStringToBigIntTable(@brandIds,',')))
and (
@search = ''
or p.title like '%' + @search + '%'
or p.description like '%' + @search + '%'
or p.detail like '%' + @search + '%'
)
and (@specIds = ''
or (
ps.specificationId in (select ID from fnStringToBigIntTable(@specIds,','))
and ps.value in (@specValues)
)
)
) x
where
(rowId > @pageSize * (@pageNo - 1) and rowId <= @pageSize * @pageNo)
select * from #products
select * from Categories where id in (select categoryId from #products)
select * from Brands where id in (select brandId from #products)
select count(p.id)
from Products p left join ProductSpecifications ps on ps.productId = p.id
where
(@status = -1
or (@status = -2 and (p.status = 0 or p.status = 1))
or (p.status = @status)
)
and (@categoryId = -1 or p.categoryId = @categoryId)
and (@brandIds = '' or p.brandId in (select ID from fnStringToBigIntTable(@brandIds,',')))
and (
@search = ''
or p.title like '%' + @search + '%'
or p.description like '%' + @search + '%'
or p.detail like '%' + @search + '%'
)
and (@specIds = ''
or (
ps.specificationId in (select ID from fnStringToBigIntTable(@specIds,','))
and ps.value in (@specValues)
)
)
drop table #products
我的问题是:
and (@specIds = ''
or (
ps.specificationId in (select ID from fnStringToBigIntTable(@specIds,','))
and ps.value in (@specValues)
)
)
我完全可以更改此部分以及此部分中使用的参数。
答案 0 :(得分:3)
首先,我要感谢你@alex。我使用表值参数来解决我的问题。
类型:
function myfunction($template)
{
$template['selected'] = 'null';
return $template;
}
存储过程:
CREATE TYPE [dbo].[specificationsFilter] AS TABLE(
[specId] [int] NULL,
[specValue] [varchar](50) NULL
)
.Net Code创建数据表参数:
ALTER PROCEDURE [dbo].[ProductsGetAll]
@categoryId int,
@brandIds varchar(max),
@specifications specificationsFilter readonly,
@pageNo int,
@pageSize int,
@status smallint,
@search varchar(255),
@sortOrder smallint
as
declare @filterCount int
set @filterCount = (select count(distinct specId) from @specifications)
/*
ORDER BY
TODO: Modify query to use sortOrder
*/
select * into #products
from
(
select ROW_NUMBER() OVER (order by p.sortOrder) as rowId,p.*
from Products p
where
(@status = -1
or (@status = -2 and (p.status = 0 or p.status = 1))
or (p.status = @status)
)
and (@categoryId = -1 or p.categoryId = @categoryId)
and (@brandIds = '' or p.brandId in (select ID from fnStringToBigIntTable(@brandIds,',')))
and (
@search = ''
or p.title like '%' + @search + '%'
or p.description like '%' + @search + '%'
or p.detail like '%' + @search + '%'
)
and (@filterCount = 0
or (
p.id in (
select productId
from ProductSpecifications ps, @specifications s
where
ps.specificationId = s.specId
and ps.value = s.specValue
group by productId
having sum(1) >= @filterCount
)
)
)
) x
where
(rowId > @pageSize * (@pageNo - 1) and rowId <= @pageSize * @pageNo)
select * from #products
select * from Categories where id in (select categoryId from #products)
select * from Brands where id in (select brandId from #products)
select count(p.id)
from Products p
where
(@status = -1
or (@status = -2 and (p.status = 0 or p.status = 1))
or (p.status = @status)
)
and (@categoryId = -1 or p.categoryId = @categoryId)
and (@brandIds = '' or p.brandId in (select ID from fnStringToBigIntTable(@brandIds,',')))
and (
@search = ''
or p.title like '%' + @search + '%'
or p.description like '%' + @search + '%'
or p.detail like '%' + @search + '%'
)
and (@filterCount = 0
or (
p.id in (
select productId
from ProductSpecifications ps, @specifications s
where
ps.specificationId = s.specId
and ps.value = s.specValue
group by productId
having sum(1) >= @filterCount
)
)
)
drop table #products
我的查询字符串结构:
private DataTable GetSpecificationFilter(string specificationFilter)
{
DataTable table = new DataTable();
table.Columns.Add("specId", typeof(Int32));
table.Columns.Add("specValue", typeof(string));
string[] specifications = specificationFilter.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach(string specification in specifications)
{
string[] specificationParams = specification.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
int specificationId = Convert.ToInt32(specificationParams[0]);
string[] specificationValues = specificationParams[1].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach(string value in specificationValues)
{
table.Rows.Add(specificationId, value);
}
}
return table;
}
这是在垂直表结构中过滤产品规格的完整解决方案。我用它来做电子商务项目。我希望这个解决方案可以帮助您解决类似问题。
答案 1 :(得分:1)
您需要一种方法来传递规范及其值。一种方法是使用group by
和having
进行整体查询:
select ps.product_id
from product_specifications ps join
specifications s
on ps.specification_id = s.specification_id
where (s.name = @title1 and ps.value = @value1) or
(s.name = @title2 and ps.value = @value2)
having count(*) = 2; -- "2" is the number of specifications you are checking
此版本要求将规范和值添加为单独的变量。有类似的方法,您可以使用临时变量或values
子句传入值。目前还不清楚传递值的方法在您的特定情况下最有效。
答案 2 :(得分:1)
Table valued parameters。 (见related)
<小时/> 旧回答
CREATE PROCEDURE GetData
@Color CHAR(2) -- "10" is only red, "01" is only green, "11" is both red and green
, @Display CHAR(2) -- "10" is LED, "01" is OLED, "11" is both LED and OLED
AS
BEGIN
DECLARE @Values TABLE (Value NVARCHAR(10))
IF SUBSTRING(@Color, 1, 1) = '1' BEGIN INSERT INTO @Values (Value) VALUES ('Red') END
IF SUBSTRING(@Color, 2, 1) = '1' BEGIN INSERT INTO @Values (Value) VALUES ('Green') END
IF SUBSTRING(@Display, 1, 1) = '1' BEGIN INSERT INTO @Values (Value) VALUES ('LED') END
IF SUBSTRING(@Display, 2, 1) = '1' BEGIN INSERT INTO @Values (Value) VALUES ('OLED') END
SELECT *
FROM productspecifications ps
INNER JOIN products p
ON p.id = ps.productid
INNER JOIN specifications s
ON ps.specificationid = s.id
WHERE ps.Value IN (SELECT * FROM @Values)
END
此示例非常特定于您提供的表格。
你传递两个只包含0和1的字符串(例如:&#34; 0010110&#34;)。您的存储过程将知道将字符串@Color
中索引0处的1解释为Red
,将@Color
中索引1处的1解释为Blue
。 LED与OLED相同。您的存储过程将有许多IF语句来检查每个字符串中的每个索引,并将相应的值存储在某个临时表中(如果没有太多值,则存储临时表变量)。然后,当您查询表时,只需放置一个WHERE
子句,该子句检查您刚刚创建的临时表中ProductSpecifications表中的值。
如果您想要(red or blue) and LED
,那么@Color = "10"
和@Display = "10"
如果您想要blue and OLED
,那么@Color = "01"
和@Display = "01"
。
如果您想要所有@Color = "11"
和@Display = "11"
。
(red or blue) and LED
逻辑效果这不是一个好的解决方案。我个人不喜欢它,但它会完成工作。如果有人知道如何改进这将是惊人的。我很想自己学习更好的解决方案。
另外,在我看来,你需要通过&#34; array&#34;数据作为存储过程的参数,所以我想你可能想看看如何做到这一点的不同方式。我提供的示例中的一个是实现&#34;数组传递&#34;的一种方式,但还有许多其他更好的方法。
答案 3 :(得分:0)
我认为你需要第一次 外键来做你想做的事。
您可以在产品表中添加字段并将其命名为规范,这将是您的外键。
之后做你想做的事情尝试使用 GROUP BY 表达式
答案 4 :(得分:0)
我认为如果只有值参数可以使用,或者添加更多搜索参数,比如
CREATE PROCEDURE usp_ProductSpecifications (@value)
AS
BEGIN
SELECT p.id
,p.NAME
,s.etc
,ps.value
,p.etc
FROM productspecifications ps
INNER JOIN products p
ON p.id = ps.productid
INNER JOIN specifications s
ON ps.specificationid = s.id
WHERE ps.value = @value
END
答案 5 :(得分:0)
请尝试以下建议的解决方案,希望它有帮助!
Create Procedure SearchByCriteria
@Color VARCHAR(100) = NULL,
@Display VARCHAR(100) = NULL
AS
BEGIN
IF @Color IS NOT NULL
SET @Color = '%' + REPLACE (@Color,',','% OR ') + '%'
SELECT
fROM PRoduct p
INNER JOIN ProductSpecification ps ON ps.ProductId = p.productID
LEFT OUTER JOIN specification scolor ON scolor.ID = ps.SpecificationID
and scolor.Id = 1
LEFT OUTER JOIN specification sDisplay ON sdisplay.ID = ps.SpecificationID
and sdisplay.Id = 2
WHERE (@Color IS NULL OR scolor.etc like @Color)
AND (@Display IS NULL OR Sdisplay like @Display)
END
GO