【发布时间】:2013-04-08 15:52:10
【问题描述】:
奇怪的是,我用mySQL做了很多开发,今天遇到的一些东西从来没有遇到过。
所以,我有一个 user_items 表
ID | name
---------
1 | test
然后我有一个 item_data 表
ID | item | added | info
-------------------------
1 | test | 12345 | important info
2 | test | 23456 | more recent important info
然后我有一个电子邮件表
ID | added | email
1 | 12345 | old@b.com
2 | 23456 | a@b.com
3 | 23456 | b@c.com
还有一个 emails_verified 表
ID | email
-----------
1 | a@b.com
现在我意识到这些表的设置可能效率不高,但这无法更改,而且比看起来要复杂得多。
我想要做的如下。我希望能够搜索用户项目并显示相关信息,以及相关的任何电子邮件,以及显示电子邮件是否已通过验证。
user_items.name = item_data.item
item_data.added = emails.added
emails.email = emails_verified.email
所以对于用户项目 1,测试。我希望能够返回其 ID、名称、最新信息、最新电子邮件及其验证状态。
所以我想回来
ID => 1
name => test
information => more recent important info
emails => array('0' => array('email' => 'a@b.com' , 'verified' => 'YES'),'1' => array('email' => 'b@c.com' , 'verified' => 'NO'))
现在我可以相对轻松地处理多个查询。然而,我的研究表明,与使用一个(尽管非常复杂)带有大量连接语句的 mysql 查询相比,这显着增加了资源/时间成本。
使用一个查询也很有用(我相信)是因为我可以相对轻松地添加搜索功能 - 添加到查询复杂的 where 语句中。
为了更复杂的问题,我正在使用 CodeIgniter。我不能太挑剔:) 所以任何无 CI 答案仍然非常有用。
到目前为止我得到的代码如下。然而,“我不太确定我在做什么”。
function test_search()
{
$this->load->database();
$this->db->select('user_items.*,item_data.*');
$this->db->select('GROUP_CONCAT( emails.email SEPARATOR "," ) AS emails', FALSE);
$this->db->select('GROUP_CONCAT( IF(emailed.email,"YES","NO") SEPARATOR "," ) AS emailed', FALSE);
$this->db->where('user_items.name','test');
$this->db->join('item_data','user_items.name = item_data.name','LEFT');
$this->db->join('emails','item_data.added = emails.added','LEFT');
$this->db->join('emailed','emails.email = emailed.email','LEFT');
$this->db->group_by('user_items.name');
$res = $this->db->get('user_items');
print_r($res->result_array());
}
对此的任何帮助将不胜感激。
这是一个非常复杂的 sql - 这真的是实现此功能的最佳方式吗?
谢谢
更新
继续 Cryode 的出色回答。
唯一的问题是它只返回一封电子邮件。但是,通过使用 GROUP_CONCAT,我能够将所有电子邮件和所有 email_verified 状态转换为一个字符串,然后我可以使用 PHP 进行分解。
要澄清的是子查询,
SELECT item, MAX(added) AS added
FROM item_data
GROUP BY item
本质上是创建一个临时表?
类似于here的概述
肯定需要子查询来确保您只从 item_data 中获取一行 - 最新的一行?
最后回答关于设计不佳的数据库的注释。
数据库是这样设计的,因为 item_data 会定期更改,但我们希望保留历史记录。
电子邮件是商品数据的一部分,但因为可以有任意数量的电子邮件,并且我们希望它们可以被搜索到,所以我们选择了一个单独的表格。否则,电子邮件必须在 item_data 表中进行序列化。
emails_verified 表是独立的,因为一封电子邮件可以与多个项目相关联。
鉴于这一点,尽管(显然)查询很复杂,但它似乎仍然是一个合适的设置..?
谢谢
最终更新
Cryodes 的答案通常是与数据库架构相关的非常有用的答案。
稍微概念化一下,如果我们将版本 id 存储在 user_items 中,我们就不需要子查询了。
由于版本之间的数据不一定一致,我们将废弃他的建议项目表(对于这种情况)。 然后我们可以从 item_data 表中获取正确的版本 我们还可以根据版本 id 获取 items_version_emails 行,并从我们的“电子邮件”表中获取相应的电子邮件。
I.E 完美运行。
这样做的缺点是,当我在 item_data 中添加新版本数据时,我必须使用已插入的新版本更新 user_items 表。
这很好,但概括地说,什么更快? 我认为建议这种设置的原因是它更快 - 每次添加新数据时进行额外的更新是值得的,以在显示大量行时保存潜在的数百个子查询。尤其是考虑到我们显示的数据多于更新数据。
只是为了了解在未来设计数据库架构时是否有人提供任何链接/一般指导,说明什么更快以及为什么这样我们都可以制作更好的优化数据库。
再次感谢 Cryode !!
【问题讨论】:
-
能否请您根据您的示例数据以表格形式发布此类查询的预期结果?
-
您应该尽可能在表之间使用整数键。它们比字符串更有效。表之间引用的键需要被索引。有了它,你应该能够成功地使用连接来获得你想要的。
-
It is however very much 'im not too sure what im doing'.定义它 上面的代码有什么问题? 一个糟糕的设计仍然是 -
预期的输出已经在上面发布了。我很欣赏整数键的效率——我将来会改变它。我知道它可以通过连接来完成,这很简单。我对上述代码的问题是,当它与 item_data 连接时,它会同时获取两行,从而连接旧 item_data 条目和新 item_data 条目的电子邮件。我只想要最新条目的数据。
标签: php mysql sql codeigniter join