搜索数据库中是否存在多个对象

时间:2014-09-14 23:52:07

标签: mysql facebook

假设我有一个拥有500万用户的数据库,其中包含

id (unsigned int, auto-increment), facebook_id (unsigned int), and name (varchar)

在一个程序中,我有一个来自一个人的Facebook好友列表的可变数量用户的列表(通常范围从500-1200个不同的facebook ID)。

向我的数据库发送查询的最有效方法是什么,它返回数据库中存在相同facebook_id的所有用户的facebook_id?

的伪代码:

$friends = array(12345, 22345, 32345, 42345, 52345, ... ~1000 more); $q = mysql_query("SELECT * FROM users ..."); $friendsAlreadyUsingApp = parseQuery($q);

1 个答案:

答案 0 :(得分:1)

这是几乎无数文章,博客,Q& As等的主题;而这个问题的实质是看起来很简单 - 但事实并非如此。

问题的核心是参数看起来像它应该使用WHERE field IN()但它没有这样做,因为参数是单个字符串恰好有很多逗号。

因此,当该参数传递给SQL时,必须将该单个字符串处理为多个部分,以便可以将该字段与每个部分进行比较。这是一个有点复杂的地方,因为并非所有数据库类型都具有处理此功能的所有相同功能。例如,MySQL没有MS SQL Server提供的表变量。

因此。一个简单的方法,对于MySQL来说是这样的:

SET @param := '105,110,125,135,145,155,165,175,185,195,205';

SELECT
*
FROM Users
WHERE FIND_IN_SET(facebook_id, @param) > 0
;
  

FIND_IN_SET返回第一个参数的索引位置   在第二个参数

这个数据库在数据库中的扩展程度我无法判断,对于包含1000+ id的参数可能是不可接受的。

因此,如果FIND_IN_SET之类的文本处理速度太慢,则需要从参数中删除每个id并将其插入表中。这样,生成的表可以通过INNER JOIN来过滤用户;但这需要一个需要时间的表和插入,如果多个用户同时尝试使用该表,则可能会出现并发问题。


使用以下设置10,000个整数(1到10,000)的表

/* Create a table called Numbers */
CREATE TABLE `Numbers`
(
    `Number` int PRIMARY KEY
);

/* use cross joins to create 10,000 integers from 1 & store into table */
INSERT INTO Numbers (Number)
select 1 + (a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a))  as N
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as d
;

然后可以使用此“实用程序表”将逗号分隔的参数划分为各个整数的派生表,然后在用户表的INNER JOIN中使用该表将提供所需的结果。

SET @param := '105,110,125,135,145,155,165,175,185,195,205';
SET @delimit := ',';

SELECT
      users.id
    , users.facebook_id
    , users.name
FROM users
      INNER JOIN (
                  SELECT
                        CAST(SUBSTRING(iq.param, n.number + 1, LOCATE(@delimit, iq.param, n.number + 1) - n.number - 1) AS UNSIGNED INTEGER) AS itemID
                  FROM (
                              SELECT
                                    concat(@delimit, @param, @delimit) AS param
                        ) AS iq
                        INNER JOIN Numbers n
                                    ON n.Number < LENGTH(iq.param)
                  WHERE SUBSTRING(iq.param, n.number, 1) = @delimit
            ) AS derived
                  ON users.facebook_id = derived.itemID
;

此查询可用作存储过程的基础,您可以更轻松地从PHP调用。

See this SQLFiddle demo