MySQL - Select中的SubQuery

时间:2013-07-10 19:43:34

标签: mysql subquery pivot-table entity-attribute-value

我不确定这是否可行,而且描述起来肯定很难。

我想以这种方式返回SELECT查询:

pID | Name | Email            | Dog Name   | Fish Name
-------------------------------------------------------
44  | John | john@email.com   | Scruffy   | 
56  | Jane | jane@email.com   |           | Puffer
72  | Joe  | joe@email.com    |  Sparky   | Gill

共有3个表格: tbl_option_fields tbl_person tbl_person_optionals

第一个表只是定义无限的可选字段。以下是专栏:

optid | opttitle
------------------
1     | Dog Name
2     | Fish Name
3     | Email

第二个表格就是我为人们保存数据的地方:

pID |  Name
---------------
44   | John 
56   | Jane 
72   | Joe  

最后一个表让我存储pID

存在的任何可选数据
ID |  pID  | optid  | optval
----------------------------------
1   | 44   |   1    | Scruffy
2   | 56   |   2    | Puffer
3   | 72   |   1    | Sparky
4   | 72   |   2    | Gill 
5   | 72   |   3    | joe@email.com
6   | 56   |   3    | jane@email.com
7   | 44   |   3    | john@email.com

SELECT语句的结果需要在结果的标题中显示opttitle。 我不擅长子查询,这可能是一个很糟糕的。

这是我的尝试:

SELECT p.*, opt.optval
FROM `tbl_person` p
LEFT OUTER JOIN `tbl_person_optionals` opt ON p.pID = opt.pID 
WHERE 1 

此查询会导致重复的结果,我相信我理解为什么。

pID | Name | optval            
-----------------------------
44  | John | john@email.com   
44  | John | Scruffy   

56  | Jane | jane@email.com
56  | Jane | Puffer

72  | Joe  | joe@email.com  
72  | Joe  | Sparky  
72  | Joe  | Gill

希望我在解释我想要水平显示可选字段,而不是行中的新记录。 可以这样做吗?

3 个答案:

答案 0 :(得分:0)

你基本上想要的是一个数据透视表。这可以在MySQL中完成,如this article describes in full, glorious detail

但是,为了使用当前的表格设计来达到您想要的效果,您必须为表格SELECT中的每个选项添加tbl_option_fields - 运算符。只有少数几个选项,这是可以管理的,但是你拥有的选项越多,你的陈述就会越混乱(最终会越慢)。

另一个缺点是,每当新选项出现时,您的陈述必须进行调整。

基本上你不得不忍受它或调整你的桌子设计。可能还有一些选择,但我想这可以归结为这两种选择。

答案 1 :(得分:0)

这是一个解决方案:

SELECT p.pID, p.Name, 
  MAX(IF(opt.optid=3, opt.optval, NULL)) AS `Email`,
  MAX(IF(opt.optid=1, opt.optval, NULL)) AS `Dog Name`,
  MAX(IF(opt.optid=2, opt.optval, NULL)) AS `Fish Name`
FROM `tbl_person` p
LEFT OUTER JOIN `tbl_person_optionals` opt ON p.pID = opt.pID 
GROUP BY p.pID;

在编写此查询之前,您必须知道所有optid及其列标签。这对MySQL来说也是如此,但对于支持本机PIVOT命令(例如Microsoft SQL Server)的RDBMS品牌也是如此。 SQL要求您在开始检查数据之前定义查询的列;查询无法添加"更多列,因为它发现不同的数据值。

因此,您应该查询定义可选字段的第一个表,并使用结果动态编写上面的SQL查询。

另一种方法是逐行获取可选字段,如您在示例中所示,然后编写应用程序代码以获取所有行并将其重新排列为每人一行。

无论哪种方式:任何动态数据透视查询都要求您在查询之前或查询之后编写更多应用程序代码。

答案 2 :(得分:0)

您可以通过将表连接在一起并汇总结果来完成此操作:

select po.pid, p.name,
       MAX(case when o.optId = 'Email' then po.optval end) as Email,
       MAX(case when o.optId = 'Dog Name' then po.optval end) as DogName,
       MAX(case when o.optId = 'Fish Name' then po.optval end) as FishName
from tbl_person_optionals po join
     tbl_person p
     on p.pId = p.pId join
     tbl_optionfields o
     on p.optid = o.optid
group by po.pid, p.name;

这假定您知道列中的内容。 Select语句只能返回一组固定的列,因此如果要为tbl_optionfields中的每个值返回一个单独的列,则需要动态SQL(即使用prepare语句)。