使用多表子句将多个行值选择为单行

Posted

技术标签:

【中文标题】使用多表子句将多个行值选择为单行【英文标题】:Select multiple row values into single row with multi-table clauses 【发布时间】:2015-04-11 05:19:23 【问题描述】:

我搜索了论坛,虽然我看到了类似的帖子,但它们只解决了我需要制定的完整查询的部分内容(array_aggr、存在的位置、连接等)。如果我发布的问题已得到解答,我将很乐意接受对这些主题的引用。

我确实找到了this thread ...这与我需要的非常相似,除了它是用于 mysql 的,而且我一直在尝试将其转换为 psql 语法时遇到错误。希望有人可以帮助我把所有东西都放在一起。这是场景:

属性

attrib_id | attrib_name

用户属性

user_id | attrib_id | value

这是数据的一个小例子:

属性

attrib_id | attrib_name
-----------------------
1         | attrib1
2         | attrib2
3         | attrib3
4         | attrib4
5         | attrib5

UserAttribute -- 每个 user_id 最多可以有 15 个 attrib_id's/value's

user_id | attrib_id | value
----------------------------
101     | 1         | valueA
101     | 2         | valueB
102     | 1         | valueC
102     | 2         | valueD
103     | 1         | valueA
103     | 2         | valueB
104     | 1         | valueC
104     | 2         | valueD
105     | 1         | valueA
105     | 2         | valueB

这就是我要找的东西

结果

user_id    | attrib1_value | attrib2_value
--------------------------------------------------------
101        | valueA        | valueB
102        | valueC        | valueD
103        | valueA        | valueB
104        | valueC        | valueD
105        | valueA        | valueB

如图所示,我正在寻找包含以下内容的单行: - 来自 UserAttribute 表的 user_id - UserAttribute 表中的属性值

注意:对于 Attribute 表中的两个特定属性名称,我只需要 UserAttribute 表中的属性值

再次感谢任何对现有解决方案的帮助或参考。


更新:

@ronin 提供了一个获得所需结果的查询:

SELECT ua.user_id
      ,MAX(CASE WHEN a.attrib_name = 'attrib1' THEN ua.value ELSE NULL END) AS attrib_1_val
      ,MAX(CASE WHEN a.attrib_name = 'attrib2' THEN ua.value ELSE NULL END) AS attrib_2_val
  FROM UserAttribute ua
  JOIN Attribute a ON (a.attrib_id = ua.attrib_id)
  WHERE a.attrib_name IN ('attrib1', 'attrib2')
  GROUP BY ua.user_id;

为此,我尝试在 'WHEN' 条件(针对 ua.value)中添加一些 'LIKE' 模式匹配,但一切都以 'FALSE' 值结束。如果我无法弄清楚,将开始一个新问题,看看是否可以合并。谢谢大家的帮助!!

【问题讨论】:

您遇到了什么错误?您尝试的查询是什么?请编辑您的问题以显示这些内容 听起来你想要某种数据透视表。 我不确定我是否理解.. 如果客户有 15 个属性,您需要 15 列吗? @Bohemian - 我遇到了 group by 和无效的参考错误。我没有运气走嵌套子查询/连接路线。 @JoeLove - 我不需要全部 15 个,只需要 2 个属性。 【参考方案1】:

如果每个属性对于一个用户只有一个值,你可以从制作一个稀疏矩阵开始:

SELECT user_id
      ,CASE WHEN attrib_id = 1 THEN value ELSE NULL END AS attrib_1_val
      ,CASE WHEN attrib_id = 2 THEN value ELSE NULL END AS attrib_2_val
  FROM UserAttribute;

然后使用聚合函数压缩矩阵:

SELECT user_id
      ,MAX(CASE WHEN attrib_id = 1 THEN value ELSE NULL END) AS attrib_1_val
      ,MAX(CASE WHEN attrib_id = 2 THEN value ELSE NULL END) AS attrib_2_val
  FROM UserAttribute
  GROUP BY user_id;

回复评论,按属性名而不是id搜索:

