我正在尝试使用PHP和MySQL构建动态查询。
我所做的是创建一个表(即。field_relations
)
该字段有5列
NULL
)NULL
)以下是示例数据
field_name display_label table_name related_to related_to_field
account_id Account ID accounts NULL NULL
account_name Name accounts NULL NULL
first_name First Name contacts NULL NULL
last_name Last Name contacts NULL NULL
contact_id Contact ID contacts NULL NULL
account_id Account ID contacts accounts account_id
task_id Task ID tasks NULL NULL
subject Subject tasks NULL NULL
owner_id Assigned To contacts contacts contact_id
daily_sales Sales transactions accounts account_id
sold_on Sold On transactions NULL NULL
因此,如果我创建一个3秒的HTML表单
该表单的第一部分将显示display_label
列中列出的所有值。
如果用户选择了Name, First Name, Last Name
然后查询将需要看起来像这样
SELECT accounts.account_name, contacts.first_name, contacts.last_name
FROM accounts
INNER JOIN contacts ON contacts.account_id = accounts.account_id
查询完成后将执行。
或者,如果用户选择了“名称,销售”。然后,用户想要在列daily_sales
上应用SUM函数。最后,用户选择了Sold On between '2014-01-01 00:00:00' AND '2014-10-01 00:00:00'
然后查询将需要看起来像这样
SELECT accounts.account_name, SUM(daily_sales) AS daily_sales
FROM accounts
LEFT JOIN sales ON sales.account_id = accounts.account_id
WHERE sales.sold_on BETWEEN '2014-01-01 00:00:00' AND '2014-10-01 00:00:00'
GROUP BY accounts.account_name
查询完成后将执行。
如何生成此类查询?我是否需要在field_relations
表中添加更多列?
我并不担心如何构建PHP表单来捕获用户规范,但我想弄清楚如何正确生成MySQL查询?
提前感谢您的帮助和时间。
答案 0 :(得分:0)
将构建sql查询视为一个简单的字符串构建练习。当您引用field_relations表时,从上一页的表单中发布的值将标识您在查询中需要的列和表。
第一个发布的值将标识其中一个表中的字段,告诉您需要FROM子句。然后,只要您遇到第二个表中的字段,就会告诉您添加一个字段 INNER JOIN联系人(或销售人员)ON contacts.account_id = accounts.account_id子句。如果您以后遇到第三个表中的字段,则必须添加另一个JOIN子句。
在field_relations中根本不需要related_to和related_to_field列,因为从表单发布的列名称在引用field_relations表时会告诉您字段来自哪个表。
答案 1 :(得分:0)
首先,也许您最好看一下可用于PHP / MySQL的几个ORM(对象关系管理)系统中的任何一个。
但重新发明轮子相对容易,只要你保持小巧(这意味着你只能解决非常简单的查询)。
如果是这种情况,假设我们只需要内连接,它们都是一对多类型(实际上并不是严格的要求,正如我们将要看到的那样)。我们可以建立一个DIY ORM(我怀疑一个更恰当的名字就像'Diy Orm Cthulhu Fhtagn')
第一步是将此信息存储在某个位置,例如数组。所有可能的JOIN的一个条目。您还需要描述您的表到系统。您还可以让系统查询MySQL以检索表和字段名称,可能偶尔会从生成PHP代码的单独实用程序中检索。
// This just maps what fields are in what tables
$tables = array(
'contacts' => array(
'first_name' => true /* or an array of information such as SQL type, etc. */
);
);
// This maps all the JOINs
$orm = array(
'contacts' => array(
'accounts' => array(
'on' => array( 'account_id', 'account_id' ),
'type' => 'LEFT JOIN', //
)
)
);
所以你从$ selectFields列表开始。您将这些字段复制到$unresolvedFields
,然后开始一个接一个地检查它们。您的目标是解决所有字段。
伪代码(实际上不是那么伪):
while (!empty($unresolvedFields)) {
$changes = false;
// Try to resolve one of them.
foreach ($unresolvedFields as $i => $field) {
// Try to resolve it.
list($tableName, $fieldName) = explode('.', $field);
// Did we already select from this table?
if (array_key_exists($tableName, $selectedTables)) {
// Yes, so this field has been resolved for free.
$changes = true;
$resolvedFields[] = $field;
array_push($selectedTables[$tableName], $fieldName);
unset($unresolvedFields[$i];
// On to the next field.
continue;
}
// This is the first time we see this table.
// Is this the VERY FIRST table (assume it's the "lead" table --
// it's not necessary but it simplifies the code)?
if (empty($selectedTables)) {
// Yes. We need do no more.
$selectedTables[$tableName] = array( $fieldName );
$changes = true; //-//
$resolvedFields[] = $field; //-//
unset($unresolvedFields[$i]; //-//
// On to the next field. //--//
continue; //--//
} // We could also put this check before the last. If we did, the
// lines above marked //-// could be left out; those with //--// should.
// And we would need $selectedTables[$tableName] = array(/*EMPTY*/);
// We did not see this table before, and it's not the first.
// So we need a way to join THIS table with SOME of those already used.
// Again we suppose there're no ambiguities here. This table HAS a
// link to some other. So we just need ask, "WHICH other? And do we have it?"
$links = $orm[$tableName];
$useful = array_intersect_keys($orm[$tableName], $selectedTables);
// $useful contains an entry 'someTable' => ( 'on' => ... )
// for each table that we can use to reference $tableName.
// THERE MUST BE ONLY ONE, or there will be an ambiguity.
// Of course most of the time we will find none.
// And go on with the next field...
if (empty($useful)) {
continue;
}
// TODO: check count($useful) is really 1.
$changes = true;
$selectedTables[$tableName] = array( $fieldName );
list($joinWith, $info) = each($useful[0]);
// We write SQL directly in here. We actually shouldn't, but it's faster
// to do it now instead of saving the necessary info.
// $info could actually also contain the JOIN type, additional conditions...
$joins[] = "INNER JOIN {$joinWith} ON ( {$tableName}.{$info['on'][0]}
= {$joinWith}.{$info['on'][1]} )";
unset($unresolvedFields[$i];
}
// If something changed, we need to repeat, because a later field could have
// supplied some table that could have made joinable an earlier field which we
// had given up on, before.
if (!$changes) {
// But if nothing has changed there's no purpose in continuing.
// Either we resolved all the fields or we didn't.
break;
}
}
// Now, if there're still unresolved fields after the situation stabilized,
// we can't make this query. Not enough information. Actually we COULD, but
// it would spew off a Cartesian product of the groups of unjoined tables -
// almost surely not what we wanted. So, unresolveds cause an error.
if (!empty($unresolvedFields)) {
throw new \Exception("SOL");
}
// Else we can build the query: the first table leads the SELECT and all the
// others are joined.
$query = "SELECT " . implode(', ', $selectedFields)
. ' FROM ' . array_shift($selectedTables) . "\n";
// Now for each $selectedTables remaining
foreach ($selectedTables as $table) {
$query .= $joins[$table] . "\n";
// Now we could add any WHEREs, ORDER BY, LIMIT and so on.
...
如果用户选择了姓名,名字,姓氏
您还需要在人类可读的“名称”和“accounts.account_name”之间进行“翻译”。但是,一旦你做了,上面的算法就会找到那些记录:
Name ... fields = [ accounts.account_name ], tables = [ accounts ], joins = [ ]
First Name ... fields = [ a.ac_name, co.first ], tables = [ ac, co ], joins = [ co ]
Last Name ... contacts is already in tables, so fields = [ 3 fields ], rest unchanged
答案 2 :(得分:0)
为什么不使用ORM? 在这种情况下,学说非常有用:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html
您可以通过动态添加实体和字段并将它们连接在一起:易于学习和实施。