这将是显著更容易,如果在MySQL支持的窗口函数,因为它不一定是最容易为每个userid
行号沿着数据设置。我将向您展示两种使用硬编码查询(结果数量有限)的方法,然后我将包含一个使用动态SQL的版本。
为了获得最终结果,您需要在userid
内的每个gname
有一些连续编号。这可以通过几种不同的方式来完成。
首先,你可以使用相关子查询到count
每个用户gname
S上的号码,你会再使用这个序列通过聚合函数CASE
表达式来创建新的列:
select
userid,
max(case when gnameNum = 1 then gname else '' end) gname1,
max(case when gnameNum = 2 then gname else '' end) gname2,
max(case when gnameNum = 3 then gname else '' end) gname3,
max(case when gnameNum = 4 then gname else '' end) gname4,
max(case when gnameNum = 5 then gname else '' end) gname5,
max(case when gnameNum = 6 then gname else '' end) gname6,
max(case when gnameNum = 7 then gname else '' end) gname7,
max(case when gnameNum = 8 then gname else '' end) gname8
from
(
select userid,
gname,
(select count(*)
from gnames d
where g.userid = d.userid
and g.gname <= d.gname) as gnameNum
from gnames g
) src
group by userid;
见SQL Fiddle with Demo。在子查询中,您将为每个gname
创建一个行号,然后在列创建中使用此新值。相关子查询的问题是您可能会遇到大数据集上的性能问题。
第二种方法是包含用户变量以创建行号。如果userid
与前一行相同,则此代码使用2个变量将前一行与当前行进行比较,并增加行号。再次,您将使用创建的行号将数据转换为新列:
select
userid,
max(case when rownum = 1 then gname else '' end) gname1,
max(case when rownum = 2 then gname else '' end) gname2,
max(case when rownum = 3 then gname else '' end) gname3,
max(case when rownum = 4 then gname else '' end) gname4,
max(case when rownum = 5 then gname else '' end) gname5,
max(case when rownum = 6 then gname else '' end) gname6,
max(case when rownum = 7 then gname else '' end) gname7,
max(case when rownum = 8 then gname else '' end) gname8
from
(
select
g.userid,
g.gname,
@row:=case when @prev=userid then @row else 0 end + 1 as rownum,
@prev:=userid
from gnames g
cross join
(
select @row:=0, @prev:=null
) r
order by userid, gname
) src
group by userid;
请参阅SQL Fiddle with Demo。
现在,为了动态地执行此操作,您需要使用prepared statement。这个过程将创建一个sql字符串,您将执行以获得最终结果。对于此示例,我使用上面的用户变量查询:
SET @sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when rownum = ',
rownum,
' then gname else '''' end) AS `gname',
rownum, '`'
)
) INTO @sql
from
(
select
g.userid,
g.gname,
@row:=case when @prev=userid then @row else 0 end + 1 as rownum,
@prev:=userid
from gnames g
cross join
(
select @row:=0, @prev:=null
) r
order by userid, gname
) src;
SET @sql = CONCAT('SELECT userid, ', @sql, '
from
(
select
g.userid,
g.gname,
@row:=case when @prev=userid then @row else 0 end + 1 as rownum,
@prev:=userid
from gnames g
cross join
(
select @row:=0, @prev:=null
) r
order by userid, gname
) src
group by userid');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
请参阅SQL Fiddle with Demo。所有这三个版本会给出一个结果:
| USERID | GNAME1 | GNAME2 | GNAME3 | GNAME4 | GNAME5 | GNAME6 | GNAME7 | GNAME8 |
|--------|--------|--------|--------|--------|--------|--------|--------|--------|
| 12 | Aew1 | ASD | ASD23 | ASDer | AVBD | AVBD32 | AVBDe | AVFD |
| 45 | ASD44 | ASD444 | AVBD | AVBD44 | | | | |
| 453 | ASD1 | AVBD22 | | | | | | |
有一点要考虑使用动态SQL时,MySQL有对group_concat_max_len
集的长度,如果你创建了很多列,你可能会遇到的问题。你会想说明这一点。这是另一个处理这个MySQL and GROUP_CONCAT() maximum length的问题。
在链接的答案中,您可以使用列'gname'代替'order',并且'Userid'代替'ID'。 – 2014-10-16 18:32:02
'data'列如何? – MT467 2014-10-16 18:50:51
对不起 - 它看起来像使用'gname'代替'data','Userid'代替'ID',并且只是省略'order'。我会尽力建立一个类似的例子在sqlfiddle ... – 2014-10-16 18:53:38