带联接的SQL查询以获取嵌套的对象数组

App*_*per 5 mysql sql node.js

简介:我将从JSON模式开始描述期望。请注意,这些角色带有嵌套的对象数组,我正在寻找一种“智能查询”,可以通过一个查询来获取它。

{
    "id": 1,
    "first": "John",
    "roles": [ // Expectation -> array of objects
        {
            "id": 1,
            "name": "admin"
        },
        {
            "id": 2,
            "name": "accounts"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

用户

+----+-------+
| id | first |
+----+-------+
|  1 | John  |
|  2 | Jane  |
+----+-------+
Run Code Online (Sandbox Code Playgroud)

角色

+----+----------+
| id |   name   |
+----+----------+
|  1 | admin    |
|  2 | accounts |
|  3 | sales    |
+----+----------+
Run Code Online (Sandbox Code Playgroud)

user_role

+---------+---------+
| user_id | role_id |
+---------+---------+
|       1 |       1 |
|       1 |       2 |
|       2 |       2 |
|       2 |       3 |
+---------+---------+
Run Code Online (Sandbox Code Playgroud)

尝试01

在幼稚的方法中,我将multipleStatements:true在in连接字符串的帮助下在我的nodejs代码中运行两个sql查询。资讯

User.getUser = function(id) {
    const sql = "SELECT id, first FROM user WHERE id = ?; \
        SELECT role_id AS id, role.name from user_role \
        INNER JOIN role ON user_role.role_id = role.id WHERE user_id = ?";

    db.query(sql, [id, id], function(error, result){
        const data = result[0][0]; // first query result
        data.roles = result[1];  // second query result, join in code.
        console.log(data);
    });
};
Run Code Online (Sandbox Code Playgroud)

问题:上面的代码产生了预期的JSON模式,但是需要两个查询,由于有多个语句,我能够将其范围缩小到最小的代码单位,但是我在其他语言(例如Java或C#)中没有这么奢侈实例,我必须创建两个函数和两个sql查询。所以我正在寻找一个查询解决方案。


尝试02

在较早的尝试中,借助SO社区,我能够使用单个查询接近以下内容,但它只能帮助产生字符串数组(而不是对象数组)。

User.getUser = function(id) {
    const sql = "SELECT user.id, user.first, GROUP_CONCAT(role.name) AS roles FROM user \
        INNER JOIN user_role ON user.id = user_role.user_id \
        INNER JOIN role ON user_role.role_id = role.id \
        WHERE user.id = ? \
        GROUP BY user.id";
    db.query(sql, id, function (error, result) {
        const data = {
            id: result[0].id, first: result[0].first,
            roles: result[0].roles.split(",") // manual split to create array
        };
        console.log(data);
    });
};
Run Code Online (Sandbox Code Playgroud)

尝试02结果

{
    "id": 1,
    "first": "John",
    "roles": [ // array of string
        "admin",
        "accounts"
    ]
}
Run Code Online (Sandbox Code Playgroud)

生成对象数组是一个很常见的要求,因此想知道SQL中是否存在我不知道的东西。有没有一种方法可以在最佳查询的帮助下更好地实现这一目标。

或者让我知道没有这样的解决方案,就是这样,这就是在生产代码中通过两个查询完成的方式。


尝试03

使用role.id而不是role.namein GROUP_CONCAT(role.id),那样您就可以获取某些ID,然后使用另一个子查询获取关联的角色名称,只是在想...

SQL(不起作用,只是想一些事情而已)

SELECT 
  user.id,  user.first,  
  GROUP_CONCAT(role.id) AS role_ids, 
  (SELECT id, name FROM role WHERE id IN role_ids) AS roles
FROM user 
INNER JOIN user_role ON user.id = user_role.user_id 
INNER JOIN role ON user_role.role_id = role.id 
WHERE user.id = 1
GROUP BY user.id;
Run Code Online (Sandbox Code Playgroud)

编辑

根据Amit的回答,我了解到SQL Server中使用的解决方案JSON AUTO。是的,这是我正在MySQL中寻找的东西。

准确地表达。

当您联接表时,第一个表中的列将作为根对象的属性生成。第二个表中的列作为嵌套对象的属性生成。

小智 7

使用此加入查询

FOR JSON AUTO 将为您的查询结果返回 JSON

SELECT U.UserID, U.Name, Roles.RoleID, Roles.RoleName  
FROM [dbo].[User] as U 
INNER JOIN [dbo].UserRole as UR ON UR.UserID=U.UserID 
INNER JOIN [dbo].RoleMaster as Roles ON Roles.RoleID=UR.RoleMasterID
FOR JSON AUTO
Run Code Online (Sandbox Code Playgroud)

上述查询的输出是

[
  {
    "UserID": 1,
    "Name": "XYZ",
    "Roles": [
      {
        "RoleID": 1,
        "RoleName": "Admin"
      }
    ]
  },
  {
    "UserID": 2,
    "Name": "PQR",
    "Roles": [
      {
        "RoleID": 1,
        "RoleName": "Admin"
      },
      {
        "RoleID": 2,
        "RoleName": "User"
      }
    ]
  },
  {
    "UserID": 3,
    "Name": "ABC",
    "Roles": [
      {
        "RoleID": 1,
        "RoleName": "Admin"
      }
    ]
  }
]
Run Code Online (Sandbox Code Playgroud)