如何在 SQL 服务器中多重 select 过滤 EAV table

How to multi-select filter an EAV table in SQL Server

我有一个带有属性的 EAV table,我想根据传递到存储过程中的变量对项目进行混合 selection。

样本table:

| group_id | item_id | key    | value |
+----------+---------+--------+-------+
| 1        | AA      | length | 10    |
| 1        | AA      | width  | 10    | 
| 1        | AA      | color  | white |
| 1        | AA      | brand  | beta  |
| 1        | BB      | length | 25    |
| 1        | BB      | brand  | alpha |
| 2        | CC      | brand  | alpha |

示例查询:

declare @attributes nvarchar(max) = 'brand name, length'
declare @attributeValues nvarchar(max) = 'alpha, beta, 25'
declare @id int = 1

select *
into #allProductsFromGroup
from items
where group_id = @id

select item_id
from #allProductsFromGroup #all
where [key] in (select value from string_split(@attributes, ',')) 
  and [value] in (select value from string_split(@attributeValues, ','))

预期输出:

| item_id |
+---------+
| BB      |

我可以在每个 keyandor 语句中进行硬编码,但是有很多,我正在寻找更具扩展性的解决方案。

传入并解析JSON就好了,比如:

[
  { "brand": "aplha" },
  { "brand": "beta" },
  { "length": 25 }
]

我如何将第二个 select 写入动态 return allProductsFromGroup 的子集,动态包含来自同一组的多个结果(多 select 品牌或multi-select 长度),但从其他组(颜色、长度等)中排除?

目标查询可能如下所示:

with q as
(
    select item_id,
       max( case when [key] = 'brand' then [value] end ) brand,
       max( case when [key] = 'length' then cast( [value] as int ) end ) length,
    from #allProductsFromGroup 
    group by Item_id
)
select item_id
from q
where brand in ('alpha','beta') and length=25

您只需根据传入的数据构建它(糟糕)。要生成的更简单的查询表单可能类似于

select item_id
from #allProductsFromGroup 
where [key] = 'brand' and [value] in ('alpha','beta')
intersect 
select item_id
from #allProductsFromGroup 
where [key] = 'length' and [value] = 25

and 标准映射到 intersect,并将 or 标准映射到 union。它也可能更便宜,因为每个查询都可以在 (key,value) 上寻找索引。

这可能是一个迟到的答案,但如果您可以将条件传递为 JSON,下一个方法也是一个可能的解决方案。 JSON 必须与答案中的格式相同,您可以使用两个以上的条件:

Table:

CREATE TABLE Data (
   group_id int,
   item_id varchar(2),
   [key] varchar(100),
   [value] varchar(100)
)
INSERT INTO Data (group_id, item_id, [key], [value])
VALUES
   (1, 'AA', 'length', '10'),
   (1, 'AA', 'width',  '10'),
   (1, 'AA', 'color',  'white'),
   (1, 'AA', 'brand',  'beta'),
   (1, 'BB', 'length', '25'),
   (1, 'BB', 'brand',  'alpha'),
   (2, 'CC', 'brand',  'alpha')

条件为JSON:

DECLARE @conditions varchar(max) = N'
[
   {"key": "brand", "values": ["alpha", "beta"]},
   {"key": "length", "values": ["25"]}
]
'

声明:

SELECT d.item_id
FROM Data d
JOIN (
   SELECT j1.[key], j2.[value]
   FROM OPENJSON(@conditions) WITH (
      [key] varchar(100) '$.key',
      [values] nvarchar(max) '$.values' AS JSON
   ) j1
   CROSS APPLY OPENJSON(j1.[values]) j2
) o ON d.[key] = o.[key] AND d.[value] = o.[value]
GROUP BY d.item_id
HAVING COUNT(*) = (SELECT COUNT(*) FROM OPENJSON(@conditions))

结果:

item_id
BB