sql - 在线商店产品过滤器的 T-SQL 查询

标签 sql sql-server t-sql

我需要创建一个存储过程,它返回产品特征及其产品数量。输入参数为:

  1. 产品类别。查询必须返回此特定类别及其子类别的数据
  2. 选定的产品特性 - 表值参数。如果此参数包含行,则查询必须计算也具有此特定特征的产品的每个特征的产品计数。

问题:

  1. 数据库包含 500 000 多个产品行,那么就性能而言,最佳解决方案(t-sql 查询)是什么?
  2. 我尝试进行如下所示的查询,但我认为它很难看,而且实际上无法正确计算产品数量。我需要专业的帮助来尽快做出正确的查询

表脚本和示例数据:

--categories
    create table tCategory(c_id int identity(1,1) primary key, c_name nvarchar(200), c_parent int)
    insert into tCategory(c_name, c_parent) select 'Smartphones', null
    insert into tCategory(c_name, c_parent) select 'iPhone 6S', 1
    insert into tCategory(c_name, c_parent) select 'iPhone 7', 1
    insert into tCategory(c_name, c_parent) select 'iPhone 7 Plus', 1
    
    --products
    create table tProduct(p_id int identity(1,1) primary key, p_name nvarchar(200), c_id int)
    insert into tProduct(p_name, c_id) select '4.7" Apple iPhone 6S 32 gb gold', 2
    insert into tProduct(p_name, c_id) select '4.7" Apple iPhone 6S 32 gb brown', 2
    insert into tProduct(p_name, c_id) select '4.7" Apple iPhone 7 32 gb pink', 3
    insert into tProduct(p_name, c_id) select '4.7" Apple iPhone 7 32 gb brown', 3
    insert into tProduct(p_name, c_id) select '5.5" Apple iPhone 7 Plus 32 gb black', 4
    insert into tProduct(p_name, c_id) select '4.7" Apple iPhone 6S 128 gb pink', 2
    
    --characteristics type (color, size etc.)
    create table tProductCharItem(pci_id int identity(1,1) primary key, pci_name nvarchar(200))
    insert into tProductCharItem(pci_name) select 'Display'
    insert into tProductCharItem(pci_name) select 'Color'
    insert into tProductCharItem(pci_name) select 'Memory'
    
    --characteristics value (blue, 50х50 etc.)
    create table tProductCharItemValue(pciv_id int identity(1,1) primary key, pci_id int, pciv_value nvarchar(50))
    insert into tProductCharItemValue(pci_id, pciv_value) select 1, '4.7"'
    insert into tProductCharItemValue(pci_id, pciv_value) select 1, '5.5"'
    insert into tProductCharItemValue(pci_id, pciv_value) select 2, 'gold'
    insert into tProductCharItemValue(pci_id, pciv_value) select 2, 'brown'
    insert into tProductCharItemValue(pci_id, pciv_value) select 2, 'pink'
    insert into tProductCharItemValue(pci_id, pciv_value) select 2, 'black'
    insert into tProductCharItemValue(pci_id, pciv_value) select 3, '32 gb'
    insert into tProductCharItemValue(pci_id, pciv_value) select 3, '128 gb'
    
    --products characteristics
    create table tProductChar(pc_id int identity(1,1) primary key, p_id int, pciv_id int)
    insert into tProductChar(p_id, pciv_id) select 1, 1
    insert into tProductChar(p_id, pciv_id) select 1, 7
    insert into tProductChar(p_id, pciv_id) select 1, 3
    insert into tProductChar(p_id, pciv_id) select 2, 1
    insert into tProductChar(p_id, pciv_id) select 2, 4
    insert into tProductChar(p_id, pciv_id) select 2, 7
    insert into tProductChar(p_id, pciv_id) select 3, 1
    insert into tProductChar(p_id, pciv_id) select 3, 5
    insert into tProductChar(p_id, pciv_id) select 3, 7
    insert into tProductChar(p_id, pciv_id) select 4, 1
    insert into tProductChar(p_id, pciv_id) select 4, 4
    insert into tProductChar(p_id, pciv_id) select 4, 7
    insert into tProductChar(p_id, pciv_id) select 5, 2
    insert into tProductChar(p_id, pciv_id) select 5, 6
    insert into tProductChar(p_id, pciv_id) select 5, 7
    insert into tProductChar(p_id, pciv_id) select 6, 1
    insert into tProductChar(p_id, pciv_id) select 6, 5
    insert into tProductChar(p_id, pciv_id) select 6, 8

用户未选择任何过滤器时的预期结果:

