MySQL动态地从行中创建列

MySQL动态地从行中创建列,mysql,pivot,Mysql,Pivot,我的问题是我有一个表apidata,它为每个域保存多行数据。因此,当我查询apidata时,自然会得到多行结果。有没有办法把这些行变成列?我这样问是因为我已经在使用一个查询来提取域数据(页面标题、URL、顶级域、ip地址等),我需要添加api数据。我相信我必须在两个查询中完成这项工作,但我希望每个域至少有一行,以使查询和循环尽可能快 所以问题是,我可以用行动态创建列吗? 这里有一个SQL Fiddle=> (注意,我没有把整个数据库放在小提琴上,只是放在影响查询的表上。如果您认为缺少一些您需要的

我的问题是我有一个表
apidata
,它为每个域保存多行数据。因此,当我查询
apidata
时,自然会得到多行结果。有没有办法把这些行变成列?我这样问是因为我已经在使用一个查询来提取域数据(页面标题、URL、顶级域、ip地址等),我需要添加api数据。我相信我必须在两个查询中完成这项工作,但我希望每个域至少有一行,以使查询和循环尽可能快

所以问题是,我可以用行动态创建列吗?

这里有一个SQL Fiddle=>

(注意,我没有把整个数据库放在小提琴上,只是放在影响查询的表上。如果您认为缺少一些您需要的东西,请告诉我。)

工具运行(id\u sha是工具运行的主要查找值)

(Run\u id对于tool\u runs.id是FK)

API数据

| ID | DOMAIN_ID | EXPORT_COLUMN    | COLUMN_TITLE      | VALUE |
+----+-----------+------------------+-------------------+-------+
| 1  | 1         | referringDomains | Referring Domains | 10    |
+----+-----------+------------------+-------------------+-------+
| 2  | 1         | linkCount        | Backlink Count    | 55    |
下面是我的问题:

SELECT a.domain_id, a.export_column, a.column_title, a.value 
FROM apidata AS a WHERE domain_id IN
(
  SELECT d.id FROM tool_runs AS t
  JOIN domains AS d ON d.run_id = t.id
  WHERE id_sha = '68300DF58B2A8A6E098CB0B3D1A9AE80BBE5897A'
)
ORDER BY a.domain_id
我得到的是:

| DOMAIN_ID | EXPORT_COLUMN    | COLUMN_TITLE      | VALUE    |
+-----------+------------------+-------------------+----------+
| 1         | referringDomains | Referring Domains | 10       |
+-----------+------------------+-------------------+----------+
| 1         | linkCount        | Backlink Count    | 55       |
但我想要的是

| DOMAIN_ID | referringDomains | referringDomains_TITLE | linkCount | linkCount_TITLE |
+-----------+------------------+------------------------+-----------+-----------------+
| 1         | 10               | Referring Domains      | 55        | Backlink Count  |

您尝试的是将表的行透视到列中。不幸的是,MySQL没有本机pivot表运算符,但您可以使用
CASE
表达式来执行此操作:

SELECT 
  a.Domain_id,
  MAX(CASE WHEN a.export_column = 'referringDomains' THEN a.value END) AS referringDomains,
  MAX(CASE WHEN a.export_column = 'referringDomains' THEN a.column_title END) AS referringDomains_TITLE,
  MAX(CASE WHEN a.export_column = 'linkCount' THEN a.value END) AS linkCount,
  MAX(CASE WHEN a.export_column = 'linkCount' THEN a.column_title END) AS linkCount_TITLE
FROM apidata AS a 
WHERE domain_id IN
(
  SELECT d.id FROM tool_runs AS t
  JOIN domains AS d ON d.run_id = t.id
  WHERE id_sha = '68300DF58B2A8A6E098CB0B3D1A9AE80BBE5897A'
) 
GROUP BY a.domain_id;

请注意:如果要对
export\u列中的所有值执行此操作,必须为每个值编写一个
大小写表达式。但您可以使用动态sql这样做:

SET @ecvalues = NULL;
SET @ectitles = NULL;
SET @sql = NULL;

SELECT
  GROUP_CONCAT(DISTINCT CONCAT('MAX(IF(a.export_column = ''',
      a.export_column, ''', a.value , NULL)) AS ', '''', a.export_column , '''')
  ) INTO @ecvalues
FROM apidata a;


SELECT
  GROUP_CONCAT(DISTINCT CONCAT('MAX(IF(a.export_column = ''',
      a.export_column, ''', column_title , NULL)) AS ', '''', CONCAT(a.export_column , '_Titles'), '''')
  ) INTO @ectitles
FROM apidata a;


SET @sql = CONCAT('SELECT 
  a.Domain_id, ', @ectitles , ',', @ecvalues, '
    FROM apidata AS a 
WHERE domain_id IN
(
  SELECT d.id FROM tool_runs AS t
  JOIN domains AS d ON d.run_id = t.id
  WHERE id_sha = ''68300DF58B2A8A6E098CB0B3D1A9AE80BBE5897A''
) 
GROUP BY a.domain_id;');

prepare stmt 
FROM @sql;

execute stmt;
可以将该查询放入存储过程中


作为@MahmoudGamal答案的补充,您应该知道,对于任何新的注册表(
EXPORT\u COLUMN
),您必须添加一个新的
case
语句

因此,为了实现动态,您可以在dba.stackexchange上创建一个过程,如本文所述


它显示了如何动态执行此操作。

如果您想要列,请按照上面的示例继续并旋转。如果出于某种报告原因,您只需要一个字符串,请继续执行以下操作:

SELECT group_concat(CONCAT_WS(' ',a.domain_id, a.value, a.column_title, a.export_column, 'next row string separator'))
FROM apidata AS a WHERE domain_id IN
(
  SELECT d.id FROM tool_runs AS t
  JOIN domains AS d ON d.run_id = t.id
  WHERE id_sha = '68300DF58B2A8A6E098CB0B3D1A9AE80BBE5897A'
)
ORDER BY a.domain_id

除了答案,仅供参考Rachel,技术术语是数据透视或数据透视表,以备进一步研究。MySQL还有
IF()
函数,它比单个条件'case'语句稍微简单一点。@fancyPants谢谢你,我确实想研究一下,因为它会很方便:)一般来说,出于灵活性和可伸缩性的考虑,最好在应用程序级别处理数据显示问题——假设您有一个(例如,一个简单的PHP循环作用于一个有序数组)
SET @ecvalues = NULL;
SET @ectitles = NULL;
SET @sql = NULL;

SELECT
  GROUP_CONCAT(DISTINCT CONCAT('MAX(IF(a.export_column = ''',
      a.export_column, ''', a.value , NULL)) AS ', '''', a.export_column , '''')
  ) INTO @ecvalues
FROM apidata a;


SELECT
  GROUP_CONCAT(DISTINCT CONCAT('MAX(IF(a.export_column = ''',
      a.export_column, ''', column_title , NULL)) AS ', '''', CONCAT(a.export_column , '_Titles'), '''')
  ) INTO @ectitles
FROM apidata a;


SET @sql = CONCAT('SELECT 
  a.Domain_id, ', @ectitles , ',', @ecvalues, '
    FROM apidata AS a 
WHERE domain_id IN
(
  SELECT d.id FROM tool_runs AS t
  JOIN domains AS d ON d.run_id = t.id
  WHERE id_sha = ''68300DF58B2A8A6E098CB0B3D1A9AE80BBE5897A''
) 
GROUP BY a.domain_id;');

prepare stmt 
FROM @sql;

execute stmt;
SELECT group_concat(CONCAT_WS(' ',a.domain_id, a.value, a.column_title, a.export_column, 'next row string separator'))
FROM apidata AS a WHERE domain_id IN
(
  SELECT d.id FROM tool_runs AS t
  JOIN domains AS d ON d.run_id = t.id
  WHERE id_sha = '68300DF58B2A8A6E098CB0B3D1A9AE80BBE5897A'
)
ORDER BY a.domain_id