一般来说,在您担心性能之前,您应该始终让架构正确无误!
这样您就可以就调整架构以解决特定的性能问题做出明智的决定,而不是猜测。
您绝对应该走 2 桌路线。这将显着减少存储量、代码复杂性以及更改系统以添加新属性的工作量。
假设每个属性都可以用Ordinal number 表示,并且您只是在寻找对称匹配(即您试图根据相似属性来匹配人,而不是意图表达)... .
简单来说,查找合适匹配项的查询可能非常昂贵。实际上,您正在寻找 N 维空间中相同邻近的节点,不幸的是大多数关系数据库并没有真正为这种操作设置(我相信 PostgreSQL 支持这一点)。所以大多数人可能会从以下内容开始:
SELECT candidate.id,
COUNT(*)
FROM users candidate,
attributes candidate_attrs,
attributes current_user_attrs
WHERE current_user_attrs.user_id=$current_user
AND candidate.user_id<>$current_user
AND candidate.id=candidate_attrs.user_id
AND candidate_attrs.attr_type=current_user.attr_type
AND candidate_attrs.attr_value=current_user.attr_value
GROUP BY candidate.id
ORDER BY COUNT(*) DESC;
但是,这会强制系统比较每个可用的候选人以找到最佳匹配。应用一些启发式方法,您可以获得非常有效的查询:
SELECT candidate.id,
COUNT(*)
FROM users candidate,
attributes candidate_attrs,
attributes current_user_attrs
WHERE current_user_attrs.user_id=$current_user
AND candidate.user_id<>$current_user
AND candidate.id=candidate_attrs.user_id
AND candidate_attrs.attr_type=current_user.attr_type
AND candidate_attrs.attr_value
BETWEEN current_user.attr_value+$tolerance
AND current_user.attr_value-$tolerance
GROUP BY candidate.id
ORDER BY COUNT(*) DESC;
($tolerance 的值会影响返回的行数和查询性能——如果你有 attr_type 的索引,attr_value)。
这可以进一步细化为积分系统:
SELECT candidate.id,
SUM(1/1+
((candidate_attrs.attr_value - current_user.attr_value)
*(candidate_attrs.attr_value - current_user.attr_value))
) as match_score
FROM users candidate,
attributes candidate_attrs,
attributes current_user_attrs
WHERE current_user_attrs.user_id=$current_user
AND candidate.user_id<>$current_user
AND candidate.id=candidate_attrs.user_id
AND candidate_attrs.attr_type=current_user.attr_type
AND candidate_attrs.attr_value
BETWEEN current_user.attr_value+$tolerance
AND current_user.attr_value-$tolerance
GROUP BY candidate.id
ORDER BY COUNT(*) DESC;
这种方法可以让您做很多不同的事情 - 包括按属性子集进行搜索,例如
SELECT candidate.id,
SUM(1/1+
((candidate_attrs.attr_value - current_user.attr_value)
*(candidate_attrs.attr_value - current_user.attr_value))
) as match_score
FROM users candidate,
attributes candidate_attrs,
attributes current_user_attrs,
attribute_subsets s
WHERE current_user_attrs.user_id=$current_user
AND candidate.user_id<>$current_user
AND candidate.id=candidate_attrs.user_id
AND candidate_attrs.attr_type=current_user.attr_type
AND candidate_attrs.attr_value
AND s.subset_name=$required_subset
AND s.attr_type=current_user.attr_type
BETWEEN current_user.attr_value+$tolerance
AND current_user.attr_value-$tolerance
GROUP BY candidate.id
ORDER BY COUNT(*) DESC;
显然,这不包含非序数数据(例如出生标志、最喜欢的流行乐队)。如果不了解更多关于现有数据的结构,很难确切地说这将是多么有效。
如果您想添加更多属性,则无需对 PHP 代码或数据库架构进行任何更改 - 它可以完全由数据驱动。
另一种方法是识别刻板印象 - 即 N 维空间内的参考点,然后计算出特定用户最接近其中的哪一个。您将所有属性分解为一个复合标识符 - 然后您只需要应用相同的方法在也与原型匹配的候选人子集中找到最佳匹配。