【问题标题】:Select users and pivot on existence of rows in related tables选择用户并根据相关表中是否存在行进行透视
【发布时间】:2012-11-17 20:37:52
【问题描述】:

我是 sql 新手,我真的尝试自己解决这个问题,但没有成功...... 希望有人能帮忙。

我有 3 张桌子:

身份验证用户

+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| username     | varchar(30)  | NO   | UNI | NULL    |                |
| first_name   | varchar(30)  | NO   |     | NULL    |                |
| last_name    | varchar(30)  | NO   |     | NULL    |                |
| email        | varchar(75)  | NO   |     | NULL    |                |
| password     | varchar(128) | NO   |     | NULL    |                |
| is_staff     | tinyint(1)   | NO   |     | NULL    |                |
| is_active    | tinyint(1)   | NO   |     | NULL    |                |
| is_superuser | tinyint(1)   | NO   |     | NULL    |                |
| last_login   | datetime     | NO   |     | NULL    |                |
| date_joined  | datetime     | NO   |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+

目录展示

+-------------+----------------------+------+-----+---------+----------------+
| Field       | Type                 | Null | Key | Default | Extra          |
+-------------+----------------------+------+-----+---------+----------------+
| id          | int(11)              | NO   | PRI | NULL    | auto_increment |
| name        | varchar(48)          | NO   |     | NULL    |                |
| slug        | varchar(50)          | NO   | UNI | NULL    |                |
| amount      | smallint(5) unsigned | NO   |     | 0       |                |
| points      | smallint(5) unsigned | NO   |     | 500     |                |
| created_at  | datetime             | NO   |     | NULL    |                |
| updated_at  | datetime             | NO   |     | NULL    |                |
| active      | tinyint(1)           | NO   |     | NULL    |                |
| image       | varchar(100)         | YES  |     | NULL    |                |
| description | longtext             | YES  |     | NULL    |                |
+-------------+----------------------+------+-----+---------+----------------+

目录订单

+------------+----------------------+------+-----+---------+----------------+
| Field      | Type                 | Null | Key | Default | Extra          |
+------------+----------------------+------+-----+---------+----------------+
| id         | int(11)              | NO   | PRI | NULL    | auto_increment |
| user_id    | int(11)              | NO   | MUL | NULL    |                |
| status     | smallint(5) unsigned | NO   |     | 0       |                |
| present_id | int(11)              | NO   | MUL | NULL    |                |
| address_id | int(11)              | NO   | MUL | NULL    |                |
| created_at | datetime             | NO   |     | NULL    |                |
| updated_at | datetime             | NO   |     | NULL    |                |
+------------+----------------------+------+-----+---------+----------------+

我正在尝试选择一个用户并检查他是否订购了特定的礼物并相应地写入该行,然后检查另一个,另一个等等。

基本上我希望结果如下所示:

id present1 present2 present3 present4
1 1 0 0
2 1 1 1
3 0 0 0
4 1 0 1

其中 1 是用户订购了该类型的礼物,0 是他没有订购。

我的查询如下所示

select auth_user.id, case when present_id = 1 from auth_user 
left join catalog_orders on catalog_orders.user_id = auth_user.id 
left join catalog_presents on catalog_presents.id = catalog_orders.present_id

问题是用户的所有不同订单都像这样在多行中排序:

id present1 present2 present3 present4
1 1 0 0
2 1 0 0
2 0 1 0
2 0 0 1
3 0 0 0
4 1 0 0
4 0 0 1

有人可以帮我解决这个问题吗?提前致谢!

【问题讨论】:

  • 我对问题标题进行了编辑,使其不太适合您的用例。
  • 正如您所说,您是 SQL 新手,实际上您在一个复杂的概念上做出了非常出色的努力。

标签: mysql sql


【解决方案1】:

这是一种数据透视表。

您走在正确的道路上,但您需要将结果合并到每个用户的一行。

使用聚合 MAX()auth_user.id 将它们折叠成一行。 CASE 为每个礼物提供一个零或一,然后MAX()auth_user.id 选择所有行中的最大值,如果礼物被购买,则为1,否则为0

SELECT
  auth_user.id,
  MAX(CASE WHEN present_id = 1 THEN 1 ELSE 0 END) AS present1,
  MAX(CASE WHEN present_id = 2 THEN 1 ELSE 0 END) AS present2,
  MAX(CASE WHEN present_id = 3 THEN 1 ELSE 0 END) AS present3,
  MAX(CASE WHEN present_id = 4 THEN 1 ELSE 0 END) AS present4
FROM
   auth_user
   LEFT JOIN catalog_orders on catalog_orders.user_id = auth_user.id 
   LEFT JOIN catalog_presents on catalog_presents.id = catalog_orders.present_id
GROUP BY auth_user.id
ORDER BY auth_user.id

特别是对于 MySQL,您不需要 CASE,因为布尔比较 present_id = 1 将自行返回 1 或 0。但是,这不能移植到所有其他 RDBMS。首选上述方法。

SELECT
  auth_user.id,
  /* MySQL ok, not all other RDBMS will do this - boolean comparison returns 1 or 0 */
  MAX(present_id = 1) AS present1,
  MAX(present_id = 2) AS present2,
  MAX(present_id = 3) AS present3,
  MAX(present_id = 4) AS present4
FROM
   auth_user
   LEFT JOIN catalog_orders on catalog_orders.user_id = auth_user.id 
   LEFT JOIN catalog_presents on catalog_presents.id = catalog_orders.present_id
GROUP BY auth_user.id
ORDER BY auth_user.id

【讨论】:

  • 迈克,你太棒了!感谢您的帮助!
猜你喜欢
  • 2022-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多