确定值列表是否完全满足一对多关系的最有效方法 (MySQL)

Most efficient method determining if a list of values completely satisfy a one to many relationship (MySQL)

我有一个房间和他们的住户的一对多关系:

Room | User
1    | 1
1    | 2
1    | 4
2    | 1
2    | 2
2    | 3
2    | 5
3    | 1
3    | 3

给定一个用户列表,例如1、3,确定哪个房间 completely/perfectly 被他们填满的最有效方法是什么?所以在这种情况下,它应该 return 房间 3,因为虽然他们都在房间 2,但房间 2 也有其他人,这不 "perfect" 合适。

这个问题我想了几个办法,但是不知道效率如何。例如,我可以对按房间分组的用户(按升序排列)进行组连接,这将给我逗号分隔的字符串,例如“1,2,4”、“1,2,3,5”和“1, 3"。然后我可以按升序排列我的输入列表并寻找与“1,3”的完美匹配。

或者我可以计算一个房间中的用户总数并且包含用户 1 和 3。然后我将 select 用户数等于两个的房间。

请注意,我想要一种最有效的方式,或者至少是一种可以扩展到数百万用户和房间的方式。每个房间将有大约 25 个用户。我要考虑的另一件事是如何将此列表传递给数据库。我应该通过连接 AND userid = 1 AND userid = 3 AND userid = 5 等来构造查询吗?或者有没有办法将值作为数组传递到存储过程中?

如有任何帮助,我们将不胜感激。

可能不是最有效的 SQL,但类似于:

SELECT x.room_id,
       SUM(x.occupants) AS occupants,
       SUM(x.selectees) AS selectees,
       SUM(x.selectees) / SUM(x.occupants) as percentage
  FROM ( SELECT room_id, 
                COUNT(user_id) AS occupants,
                NULL AS selectees
           FROM Rooms 
          GROUP BY room_id
         UNION
         SELECT room_id, 
                NULL AS occupants,
                COUNT(user_id) AS selectees
           FROM Rooms 
          WHERE user_id IN (1,3) 
          GROUP BY room_id
        ) x
 GROUP BY x.room_id
 ORDER BY percentage DESC

将为您提供按"best fit"百分比

排序的房间列表

即。它根据房间中的人数以及您所在房间中的人数

计算出满足感的百分比

For example, I can do a group concatenate on the user (ordered ascending) grouping by room, which will give me comma separated strings such as "1,2,4", "1,2,3,5" and "1,3". I can then order my input list ascending and look for a perfect match to "1,3".

首先提个建议,提高自己作为开发者的功能水平。停止根据 CSV 考虑数据和解决方案。它限制您以电子表格的方式思考,并阻止您以关系数据的方式思考。你不需要构造字符串,然后匹配字符串,当数据在数据库中时,你可以在那里匹配它。

解决方案

那么,在关系数据方面,您到底想要什么?您想要与参数用户列表匹配的用户数最多的房间。那是对的吗 ?如果是这样,代码就简单了。

你还没有给出表格。我假设 room, user, room_user, 前两个是致命的 ids,第三个是复合键。我可以给你 SQL 解决方案,你必须弄清楚如何在非 SQL.

Another thing I want to consider is how to pass this list to the database. Should I construct a query by concatenating AND userid = 1 AND userid = 3 AND userid = 5 and so on? Or is there a way to pass the values as an array into a stored procedure?

  1. 要将列表传递给存储过程,因为它需要一个单一的调用参数,其长度是可变的,你必须创建一个 CSV 用户列表。我们称该参数为 @user_list.(请注意,这不是考虑数据,而是在单个参数中将列表传递给 proc,因为否则您无法将未知数量的已识别用户传递给 proc。)

  2. 既然你在客户端构建了@user_list,你也可以在计算@user_count(列表中的成员数)的同时,在客户端,并将其传递给 proc.

类似于:

CREATE PROC room_user_match_sp (
    @user_list    CHAR(255),
    @user_count   INT
    ...
    )
AS
    -- validate parms, etc
    ...
SELECT  room_id,
        match_count,
        match_count / @user_count * 100 AS match_pct
    FROM  (
        SELECT  room_id,
                COUNT(user_id) AS match_count -- no of users matched
            FROM room_user
            WHERE user_id IN ( @user_list )
            GROUP BY room_id                  -- get one row per room
            ) AS match_room                   -- has any matched users
    WHERE match_count = MAX( match_count )    -- remove this while testing

不清楚,如果你想要完全匹配只有。在这种情况下,使用:

    WHERE match_count = @user_count

预期

您要求基于过程的解决方案,所以我已经给出了。是的,这是最快的。但是请记住,对于这种需求和解决方案,您可以在客户端构造 SQL 字符串,并以通常的方式在 "server" 上执行它,而无需使用 proc。这里的 proc 更快只是因为代码被编译并且该步骤被删除,而不是每次客户端使用 SQL 字符串调用 "server" 时执行该步骤。

我在这里要表达的意思是,使用合理的关系形式的数据,您可以使用单个 SELECT 语句获得您正在寻找的结果,您 必须处理工作表或临时表或中间步骤,这 需要 过程。在这里,proc 不是 必需的 ,您是出于性能原因实现了一个 proc。

我提出这一点是因为从您的问题中可以清楚地看出您对解决方案的期望是 "gee, I can't get the result directly, I have work with the data first, I am ready and willing to do that"。只有当数据不是关系数据时才需要这样的中间工作步骤。