【问题标题】:Dynamic MySQL query: Join results from numerous tables动态 MySQL 查询:连接来自众多表的结果
【发布时间】:2013-10-20 16:42:11
【问题描述】:

我现在一直在磕磕绊绊。我有一个名为gears 的表,其中包含名称为:idmidcidinstalled 的行。我想搜索此表并以 csv 格式返回 mids 列表以获取一些独特的 cid。例如,如果cid = $cid 我可以使用:

$query = $database -> query("SELECT COUNT(mid), GROUP_CONCAT(mid) FROM gears WHERE cid=$cid", __LINE__, __FILE__);
$gears_installed = $database -> get_result($query);
$gears = $database -> get_result($query, 0, 1);

不用担心函数名称,它们的功能完全符合人们的预期。因此,如果该特定 $cid 有 3 行,mids:banklotterypost,则 $gears_installed 将等于 3,$gears 将等于 bank,lottery,post .这按预期工作。

现在谈谈我的问题。每个唯一的mid 都有自己的表,名为settings_mid_here。即,对于上述三个,我有表settings_banksettings_lottery,最后是settings_post。这些表中的每一个也将有一个名为cid 的列(这就是两者的关联方式)。如何运行一个查询以从 cid=$cid 所在的每个表中返回整行?我不想对SELECT * FROM settings_bank WHERE cid=$cidSELECT * FROM settings_post WHERE cid=$cid 以及最后SELECT * FROM settings_post WHERE cid=$cid 运行单独的查询,因为这可能会导致一页加载时大约有10 个额外的查询(目前,有10 个不同的mids )。

如您所见,问题是动态的。它必须能够适应不同数量的mids,以某种方式区分每个表中的设置(例如settings_bank 可能有一个名称为name 的列,settings_post 也可能有)。最后,如果不存在对应于给定$cid 的行,它还必须能够返回默认行(非空值)。

一项复杂的任务,但我希望有人可以帮助我,因为我无法到达任何地方。

【问题讨论】:

  • 我有一些想法,但我只能提供一些方案的转储和一些演示数据来测试查询。否则,它只是一个聪明的猜测。
  • 您的意思是每次mid 出现在gears 表中时,您都会创建一个新表?
  • @geomagas gears 表可以多次包含特定的midgears 表将为每个 mid 安装在一个 cid 帐户上,可以这么说。例如,每个mid 都是一个小部件。
  • 是的,但这不是我问的。假设我在gears 中添加了一行,其中包含以前不存在的mid='somewidget'。我也必须创建一个settings_somewidget 表吗?

标签: php mysql sql join


【解决方案1】:
$queries = array();
foreach(explode(',', $gears) as $gear) {
    $queries[] = "SELECT '$gear' AS gearname, settings_$gear.* FROM settings_$gear WHERE cid=$cid";
}
$sql = implode(' UNION ', $queries);
$query2 = $database->query($sql);

此查询将为每个表返回一行,并带有一个额外的 gearname 列来指示该行来自哪个表。

或者你可以动态创建一个JOIN:

$gears_array = explode(',', $gears);
$joins = implode(' JOIN ', $gears_array);
$wheres = implode(' AND ',
                  array_map(function($g) use ($cid) {
                    return "$g.cid = $cid";
                  }, $gears_array));
$sql = "SELECT * FROM $joins WHERE $wheres";
$query2 = $database->query($sql);

【讨论】:

  • 我已经为一个测试用例运行了这个:SELECT 'currency' AS gearname, * FROM settings_currency WHERE cid=3 但这会产生错误(由于'currency' AS gearname 部分)。
  • 已修复。执行此操作时,MySQL 需要在 * 之前有一个显式表名。
  • 这很好用,直到两个表有不同数量的列(对我来说就是这种情况)——此时它分崩离析并引发错误。
  • 我曾想过做一些类似于SELECT a.*, b.* FROM settings_currency a LEFT JOIN settings_post b ON b.cid=a.cid WHERE a.cid=$cid 的事情,除了我不知道如何使这个工作适用于多个表,而且现在存在无法将列与其原始表区分开来的问题.例如,将有两列名为name,一列来自settings_currency,一列来自settings_post
  • 我添加了一个 JOIN 解决方案。虽然我想知道应用程序将如何处理所有这些动态数据——如果表具有不同的模式,应用程序将如何知道结果中的内容?
【解决方案2】:

这并不是您具体问题的真正答案,仅仅是因为没有办法通过一个查询来完成您正在尝试的事情

原因很简单:RDBMS 并非设计为以这种方式工作。表应该存储代表实体关系数据。在您的情况下,对于mid 的每个不同值,必须存在一个名为settings_{mid} 的表,从而强制mid 列隐式存储(一部分)表名。但那不是数据,那是元数据

如果 SQL 语法可以接受变量、参数化、列相关或任意表名,那将不是问题。但事实并非如此。这是设计。相反,RDBMS 为您提供了将数据相互关联所需的所有工具。通过以预期的方式使用它,您将永远不必求助于这种“动态”技巧。

在您的情况下,应该有 一个 config 表和 mid 列来区分引用特定 mid 值的行。那么,查询就很简单了:

select * from `config` where mid='$mid' and cid='$cid'

这是关系方式。因此 RDBMS 中的 R。完全没有理由将数据与元数据混合在一起。如果这样做,则将关系解析问题移到更高级别的应用程序模型中。

最后一件事:有人可能会争辩说config_{mid} 表可能具有相似但不相同的结构。也有解决方案:IS-A relations

话虽如此,对于您的具体问题,按照 Barmar 的答案的解决方案就可以解决问题。

【讨论】:

  • 我最初的想法是使用config 表,但由于每个mid 都有任意数量的“设置”,这实际上是不可能的。
  • 除了我提到的 IS-A 方法,如果结构不能以任何有用的方式分类,您仍然可以使用键/值对表来实现这一点:CONFIG(mid,cid,setting_name,setting_value)。但要避免任意数量的表格;你只会让事情变得更糟。
  • 其实我很喜欢这个主意,谢谢。您是否建议创建两个表,即setting_ids,它有 3 列:sidmidsetting_name(因此可以在此处定义每个设置)和另一个表,其中包含每个 @987654337 的设置的每个实例@,有列:idsidcidsetting_val。所以在第一个表中我可以有一行:sid=1mid=banksetting_name=sign,然后在另一个表中有带有 vals 的行:cid=345setting_val=£; cid=24, setting_val=$ -> 两行都有sid=1
  • 事实上,我会推荐一个更复杂的模式,具有必要的(甚至不是必要的)约束和结构。但这不是 cmets 讨论的主题。我建议打开一个新主题来解决这些问题,以便其他人也可以做出贡献。我很乐意参与。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-28
  • 2014-11-16
  • 2012-10-29
  • 2016-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多