假设我有一个拥有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);
答案 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调用。