有没有比执行多个 JOINS (MySQL) 更有效的方法?

发布于 2024-08-07 13:44:21 字数 645 浏览 0 评论 0原文

我有三个表:

  1. USER: user_id (pk);用户名
  2. FIELD: field_id (pk);名称
  3. 元数据:metadata_id (pk);字段_id(索引);用户id(索引); 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 个用户元数据字段,但是您知道如果有十几个字段,这会是什么样子。

还有另一种更好的方法来编写这个查询吗?随着用户和元数据字段的增长,我担心此查询的性能。

编辑: 我希望返回的结果中每行有一个用户。

I have three tables:

  1. USER: user_id (pk); username
  2. FIELD: field_id (pk); name
  3. METADATA: metadata_id (pk); field_id (indx); user_id (indx); value

The reasoning for this is that the application allows custom fields to be created for each user. To display all the information for a user I am building a dynamic query (via PHP) which ends up looking like this:

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)

This example has only 2 user metadata fields, but you get the idea what how this would look if there were a dozen fields.

Is there another, better way to write this query? I'm worried about the performance of this query as the users and metadata fields grow.

EDIT:
I'd like to have one user per row in the returned results.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

卷耳 2024-08-14 13:44:21

为什么不一次把它们全部抓住呢?

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 的类型和长度完全相同,否则您最终仍然会进行表扫描。

你的服务器代码是什么语言?

为每个用户获取一行(有点)的一种简单方法是在循环中返回行,检查每个 user_id 是否与最后一个相同。如果没有,则新行。

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;
}

Why not just grab them all at once?

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

Or for a particular user:

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

Beyond being indexed, make sure the user_id is exactly the same type and length between the two tables, or you still end up doing table scans.

What language is your server code?

A simple way to get one row per user (kinda) is in your loop returning the rows, check each user_id if it's the same as the last. If not, new row.

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;
}
月光色 2024-08-14 13:44:21

您将必须有两个查询。一个用于用户检索(SELECT * FROM users),另一个用于提取用户的自定义字段(SELECT * FROM fields WHERE users_id = user_id)。

不过,在某些情况下,您可以通过一个查询来完成它......告诉我们更多关于您想要的结果到底是什么。

You will have to have two queries. One for user retrivial (SELECT * FROM users) and then another that will pull user's custom fields (SELECT * FROM fields WHERE users_id = user_id).

You could pull it off with one query in certain cases though ... tell us more on what exactly is the result you are going after.

潜移默化 2024-08-14 13:44:21

您通常会每行返回一个元数据元素,如下所示:

SELECT u.username, mi.field_id, m1.value
FROM user AS u
LEFT JOIN metadata AS m1 ON u.user_id = m1.user_id

这对于数千个用户来说应该表现良好。

You'd normally return one meta data element per row, as in:

SELECT u.username, mi.field_id, m1.value
FROM user AS u
LEFT JOIN metadata AS m1 ON u.user_id = m1.user_id

This should perform fine up to thousands of users.

記柔刀 2024-08-14 13:44:21

您可能会尝试类似...

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 均已编入索引。

you might try something like...

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

... but the performance will probably be similar (and may be worse) to what you have. Check to make sure both user_id and field_id are indexed if you are having performance issues.

无言温柔 2024-08-14 13:44:21

(正如评论者指出的,这在 MySQL 中无效,所以,抱歉。但如果您感兴趣的话:)

按照上面的建议对元数据表进行一次 JOIN,然后使用 PIVOT 将每个用户的许多行更改为一行有多列,每个字段一个。我认为这在 SQL Server 2005 及更高版本中有效。

(As a commenter pointed out, this is not valid in MySQL, so, sorry. But in case you're interested:)

Do one JOIN to the metadata table as suggested above, and then use PIVOT to change your many rows per user into one row with many columns, one per field. I think this is valid in SQL Server 2005 and later.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文