5

背景

你好,我正在用 PHP 和 MySQL 开发一个实验/教育工具。我是 SQL 新手,但我想从一开始就以正确的方式做事。我对所有变量替换使用 PDO 准备好的语句,并尽可能地反引号(因此,据我了解,它不能移植到非 MySQL 数据库)。关于我的问题,我有一个关于如何进行的想法,但是我需要几个小时来实现(我什至对 SQL 的语法都是新手),所以同时我想我会先创建一个问题以防有人大喊:“这不是办法!” 并节省我数小时的精力。

问题

我想创建一个用户可以从下拉菜单中选择的界面:

  1. 一张桌子A
  2. 该表上的一个或多个字段,例如A.xand A.y,
  3. 一张桌子B
  4. 该表上的一个或多个字段,例如B.zand B.y,

并且在提交后,代码将执行内部连接,分别匹配每个字段,例如A.x = B.zA.y = B.y等,并返回所有匹配的行。

我的计划是简单地生成一条INNER JOINSQL 语句,遍历字段并插入占位符 ( ?),绑定各自的参数,最后执行语句。

有没有更简单的方法来做到这一点?有没有更好的方法来做到这一点?这会以某种方式被利用吗?

非常感谢,提前。如果在我完成时没有人回应(怀疑),我将发布我的解决方案。

杂项。

假设我将验证

  1. 用户在 和 之间选择相等数量的A字段B
  2. 字段和表存在,
  3. 等等

并且字段名称不必相同:它们将按顺序匹配。(请指出我可能不知道的任何其他细节!)

最终,目标是将这些选择保存在“设置”表中。实际上,用户创建了他们希望每次返回时看到的“视图”。

4

2 回答 2

2

你做的非常正确,以至于我真的感到内疚,指出你做错了什么!:)

您只能使用准备好的语句来参数化字段值,而不是 SQL 标识符,例如列名或表名。因此,您将无法通过准备好的语句参数将等传递到您的条件中:您A.x必须B.z一些感觉非常错误的事情,并将它们直接连接到您的 SQL 字符串中。JOIN

然而,一切都没有丢失。在一些模糊的偏好顺序中,您可以:

  1. 向用户提供一个选项列表,您随后可以从中重新组装 SQL:

    <select name="join_a">
      <option value="1">x</option>
      <option value="2">y</option>
    </select>
    <select name="join_b">
      <option value="1">z</option>
      <option value="2">y</option>
    </select>
    

    然后你的表单处理程序:

    switch ($_POST['join_a']) {
      case 1:  $acol = 'x'; break;
      case 2:  $acol = 'y'; break;
      default: die('Invalid input');
    }
    switch ($_POST['join_b']) {
      case 1:  $bcol = 'z'; break;
      case 2:  $bcol = 'y'; break;
      default: die('Invalid input');
    }
    
    $sql .= "FROM A JOIN B ON A.$acol = B.$bcol";
    

    这种方法的优点是,在不损害 PHP(在这种情况下,您将比 SQL 注入更担心)的情况下,任意 SQL 绝对无法进入您的 RDBMS。

  2. 确保用户输入与预期值之一匹配:

    <select name="join_a">
      <option>x</option>
      <option>y</option>
    </select>
    <select name="join_b">
      <option>z</option>
      <option>y</option>
    </select>
    

    然后你的表单处理程序:

    if (!in_array($_POST['join_a'], ['x', 'y'])
     or !in_array($_POST['join_b'], ['z', 'y']))
       die('Invalid input');
    
    $sql .= "FROM A JOIN B ON A.$_POST[join_a] = B.$_POST[join_b]";
    

    这种方法依赖于 PHP 的in_array安全功能(并且还向用户公开了您的基础列名,但考虑到您的应用程序,我怀疑这是一个问题)。

  3. 执行一些输入清理,例如:

    mb_regex_encoding($charset); // charset of database connection
    $sql .= 'FROM A JOIN B ON A.`' . mb_ereg_replace('`', '``', $_POST['join_a']) . '`'
                        . ' = B.`' . mb_ereg_replace('`', '``', $_POST['join_b']) . '`'
    

    虽然我们在这里引用用户输入并替换用户试图逃避该引用的任何尝试,但这种方法可能充满各种缺陷和漏洞(在 PHP 的mb_ereg_replace函数或 MySQL 对引用标识符中特制字符串的处理中)。

    如果完全可以使用上述方法之一来避免将用户定义的字符串完全插入到一个人的 SQL 中,那会好得多。

于 2012-06-07T06:09:11.520 回答
1

假设用户输入仅限于选择表格和字段(即没有附加条件),您应该可以使用您的方法;听起来不错 :)

我想补充的一件事是某些连接比其他连接更好。例如,使用它们的主键(或其他索引)连接两个表将比需要全表扫描的两个不相关的列执行得更好。

这一切首先取决于桌子有多大。对于少于几千条记录,您应该没问题;任何超出认真考虑的事情都已到位:)

于 2012-06-07T06:06:48.760 回答