我有三张桌子:
USER: user_id (pk); username
FIELD: field_id (pk); name
METADATA: metadata_id (pk); field_id (indx); user_id (indx); value
这样做的原因是应用程序允许为每个用户创建自定义字段。要显示用户的所有信息,我正在构建一个动态查询(通过PHP),最终看起来像这样:
SELECT
u.username, m1.value AS m1value, m2.value AS m2value
FROM user AS u
LEFT JOIN metadata AS m1
ON (u.user_id=m1.user_id AND m1.field_id=1)
LEFT JOIN metadata AS m2
ON (u.user_id=m2.user_id AND m2.field_id=2)
此示例只有2个用户元数据字段,但您可以了解如果有十几个字段,这会是什么样子。
还有另一种更好的方法来编写此查询吗?随着用户和元数据字段的增长,我担心这个查询的性能。
修改 我想在返回的结果中每行有一个用户。
答案 0 :(得分:2)
为什么不立刻抓住它们?
SELECT u.user_id,u.username, m.field_id,m.value FROM user u
LEFT JOIN metadata m
ON u.user_id=m.user_id
WHERE 1 ORDER BY user_id
或针对特定用户:
SELECT u.user_id,u.username, m.field_id,m.value FROM user u
LEFT JOIN metadata m
ON u.user_id=m.user_id
WHERE user_id = ? ORDER BY user_id
除了被索引之外,请确保user_id在两个表之间的类型和长度完全相同,或者您仍然最终进行表扫描。
您的服务器代码是什么语言?
每个用户获得一行(kinda)的一种简单方法是在循环中返回行,检查每个user_id是否与最后一行相同。如果没有,新行。</ p>
while ( $row = $sth->fetch_object() ) {
$previous_user_id = '';
if ( $row->user_id != $previous_user_id ) {
# new row
} else {
# not new row
}
$previous_user_id = $row->user_id;
}
答案 1 :(得分:0)
您必须有两个查询。一个用于用户追溯(SELECT * FROM users),然后另一个用于提取用户的自定义字段(SELECT * FROM fields WHERE users_id = user_id)。
在某些情况下,您可以通过一个查询将其拉出来......告诉我们更多关于您将要追求的结果的详细信息。
答案 2 :(得分:0)
您通常每行返回一个元数据元素,如:
SELECT u.username, mi.field_id, m1.value
FROM user AS u
LEFT JOIN metadata AS m1 ON u.user_id = m1.user_id
这应该可以在数千名用户中执行。
答案 3 :(得分:0)
你可能会尝试类似......
SELECT
u.username,
(SELECT TOP 1 m.value
FROM metadata m
WHERE u.user_id=m.user_id AND m.field_id=1),
(SELECT TOP 1 m.value
FROM metadata m
WHERE u.user_id=m.user_id AND m.field_id=2)
FROM user AS u
......但是你的表现可能与你的表现相似(也可能更差)。如果遇到性能问题,请检查以确保user_id和field_id都已建立索引。
答案 4 :(得分:0)
(正如评论者所指出的,这在MySQL中无效,所以,对不起。但如果你有兴趣:)
如上所述,对元数据表执行一次JOIN,然后使用PIVOT将每个用户的多行更改为一行,其中包含许多列,每个字段一个。我认为这在SQL Server 2005及更高版本中有效。