【问题标题】:Dynamically create columns from rows in MySQL [duplicate]从 MySQL 中的行动态创建列 [重复]
【发布时间】:2014-02-10 02:17:34
【问题描述】:

我有以下表格:

"crawlresults"
id  |   url                 | fk_crawljobs_id
---------------------------------------------
1   |   shop*com/notebooks  |   1
2   |   shop*com/fridges    |   1
3   |   website*com/lists   |   2


"extractions"
id  | fk_extractors_id  | data          |   fk_crawlresults_id
---------------------------------------------------------------
1   |   1               | 123.45        |   1
2   |   2               | notebook      |   1
3   |   3               | ibm.jpg       |   1
4   |   1               | 44.5          |   2
5   |   2               | fridge        |   2
6   |   3               | picture.jpg   |   3
7   |   4               | hello         |   3
8   |   4               | world         |   3
9   |   5               | hi            |   3
10  |   5               | my            |   3
11  |   5               | friend        |   3


"extractors"
id  |   extractorname
----------------------
1   |   price
2   |   article
3   |   imageurl
4   |   list_1
5   |   list_2

我需要构造一个 select 语句来为提取表中使用的提取器表中的每个提取器获取列。

例子:

url                 | price     | article   | imageurl
--------------------------------------------------------
shop*com/notebooks  | 123.45    | notebook  | ibm.jpg
shop*com/fridges    | 44.5      | fridge    | NULL

我不知道执行 select 语句时存在多少提取器名称,因此必须动态构建它。

编辑: 我忘了提到我的提取中可能有多个“列表”。在这种情况下,我需要以下结果集。

示例 2:

url                 | list_1    | imageurl      | list_2
--------------------------------------------------------
website*com/lists   | hello     | picture.jpg   | NULL
website*com/lists   | world     | picture.jpg   | NULL
website*com/lists   | NULL      | picture.jpg   | hello
website*com/lists   | NULL      | picture.jpg   | my
website*com/lists   | NULL      | picture.jpg   | friend

谢谢!

【问题讨论】:

  • 另一个类似的问题stackoverflow.com/q/7674786/684229
  • MySQL 中没有数据透视查询。事实上,这些类型的查询不属于数据库,因为它们的结果本身不是表;枢轴转换属于数据库顶部的应用层。
  • @Tomas,检查我的答案,你所需要的只是一份准备好的陈述和一些爱。不需要把应用层放在这上面:)。
  • @AnthonyAccioly 看起来不像是执行数据透视查询的好方法。我认为更可行的方法是在 Java/PHP/ASP/C#/whatever 中找到一些枢轴转换库(类似于 R 中的cast 函数 - 非常简单的用法),而不是一遍又一遍地调试一些复杂的 SQL :-)跨度>
  • @AnthonyAccioly 我认为应用层正是它所属的地方。

标签: mysql sql pivot crosstab


【解决方案1】:

您正在寻找Dynamic pivot tables

代码:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'MAX(IF(pa.extractorname = ''',
      extractorname,
      ''', p.data, NULL)) AS ',
      extractorname
    )
  ) INTO @sql
FROM extractors;

SET @sql = CONCAT('SELECT c.url, ', 
  @sql, 
  ' FROM crawlresults c', 
  ' INNER JOIN extractions p on (c.id = p.fk_crawlresults_id)', 
  ' INNER JOIN extractors pa on (p.fk_extractors_id = pa.id)'
  ' WHERE c.fk_crawljobs_id = 1',
  ' GROUP BY c.id');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Working fiddle


基本上,您的原始查询生成了一个虚假的@sql 变量,它并没有真正为每个extractorname 提取data。您也不需要所有这些连接来创建@sql。您只需要每个属性名称(来自extractor 表)和对包含预期值的列的引用(data)。

当对结构有疑问时,为一组固定的属性编写一个简单的透视查询。这样就很容易识别编写动态查询的模式。

SELECT c.url, 
  MAX(IF(pa.extractorname = 'price', p.data, NULL)) AS price,
  MAX(IF(pa.extractorname = 'article', p.data, NULL)) AS article,
  MAX(IF(pa.extractorname = 'imageurl', p.data, NULL)) AS imageurl 
FROM crawlresults c 
  LEFT JOIN extractions p on (c.id = p.fk_crawlresults_id) 
  LEFT JOIN extractors pa on (p.fk_extractors_id = pa.id) 
WHERE c.fk_crawljobs_id = 1
GROUP BY c.id

至于查询的其余部分,这很好,请记住,如果某些crawlresults 没有extractionsLEFT JOINS 可能会很有用。此外,如果您的表中每个url / fk_crawljobs_id 可以包含多个crawlresult,则按url 分组是个坏主意(MAX 可能会混合来自多个extractions 的结果。

【讨论】:

  • 当我在提取器表中有一个未在提取表中链接的附加提取器名称时,它仍然成为结果集中的一列。另一个问题是当我有一个或多个提取列表时。我已经相应地编辑了我的问题。
  • Jimbo,关于链接很简单,只过滤与该查询相关的extractorname (working fiddle),只需相应地更新创建@sql 的子句。跨度>
  • Masterpiece fiddle 现在在多值行上重复单值结果。
  • 最后的小提琴正是我所需要的。非常感谢你做的这些! :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-05
  • 1970-01-01
  • 2016-05-10
  • 1970-01-01
  • 1970-01-01
  • 2022-12-01
  • 1970-01-01
相关资源
最近更新 更多