SELECT ua.user_id
      ,MAX(CASE WHEN a.attrib_name = 'attrib1' THEN ua.value ELSE NULL END) AS attrib_1_val
      ,MAX(CASE WHEN a.attrib_name = 'attrib2' THEN ua.value ELSE NULL END) AS attrib_2_val
  FROM UserAttribute ua
  JOIN Attribute a ON (a.attrib_id = ua.attrib_id)
  WHERE a.attrib_name IN ('attrib1', 'attrib2')
  GROUP BY ua.user_id;

【讨论】:

这是一个很好的起点!不确定我会自己到这里。根据属性表中的 attrib_name 进行过滤会复杂多少(因为我正在查询的数据库实例之间的 ID 可能不同)?另外,要添加“如果值包含'字符串',则返回此值”? 我尝试在 'WHEN' 条件(针对 ua.value)中添加一些 'LIKE' 模式匹配,但一切都以 'FALSE' 值结束。在这种情况下甚至可以这样做吗?如果没有,我将处理 php 代码中的正则表达式。 @vmoralito:几乎任何事情都是可能的。但请不要在 cmets 中提问。更新您的问题或开始一个新问题。【参考方案2】:

从 Postgres 9.4 开始你可以使用更简单的aggregate FILTER clause:

SELECT user_id
      ,MAX(value) FILTER (WHERE attrib_id = 1) AS attrib_1_val
      ,MAX(value) FILTER (WHERE attrib_id = 2) AS attrib_2_val
FROM   UserAttribute
WHERE  attrib_id IN (1,2)
GROUP  BY 1;

要获得更多属性或最佳性能,请查看附加模块 tablefunc(Postgres 8.3+)中的 crosstab()。详情在这里:

PostgreSQL Crosstab Query

【讨论】:

我现在锁定在 9.1.3(支持供应商),但感谢您的评论。我会标记参考文献。【参考方案3】:

这样的事情怎么样:

select ua.user_id, a.attrib_name attrib_value1, a2.attrib_name attrib_value2
from user_attribute ua
left join attribute a on a.atribute_id=ua.attribute_id and a.attribute_id in (1,2)
left join user_attribute ua2 on ua2.user_id=ua.user_id and ua2.attribute_id > ua.attribute_id
left join attribute a2 on a2.attribute_id=ua2.attribute_id and a2.attribute_id in (1,2)

【讨论】:

在调整以匹配实际表名后试一试,但我收到此错误:错误:a.value 列不存在第 1 行:选择 ua.user_id、a.value attrib_value1、a2。 attrib_value_2 不得不在 SELECT 中添加一些逗号,并再次重命名表,但现在我得到了这个:错误:a.atribute_id 列不存在第 3 行:在 a.atribute_id 上左加入 ccs_attribute a =ua.attribute_id a... 我不得不再做一些编辑,试图让它发挥作用。此查询执行,但我没有得到预期的结果:select ua.user_id, a.name, ua.value, a2.name, ua2.value from ccs_user_attribute ua left join ccs_attribute a on a.attribute_id=ua.attribute_id and a.attribute_id in (1,2) left join ccs_user_attribute ua2 on ua2.user_id=ua.user_id and ua2.attribute_id > ua.attribute_id left join ccs_attribute a2 on a2.attribute_id=ua2.attribute_id and a2.attribute_id in (1,2); 这也使我无法知道 attrib_id,它在实例之间确实有所不同。 试图弄清楚你在这里做什么select ua.user_id, a.attrib_name attrib_value1, a2.attrib_name a2.attrib_value_2 ,因为我得到一个语法错误。请注意,“attrib_value1”在任何地方都不作为列存在,这正是我希望结果表显示的内容。 我有一个错字——已修复。也许如果我们有一个 SQLfiddle 可以玩它会更容易。希望这个能解决,我只是对同一个表进行几次连接以获取每个属性。很抱歉长时间延迟回复。

以上是关于使用多表子句将多个行值选择为单行的主要内容,如果未能解决你的问题,请参考以下文章

多表搜索,带有限制子句和已知结果源表名称

将另一个表中的逐行值转换为字符串并将其插入到 SQL 中的 NOT IN 子句中

根据行值python将大型csv文件拆分为多个文件

如何在 Laravel 8 中使用 PostgreSQL 中的子查询通过 group by 子句获取行值?

PHP MySQL根据列/行值选择多个表

使用字符串生成器或存储过程进行单行选择