SQL:根据其属性查找项目

发布于 2025-02-06 17:28:35 字数 1602 浏览 0 评论 0 原文

在PostgreSQL上,我有一个表项目和一个table item_attribute。因此,项目可以具有多个属性。

表:项目

ID 名称
1 A
2 B
3 C

表:Item_Attribute

ID ITEM_ID 名称
1 1 FOO bar
2 1 size m
3 1 country de
4 2 foo baz
5 2 size m
6 2 Country de
7 3 其他 测试
8 3 3 尺寸 m
9 3 Country 现在

,我需要找到与给定属性的名称/值对匹配的项目

,因此,假设我想找到带有 country 带有值 de de country 属性的项目代码>和 size 带有值 m 结果集应为

ID 名称
1 A
2 B,

如果我想找到带有 country> country 的项目值 en size 带有值 m 结果集应

ID 名称
3 C。

On PostgreSQL, I have a table item, and a table item_attribute. So an item can have multiple attributes.

table: item

id name
1 A
2 B
3 C

table: item_attribute

id item_id name value
1 1 foo bar
2 1 size M
3 1 country DE
4 2 foo baz
5 2 size M
6 2 country DE
7 3 other test
8 3 size M
9 3 country EN

Now, I need to find items that match a given set of attribute name/value pairs

So, let's say I want to find items with attributes of country with value DE and size with value M the result set should be

id name
1 A
2 B

if I want to find items with attributes of country with value EN and size with value M the result set should be

id name
3 C

Any hints here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

恬淡成诗 2025-02-13 17:28:35

使用聚合方法,我们可以尝试:

SELECT i.id, i.name
FROM item i
INNER JOIN item_attribute ia
    ON ia.item_id = i.id
GROUP BY i.id, i.name
HAVING COUNT(*) FILTER (WHERE ia.name = 'country' AND ia.value = 'DE') > 0 AND
       COUNT(*) FILTER (WHERE ia.name = 'size' AND ia.value = 'M') > 0;

上述使用条件聚合以中等大小的饮食项目。

Using an aggregation approach we can try:

SELECT i.id, i.name
FROM item i
INNER JOIN item_attribute ia
    ON ia.item_id = i.id
GROUP BY i.id, i.name
HAVING COUNT(*) FILTER (WHERE ia.name = 'country' AND ia.value = 'DE') > 0 AND
       COUNT(*) FILTER (WHERE ia.name = 'size' AND ia.value = 'M') > 0;

The above uses conditional aggregation to find items from Germnay in medium size.

十级心震 2025-02-13 17:28:35

您可以在“ item_attribute ”表中找到满足您条件的所有“ item_id ”之间的交点,然后匹配“ item “表。

WITH filtered_ids AS (
    SELECT item_id FROM item_attribute WHERE name  = 'country' AND value = 'DE'
    INTERSECT
    SELECT item_id FROM item_attribute WHERE name  = 'size'    AND value = 'M'
)
SELECT * 
FROM item
WHERE id IN (SELECT * FROM filtered_ids)

您可以在“ filtered_ids ” CTE中堆叠尽可能多的条件。

检查演示在这里

You can find the intersection between all "item_id"s that satisfy your conditions inside the "item_attribute" table, then match the corresponding value inside the "item" table.

WITH filtered_ids AS (
    SELECT item_id FROM item_attribute WHERE name  = 'country' AND value = 'DE'
    INTERSECT
    SELECT item_id FROM item_attribute WHERE name  = 'size'    AND value = 'M'
)
SELECT * 
FROM item
WHERE id IN (SELECT * FROM filtered_ids)

You can stack as many conditions as you want inside the "filtered_ids" cte.

Check the demo here.

绿萝 2025-02-13 17:28:35

一个选项是将属性汇总到JSON值中,并使用功能非常强大的要找到这些项目,

select itm.*
from item itm
where exists (select null
              from item_attribute att
              where att.item_id = itm.id
              group by item_id
              having jsonb_object_agg(name, value) @> '{"country": "DE", "size": "M"}'
              )

我不确定这比计数的传统方法更快还是慢(请参阅Tim Biegeleisen的答案)。不过,这更灵活。

根据数据分配,对所有项目进行一次聚合可能更快:

select itm.*
from item itm
  join (
    select item_id, jsonb_object_agg(name, value) as attributes
    from item_attribute att
    group by item_id
    having jsonb_object_agg(name, value) @> '{"country": "DE", "size": "M"}'
  ) atr on atr.item_id = itm.id

这也使结果可以在结果的列中显示所有属性(作为JSON)。

One option is to aggregate the attributes into a JSON value and use the quite powerful JSON operators to find the items

select itm.*
from item itm
where exists (select null
              from item_attribute att
              where att.item_id = itm.id
              group by item_id
              having jsonb_object_agg(name, value) @> '{"country": "DE", "size": "M"}'
              )

I am not not sure if this is faster or slower than the traditional approach with counting (see Tim Biegeleisen's answer). It is a bit more flexible though.

Depending on the data distribution, doing the aggregation once for all items might be faster:

select itm.*
from item itm
  join (
    select item_id, jsonb_object_agg(name, value) as attributes
    from item_attribute att
    group by item_id
    having jsonb_object_agg(name, value) @> '{"country": "DE", "size": "M"}'
  ) atr on atr.item_id = itm.id

This also enables to display all attributes (as JSON) in a column of the result.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文