Mysql-PHP,基于变量字段的旋转/交叉制表符

时间:2013-01-10 22:45:11

标签: php mysql variables pivot crosstab

大家好,并提前感谢您的帮助!

我正在尝试根据返回的查询创建交叉表类型查询结果。 第一个查询:

SELECT DISTINCT
dc.`name`,
Count(pd.dispositionCodeId) AS NumberOfDispos
FROM
pd

 JOIN ph ON (pd.packetHeaderId = ph.packetHeaderId)
 JOIN sc ON (sc.packetHeaderId = ph.packetHeaderId)
 JOIN dc ON pd.dispositionCodeId = dc.dpcodeId
WHERE (`ph`.`customerId` = 60)
GROUP BY
dc.`name`

导致:

Name NumberofDispos
B      400
NO     245
S      134
V      98

- 我返回的name是可变的,具体取决于customerId,也取决于其他类似查询中出现的其他where子句。

有没有办法将name字段的结果作为第二个表中的列标题进行数据转换,第二个查询将计算在另一个匹配字段中显示的特定名称的出现次数?

        B   NO  S   V 
PackA   250 120 61  22
PackB   100 23  62  37
PackC   50  102 11  39

请记住,列标题是根据第一个查询的结果而变化的。

据我所知,我需要制作一个php数组并遍历sql结果

3 个答案:

答案 0 :(得分:2)

这种类型的查询称为 pivot ,但遗憾的是MySQL没有pivot函数,因此您需要使用带有CASE语句的聚合函数来复制它。

如果提前知道这些值,那么您可以将值硬编码为以下内容:

SELECT pd.dispositionCodeId,
  sum(case when dc.`name` = 'B' then 1 else 0 end) B,
  sum(case when dc.`name` = 'NO' then 1 else 0 end) NO,
  sum(case when dc.`name` = 'S' then 1 else 0 end) S,
  sum(case when dc.`name` = 'V' then 1 else 0 end) V
FROM pd
JOIN ph 
  ON pd.packetHeaderId = ph.packetHeaderId
JOIN sc 
  ON sc.packetHeaderId = ph.packetHeaderId
JOIN dc 
  ON pd.dispositionCodeId = dc.dpcodeId
WHERE (`ph`.`customerId` = 60)
GROUP BY pd.dispositionCodeId;

但是如果names的值或数量未知,那么您可以使用预准备语句来创建要执行的dyanmic sql语句。代码如下所示:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'sum(case when dc.`name` = ''',
      name,
      ''' then 1 else 0 end) AS ',
      name
    )
  ) INTO @sql
FROM dc;

SET @sql = CONCAT('SELECT pd.dispositionCodeId, ', @sql, ' 
                   FROM pd
                   JOIN ph 
                     ON pd.packetHeaderId = ph.packetHeaderId
                   JOIN sc 
                     ON sc.packetHeaderId = ph.packetHeaderId
                   JOIN dc 
                     ON pd.dispositionCodeId = dc.dpcodeId 
                   WHERE `ph`.`customerId` = 60
                   GROUP BY pd.dispositionCodeId');

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

答案 1 :(得分:1)

这应该适合你:

SELECT 
  pd.dispositionCodeId,
  SUM(CASE WHEN dc.`Name` = 'B' THEN 1 ELSE 0 END 0) AS B,
  SUM(CASE WHEN dc.`Name` = 'NO' THEN 1 ELSE 0 END 0) AS NO,
  SUM(CASE WHEN dc.`Name` = 'S' THEN 1 ELSE 0 END 0) AS S,
  SUM(CASE WHEN dc.`Name` = 'V' THEN 1 ELSE 0 END 0) AS V
FROM pd
JOIN ph ON (pd.packetHeaderId = ph.packetHeaderId)
JOIN sc ON (sc.packetHeaderId = ph.packetHeaderId)
JOIN dc ON pd.dispositionCodeId = dc.dpcodeId
WHERE (`ph`.`customerId` = 60)
GROUP BY pd.dispositionCodeId

答案 2 :(得分:0)

如果提供的答案不合适,因为您事先并不知道name字段的值并且不想使用准备好的语句,则可以随时添加另一级别的分组并执行应用程序代码中的“pivot”。选择可能看起来像

SELECT
dc.`name` as `name`,
dc.`other_field` as `other_field`,
Count(pd.dispositionCodeId) AS NumberOfDispos
FROM
pd
 JOIN ph ON (pd.packetHeaderId = ph.packetHeaderId)
 JOIN sc ON (sc.packetHeaderId = ph.packetHeaderId)
 JOIN dc ON pd.dispositionCodeId = dc.dpcodeId
WHERE (`ph`.`customerId` = 60)
GROUP BY
dc.`name`,
dc.`other_field`
ORDER BY
dc.`name` ASC,
dc.`other_field` ASC

然后,您可以在PHP中构建数据透视表所需的二维数组(假设此处为PDO,但基本要点与通过其他库迭代结果集相同):

$final_array = array();
while($row = $statement->fetchObject()) {
    $final_array[$row->name][$row->other_field] = $row->NumberOfDispos;
}