我完成了我的作业,并在网上和StackOverflow中查找信息,但我没有找到我的问题的答案。我看了看:
Creating two colums from rows of a table(不明白) MySQL join same table display rows in columns way (complicated)(不同,问题似乎不一样) Creating two colums from rows of a table
我用它来构建我的查询:
How to Display rows as Columns in MySQL?
我有一个表格“课程”,如下所示:
ID title
---------
1 Math
2 Latin
3 English
4 Science
我有第二个表“结果”,用于存储每个课程的测试用户类型。目前有两种类型的测试:o和w(未来可能会有更多)。表格如下:
lesson_id usr_id test_type course_id
----------------------------------------
1 100 o 1
1 100 w 1
1 24 o 1
1 36 w 1
在上表中,用户100通过了测试o和w用于数学。 在上表中,用户24通过了Math的测试o。 在上表中,用户36通过了数学测试w。
现在我想为用户100获取一份报告,我希望有类似的内容:
ID title o w
------------------------------
1 Math TRUE TRUE
2 Latin FALSE FALSE
3 English FALSE FALSE
4 Science FALSE FALSE
对于用户36:
ID title o w
------------------------------
1 Math FALSE TRUE
2 Latin FALSE FALSE
...
对于用户24:
ID title o w
------------------------------
1 Math TRUE FALSE
2 Latin FALSE FALSE
...
任何其他用户:
ID title o w
------------------------------
1 Math FALSE FALSE
2 Latin FALSE FALSE
...
我可以使用以下查询完成此操作:
SELECT l.id, l.title, COALESCE(s.o,FALSE) AS o, COALESCE(t.w, FALSE) AS w FROM lessons l
LEFT JOIN (
SELECT r.lesson_id,
CASE
WHEN r.test_type='o' THEN TRUE
ELSE FALSE
END AS o
FROM results r WHERE r.itype='o' AND r.usr_id=100
) AS s ON l.id=s.lesson_id
LEFT JOIN (
SELECT rr.lesson_id,
CASE
WHEN rr.test_type='w' THEN TRUE
ELSE FALSE
END AS w
FROM results rr WHERE rr.test_type='w' AND rr.usr_id=100
) AS t ON l.id=t.lesson_id
WHERE l.course_id=1 ORDER BY l.id ASC;
虽然这段代码似乎有用(仍在忙着测试它),但我不喜欢它,因为它需要两个连接,每个连接都是由一个表中的选择组成,将来可能会变得非常大。此查询将经常运行
你能建议一个更好的方法吗(更好的意思是更聪明,更直接,更快或其他设计......或任何其他建议:-))?
如果我需要坚持这一点,您是否可以建议参考如何设置最佳索引以快速运行此查询?链接,您使用的文章?
在创建更多test_types的情况下会发生什么?我的查询仅使用w和o如果明天还有更多?是否有通用的方法来做这种事情?
非常欢迎任何帮助/建议,
菲利普
答案 0 :(得分:1)
您尝试执行的操作在其他数据库中称为PIVOT
。不幸的是,MySQL没有,所以你必须使用聚合函数和CASE
语句创建一个。
如果您知道只有两个test_type
值,那么您可以对此进行硬编码:
select l.id,
l.title,
max(case when test_type = 'o' then 'true' else 'false' end) as o,
max(case when test_type = 'w' then 'true' else 'false' end) as w
from lessons l
left join results r
on l.id = r.lesson_id
and r.usr_id = 100 -- the user id will change for each person
group by l.id, l.title
现在,如果您想要动态执行此操作,这意味着您事先不知道要转置的test_types
的数量,那么您应该查看以下文章:
Dynamic pivot tables (transform rows to columns)
您的代码如下所示:
SET @sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(CASE WHEN test_type = ''',
test_type,
''' THEN ''true'' ELSE ''false'' END) AS ',
test_type
)
) INTO @sql
FROM Results;
SET @sql
= CONCAT('SELECT l.id,
l.title, ', @sql, '
from lessons l
left join results r
on l.id = r.lesson_id
and r.usr_id = 100
group by l.id, l.title');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
答案 1 :(得分:0)
我将此视为简单的聚合查询。聚合是由lesson_id和title。然后计算只是将结果转向o和w:
select l.lesson_id, l.title,
max(case when r.test_type = 'o' then 'TRUE' else 'FALSE' end) as o,
max(case when r.test_type = 'w' then 'TRUE' else 'FALSE' end) as w
from lessons l left outer join
results r
on l.lesson_id = r.lesson_id
where r.user_id = <whatever>
group by l.lesson_id, l.title
order by 1
用户在此级别选择是一个问题,因为它需要来自结果,由于左外连接,结果可能为NULL。以下是解决问题的变体:
select l.lesson_id, l.title,
max(case when r.test_type = 'o' then 'TRUE' else 'FALSE' end) as o,
max(case when r.test_type = 'w' then 'TRUE' else 'FALSE' end) as w
from lessons l left outer join
results r
on l.lesson_id = r.lesson_id and
r.user_id = <whatever>
group by l.lesson_id, l.title
order by 1
通过将条件移至“on”条款,我们保证将保留所有课程。