Как выбрать несколько фильтров в таблице EAV на сервере SQL - PullRequest
1 голос
/ 03 апреля 2020

У меня есть таблица EAV с атрибутами и я хотел бы сделать гибридный выбор элементов на основе переменных, которые передаются в хранимую процедуру.

Пример таблицы:

| 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      |

Я мог бы жестко закодировать в and и or операторы для каждого key, но их много, и Я ищу более масштабируемое решение.

Было бы неплохо передать и проанализировать JSON, например:

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

Как мне написать второй select для динамического возврата подмножество allProductsFromGroup, которое динамически включает в себя несколько результатов из одной и той же группы (множественный выбор бренда или многократный выбор длины), но исключает из других групп (цвет, длина и т. д. c.)?

Ответы [ 2 ]

1 голос
/ 03 апреля 2020

Целевой запрос может выглядеть примерно так:

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. Вероятно, это тоже будет дешевле, так как каждый запрос может искать индекс (ключ, значение).

0 голосов
/ 03 апреля 2020

Вероятно, это поздний ответ, но если вы можете передать условия как JSON, следующий подход также является возможным решением. JSON должен быть в том же формате, что и в ответе, и вы можете использовать более двух условий:

Таблица:

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
...