+--------+---------+----------+------------+----------------+
| pci_id | pciv_id | pci_name | pciv_value | products_count |
+--------+---------+----------+------------+----------------+
|      1 |       1 | Display  | 4.7"       |              5 |
|      2 |       3 | Color    | gold       |              1 |
|      2 |       4 | Color    | brown      |              2 |
|      2 |       5 | Color    | pink       |              2 |
|      2 |       6 | Color    | black      |              1 |
|      3 |       7 | Memory   | 32 gb      |              5 |
|      3 |       8 | Memory   | 128 gb     |              1 |
|      1 |       2 | Display  | 5.5"       |              1 |
+--------+---------+----------+------------+----------------+

用户按颜色特征“棕色”选择过滤器时的预期结果

+--------+---------+----------+------------+----------------+
| pci_id | pciv_id | pci_name | pciv_value | products_count |
+--------+---------+----------+------------+----------------+
|      1 |       1 | Display  | 4.7"       |              2 |
|      2 |       3 | Color    | gold       |              0 |
|      2 |       4 | Color    | brown      |              2 |
|      2 |       5 | Color    | pink       |              0 |
|      2 |       6 | Color    | black      |              0 |
|      3 |       7 | Memory   | 32 gb      |              2 |
|      3 |       8 | Memory   | 128 gb     |              0 |
|      1 |       2 | Display  | 5.5"       |              0 |
+--------+---------+----------+------------+----------------+

这是我的尝试(丑陋且无法正确计算产品数量):

CREATE TYPE integer_list_tbltype AS TABLE (n int NOT NULL PRIMARY KEY)

declare @c_id int = 1 --category id
declare @pciv_ids integer_list_tbltype --list of selected filters (products characteristics)

insert into @pciv_ids(n) select 4

;with cats as 
(
    select c_id from tCategory where c_id = @c_id
    union all
    select t.c_id from cats 
        inner join tCategory t on cats.c_id = t.c_parent 
),
groupped_pci as (
select distinct p.c_id, pci.pci_id, pciv.pciv_id
from tProductChar pc 
join tProduct p on pc.p_id = p.p_id
join tProductCharItemValue pciv on pc.pciv_id = pciv.pciv_id
join tProductCharItem pci on pciv.pci_id = pci.pci_id),
products_count as (
    select count(distinct p.p_id) cnt, pc.pciv_id
    from tProduct p join tProductChar pc on p.p_id = pc.p_id
    cross apply (select * from tProductChar pc left join @pciv_ids t on pc.pciv_id = t.n where p_id = p.p_id and pc.pciv_id is not null) t
    group by pc.pciv_id
)

select pci.pci_id, pciv.pciv_id, pci.pci_name, pciv.pciv_value, pc.cnt products_count
from groupped_pci
join cats on cats.c_id = groupped_pci.c_id
join tProductCharItem pci on groupped_pci.pci_id = pci.pci_id
join tProductCharItemValue pciv on groupped_pci.pciv_id = pciv.pciv_id
left join products_count pc on groupped_pci.pciv_id = pc.pciv_id

最佳答案

这就是我会做的以避免子查询:

declare @filters table (pciv_id int)
insert into @filters(pciv_id) values (4)

if exists (select * from @filters)
    set @hasFilter=1

;with cats as 
(
    select c_id from tCategory where c_id = @c_id
    union all
    select t.c_id from cats 
    inner join tCategory t on cats.c_id = t.c_parent 
)
, filteredProducts as 
(
    select p.p_id
    from cats
    inner join tProduct p on p.c_id=cats.c_id
    inner join tProductChar pc on pc.p_id=p.p_id
    inner join tProductCharItemValue pcv on pcv.pciv_id=pc.pciv_id
    left join @filters f on f.pciv_id=pc.pciv_id
    where @hasFilter=0 or f.pciv_id is not null
    group by p.p_id
)
select ci.pci_id, pcv.pciv_id, ci.pci_name, pcv.pciv_value, count(p.p_id) products_count
from tProductCharItem ci
inner join tProductCharItemValue pcv on pcv.pci_id=ci.pci_id
left join tProductChar pc on pc.pciv_id=pcv.pciv_id
left join filteredProducts p on p.p_id=pc.p_id
group by ci.pci_id, pcv.pciv_id, ci.pci_name, pcv.pciv_value

关于sql - 在线商店产品过滤器的 T-SQL 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63147609/

相关文章:

sql-server - 选择并更新; SQL服务器

sql - 将多个表插入到一张表中

php - Metaphone vs Levenshtein vs Soundex vs Hamming

c# - sql连接字符串问题

php - 自己的数据库数据计数器

asp.net - sisoDB是否使用sql server数据库来存储数据

mysql - 为什么 SQL 不匹配两个字符串?

sql - 禁止基于 WHERE 子句的 SELECT 输出

mysql - SQL查找id-s,其中列的值等于特定行上的值

sql - 新手sql事务问题: preventing race in read - compute -write