如何使用 PHP 处理从 SQL 查询返回的树结构?
Posted
技术标签:
【中文标题】如何使用 PHP 处理从 SQL 查询返回的树结构?【英文标题】:How to handle Tree structures returned from SQL query using PHP? 【发布时间】:2011-01-31 12:50:37 【问题描述】:这是一个“理论”问题。
我在定义问题时遇到了麻烦,请多多包涵。
当您在数据库中有多个相关表时,例如一个包含“用户”的表和一个包含“电话”的表
“phones”和“users”都有一个名为“user_id”的列
select user_id,name,phone from users left outer join phones on phones.user_id = users.user_id;
查询将为我提供所有用户的行,无论他们是否有电话。
如果用户有多部手机,他的姓名将按预期分两行返回。
columns=>|user_id|name|phone|
row0 = > | 0 |fred|NULL|
row1 = > | 1 |paul|tlf1|
row2 = > | 1 |paul|tlf2|
在上面的例子中,名字“paul”是一个必要的复制品,在 RDMS 的眼里根本不是复制品! 然后它将由一些服务器端脚本语言处理,例如 php。
在真实的网站或应用程序中,这些“必要的重复”实际上是如何处理的? 如,该行是如何“映射”到一些可用的对象模型中的。
附言如果您决定发布示例,请尽可能为 php、mysql、sqlite 发布示例。
编辑:
感谢您提供答案,每个答案对问题的解释都不同,因此以自己的方式不同且正确。
我得出的结论是,如果往返费用昂贵,那么这将是最好的方式以及 Jakob Nilsson-Ehle 的解决方案,该解决方案适合理论问题。
如果往返价格便宜,我将按照 9000 的建议为手机和用户分别选择,如果我需要为每个用户显示一部手机,我将为手机提供一个主列并与用户一起加入像 Ollie Jones 正确建议的那样选择。
即使对于现实生活中的应用程序,我使用的是 9000 的答案,但我认为对于这个不切实际的问题,Jakob Nilsson-Ehle 的解决方案是最合适的。
【问题讨论】:
你应该让“user_id”列进入输出,因为这样你就可以明确地识别用户(即区分 [23,paul] 和 [17,paul])。 不是重复的列而是行。列 >,行 v. @Martinho Fernandes 是的,显然,添加它是为了“正确”:) @M3t0r 在上面的示例中没有重复的行。 您对什么问题感兴趣:将其映射到对象模型的问题,还是从 DB 传输过多数据的问题? @Martinho Fernandes “映射到对象模型”,谢谢。 (不知道这是怎么称呼的) 【参考方案1】:在这种情况下,我在 PHP 中可能会做的事情是在 PHP 数组中使用 userId,然后使用它来持续更新用户
一个非常简单的例子是
$result = mysql_query('select user_id,name,phone from users left outer join phone on devices.user_id = users.user_id;'); $users = 数组(); 而($row = mysql_fetch_assoc($result)) $uid =$row['user_id']; if(!array_key_exists($uid, $users)) $users[$uid] = Array('name' => $row['name'], 'phones' => Array()); $users[$uid]['phones'][] = $row['phone'];当然,根据您的编程风格和用户数据的复杂性,您可能会定义一个 User 类或其他东西并填充数据,但基本上我会这样做。
【讨论】:
通常我倾向于认为涉及循环的事情没有得到很好的处理,但到目前为止这是最好的答案。 :( 还有像“手机”这样的数据,每次都要手动写入【参考方案2】:您的数据模型本质上允许用户拥有 0 部、1 部或更多部手机。
您可以让您的数据库为每个用户返回 0 或 1 个电话项目,方法是使用令人讨厌的 hack,例如选择数字最小的电话号码。 (MIN(电话)...按用户分组)。但是用数字比较电话号码意义不大。
您的歧义问题(几个电话号码中的哪一个)表明您的数据模型设计存在问题。如果愿意,可以看看一些常见的电话簿应用程序。手机上的快速拨号应用程序就是一个很好的例子。大多数情况下,他们提供输入多个电话号码的方法,但他们总是有一个主要电话号码的概念。
如果您在电话表中添加一列指示号码优先级,并使其成为您的主(唯一)键的一部分,并声明priority=1 表示用户的主号码,您的应用将不再有这个模棱两可的问题.
【讨论】:
谢谢。我也会试试这个,我可以看到这个主要数字列在预览中很有用。【参考方案3】:您不能轻易地从 RDBMS 中获得树结构,只能获得表结构。你想要一棵树:[(user1, (phone1, phone2)), (user2, (phone2, phone3))...]
。不过,您可以针对不同的目标进行优化。
往返比发送额外信息更昂贵:使用您当前的解决方案。它会多次获取用户名,但每个电话簿只有一次往返。如果您负担过重的 MySQL 主机在 1000 英里之外,这可能是有意义的。
发送额外信息比往返更昂贵,或者您想要更清晰:正如@martinho-fernandes 建议的那样,仅使用手机获取用户 ID,然后在另一个查询中获取用户详细信息。除非您的整个用户详细信息是一个简短的用户名,否则我会坚持使用这种方法。为了清楚起见,我会一直使用 SQLite。
【讨论】:
【参考方案4】:听起来您将对象数据模型与关系数据模型混淆了 - 了解它们如何differ in general 以及在您的应用程序的细节中对于在关系数据库之上编写 OO 代码至关重要。
简单的 ORM 不是解决方案。
有诸如休眠之类的 ORM 映射技术 - 但是这些技术不能很好地扩展。 IME,最好的解决方案是使用工厂模式来正确管理映射。
【讨论】:
你误解了这个问题,我已经在关系数据库之上有一个工厂模式,我想知道是否有一种方法可以让单个请求将数据排序为树形结构,它看起来 mysql 使用“Materialized Path”dev.mysql.com/tech-resources/articles/hierarchical-data.html kod34fr33.wordpress.com/2008/05/06/adjacency-list-tree-on-mysql ***.com/questions/3137674/… 对此提供支持,但不推荐以上是关于如何使用 PHP 处理从 SQL 查询返回的树结构?的主要内容,如果未能解决你的问题,请参考以下文章