Jay*_*len 5 php mysql dynamicquery
我正在尝试使用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
Run Code Online (Sandbox Code Playgroud)
查询完成后,将执行.
或者,如果用户选择"名称,销售".然后用户想要在列上应用SUM函数daily_sales.最后用户选择了一个过滤器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
Run Code Online (Sandbox Code Playgroud)
查询完成后,将执行.
我该如何生成这样的查询?我需要在field_relations表格中添加更多列吗?
我不担心如何构建PHP表单来捕获用户规范,但我想弄清楚如何正确生成MySQL查询?
提前感谢您的帮助和时间.
首先,也许您最好看一下可用于 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', //
)
)
);
Run Code Online (Sandbox Code Playgroud)
因此,您从 $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.
...
Run Code Online (Sandbox Code Playgroud)
如果用户选择了姓名、名字、姓氏
您还需要在人类可读的“名称”和“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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1642 次 |
| 最近记录: |