SQL查询水平显示和连接

时间:2013-01-30 05:02:51

标签: php mysql sql pivot dynamic-sql

  

可能重复:
  Join two tables (with a 1-M relationship) where the second table needs to be ‘flattened’ into one row

虽然我参加了sql课程的介绍,但我现在无法想出这个。

我有两张桌子:

表1:

    user_id date  value
    ids     dates values

表2:

    user_id user_names
    ids     names

我想要一个显示以下结果的MYSQL查询:

    /   name1  name2  name3 etc
  date1 val1-1 val1-2 val1-3 
  date2 val2-1 val2-2 val2-3

我记得有办法做到这一点,对吧?任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:1)

以这种方式旋转数据称为 pivot 。不幸的是,MySQL没有pivot函数,所以你必须使用聚合函数和CASE表达式来复制它。

用于此查询的

示例数据

CREATE TABLE Table1
    (`user_id` int, `date` datetime, `value` int)
;

INSERT INTO Table1
    (`user_id`, `date`, `value`)
VALUES
    (1, '2013-01-01 00:00:00', 100),
    (2, '2013-01-01 00:00:00', 200),
    (1, '2013-01-02 00:00:00', 500)
;

CREATE TABLE Table2
    (`user_id` int, `user_names` varchar(4))
;

INSERT INTO Table2
    (`user_id`, `user_names`)
VALUES
    (1, 'John'),
    (2, 'Tim')
;

如果您知道要转换为列的所有值(例如names),那么您可以对它们进行硬编码,SQL将与此类似:

select 
  date,
  max(case when rownum = 1 then value end) as John,
  max(case when rownum = 2 then value end) as Tim
from
(
  select date, value, user_names,
    @row:=case when @prev=date then @row else 0 end + 1 as rownum,
    @prev:=date 
  from
  (
    select t1.date, t1.value, t2.user_names
    from table1 t1
    inner join table2 t2
      on t1.user_id = t2.user_id
    order by date, user_names
  ) d, (SELECT @row:=0, @prev:=null) r
  order by date, user_names
) src
group by date

SQL Fiddle with Demo。如您所见,我必须实现用户变量,以便为日期中的每个名称分配行号。这会告诉您将有多少个不同的名称值转换为列。

但是,对于您的情况,每个日期的名称数量都是未知的,因此您需要在prepared statement内使用动态SQL。

在这种情况下,代码将类似于:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'max(case when rownum = ',
      rownum,
      ' then value end) AS `',
      user_names, '`'
    )
  ) INTO @sql
from
(
  select date, value, user_names,
    @row:=case when @prev=date then @row else 0 end + 1 as rownum,
    @prev:=date 
  from
  (
    select t1.date, t1.value, t2.user_names
    from table1 t1
    inner join table2 t2
      on t1.user_id = t2.user_id
    order by date, user_names
  ) d, (SELECT @row:=0, @prev:=null) r
  order by date, user_names
) src;


SET @sql = CONCAT('SELECT date, ', @sql, ' 
                  from
                  (
                    select date, value, user_names,
                      @row:=case when @prev=date then @row else 0 end + 1 as rownum,
                      @prev:=date 
                    from
                    (
                      select t1.date, t1.value, t2.user_names
                      from table1 t1
                      inner join table2 t2
                        on t1.user_id = t2.user_id
                      order by date, user_names
                    ) d, (SELECT @row:=0, @prev:=null) r
                    order by date, user_names
                  ) src
                  group by date');

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

SQL Fiddle with Demo。两个版本都会给出结果:

|                           DATE | JOHN |    TIM |
--------------------------------------------------
| January, 01 2013 00:00:00+0000 |  100 |    200 |
| January, 02 2013 00:00:00+0000 |  500 | (null) |

答案 1 :(得分:0)

使用内部联接加入两个表并保存为视图,然后使用此作为原始数据,PIVOT结果集,您将获得您所追求的结果。连接,视图创建和PIVOT应该都有大量的代码示例,并且应该很容易在课程结束后进行。

答案 2 :(得分:0)

是的,有办法做到这一点............它叫做PIVOT表

您可以参考this一个。

<强> MySQL pivot tables (transform rows to columns)