【问题标题】:What is faster? SQL query with wildcards and ORs, or catch-all query with php loop for filtering?什么更快?使用通配符和 OR 的 SQL 查询,还是使用 php 循环进行过滤的全面查询?
【发布时间】:2015-04-19 06:54:22
【问题描述】:

我正在对单个表运行查询。我需要最终结果是一个行数组,其中当前用户被列为数据库行记录的消息中的参与者。两个参与者列是 staffclients,并且包含用户 ID 和/或角色名称的序列化数组(例如,a:3:{i:0;s:8:"staffman";i:1;s:3:"203";i:2;s:3:"170";})。

在 SQL 查询中尝试过滤掉当前用户的所有不匹配项,或者执行一个全面查询(可能在数百行上),然后循环遍历它们以过滤掉用户不是参与者的那些?

SQL 选项:

$query = "SELECT * FROM ".self::$messages;
if($is_client) $query .= " WHERE type = 'clients'";
elseif($is_staff) $query .= " WHERE type = 'staff' AND (staff LIKE '%\"".$user->ID."\"%' OR staff LIKE 'staff\"%')";
elseif(!$is_admin)
{
    $query .= " WHERE type = 'staff' AND (staff LIKE '%\"".$user->ID."\"%' OR staff LIKE 'staff\"%' OR staff LIKE 'clientman%'";
    if($is_staffman) $query .= " OR staff LIKE 'staffman%'";
    if($is_accountant) $query .= " OR staff like 'accountant%'";
    $query .= ")";
}
$query .= " ORDER BY updated DESC";

循环选项:

$col = $is_client ? 'clients' : 'staff';
$query = "SELECT * FROM ".self::$messages." WHERE type = {$col} ORDER BY updated DESC";
// do the query, then check if there are results. if results...     
foreach($results as $msg)
{
    if(empty($msg->$col)) continue;
    $users = unserialize($msg->$col);
    if($is_client)
    {
        if(!in_array($user->ID, $users) && !in_array('client', $users)) continue;   
    }
    elseif(!$is_admin && $msg->created_by != $user->ID)
    {
        if(!in_array($user->ID, $users))
        {
            if(!in_array('staff', $users))
            {
                if(!$is_clientman || !in_array('clientman', $users))
                {
                    if(!$is_staffman || !in_array('staffman', $users))
                    {
                        if(!$is_accountant || !in_array('accountant', $users)) continue;
                    }
                }
            }
        }
    }
    // match found, do stuff with it here
}

【问题讨论】:

  • 很难预测像这样的复杂操作的性能。尝试两种方式并对其进行基准测试。
  • 对您的备选方案进行基准测试。数据库可以快速处理这样的事情,而 php 非常慢,所以只需猜测一下,查询选项很可能是更快的选项。但是基准测试总是一件好事。
  • Sql 仍然会更快,即使它正在执行扫描。但是,如果您能够将 JSON 对象规范化为与列的关系,那么如果您避免 LIKE 比较,性能将会大大提高。如果您继续存储 JSON,您可能会查看 MongoDB 以获取此数据——它将能够索引有效负载。
  • Stuart,因此标准化将涉及,您建议,在不同的表中为参与者(员工、客户)数组的每个元素创建一行,引用每一行中的消息 ID,一个每个用户的行?这就是你在说的吗?所以这会比在单行上做 LIKE 更快,对吧?
  • 测试时哪个更快?

标签: php mysql sql loops wildcard


【解决方案1】:

在这种情况下,您应该始终使用 SQL 选项。

$query = "SELECT * FROM ".self::$messages;
if($is_client) $query .= " WHERE type = 'clients'";
elseif($is_staff) $query .= " WHERE type = 'staff' AND (staff LIKE '%\"".$user->ID."\"%' OR staff LIKE 'staff\"%')";
elseif(!$is_admin)
{
    $query .= " WHERE type = 'staff' AND (staff LIKE '%\"".$user->ID."\"%' OR staff LIKE 'staff\"%' OR staff LIKE 'clientman%'";
    if($is_staffman) $query .= " OR staff LIKE 'staffman%'";
    if($is_accountant) $query .= " OR staff like 'accountant%'";
    $query .= ")";
}
$query .= " ORDER BY updated DESC";

为什么?

1.通过 WHERE 子句过滤,您已经获得了更少的记录(仅需要这些记录)。获取所有记录并在 php 中过滤是没有意义的。

2. 看到在 PHP 中管理代码变得很头疼。

3.更少的行数。

4.大部分时间由SQL管理,php逻辑会慢。

【讨论】:

    【解决方案2】:

    我接受了上面的答案,因为它是对我提出的问题的明确而明确的答案。但是,Stuart(在 cmets 中)建议我尝试规范化我的结构,而不是使用 LIKES 等扫描序列化数组。这就是我所做的。

    我没有将我的参与者数组以序列化形式保存在与messages 表相同的表中,而是创建了一个名为message_recipients 的新表,其中只有三列(id,mid,user),其中mid 指的是id messages 表中的列,其中 user 存储个人用户 ID 或角色名称。

    因此,最棘手的部分(实际上并没有那么棘手)是当用户更改给定消息的允许收件人时,如何管理在message_recipients 表中添加和删除行。 (在表格中,他们有两个多选下拉菜单——一个用于员工,一个用于客户)。

    以下是我管理更新/更改参与者部分的方式:

    对于每个字段(员工和客户),我将两个数组传递给我的函数,一个用于新参与者,一个用于旧参与者。 (如果正在创建消息,那么旧的参与者数组就是空的。)

    然后是这个函数(基本上就是四行实际工作):

    public function update_participants($mid = false, $new_staff = array(), $old_staff = array(), $new_clients = array(), $old_clients = array())
    {
        global $wpdb;
        if(empty($mid)) return array();
        $add = array_filter(array_unique(array_merge(array_diff($new_staff, $old_staff), array_diff($new_clients, $old_clients))));
        $delete = array_filter(array_unique(array_merge(array_diff($old_staff, $new_staff), array_diff($old_clients, $new_clients))));
        if(!empty($add)) $wpdb->query("INSERT INTO ".self::$participants." (mid,user) VALUES(".$mid.",\"".implode("\"),(".$mid.",\"", $add)."\")");
        if(!empty($delete)) $wpdb->query("DELETE FROM ".self::$participants." WHERE mid = {$mid} AND user IN (\"".implode("\", \"", $delete)."\")");
        return array('added'=>$add, 'deleted'=>$delete);
    }
    

    然后我返回添加或删除的人的结果数组,以便我可以在我的电子邮件通知内容中使用它。

    那么,既然我们已经规范化了,那么查询表以获取当前用户是参与者的所有消息的原始问题更快更简单:

    $query = 'SELECT DISTINCT a.* FROM '.self::$messages.' a, '.self::$participants.' b';
    if(!$is_admin) 
    {
        $query .= ' WHERE (b.user = '.$user->ID;
        if($is_client) $query .= ' OR b.user = "client"';
        elseif($is_staff) $query .= ' OR b.user = "staff"';
        else
        {
            if($is_clientman) $query .= ' OR b.user = "clientman"';
            if($is_staffman) $query .= ' OR b.user = "staffman"';
            if($is_accountant) $query .= ' OR b.user = "accountant"';
            $query .= ' OR a.created_by = '.$user->ID;
        }
        $query .= ') AND b.mid = a.id';
    }
    $query .= ' ORDER BY a.updated DESC';
    

    管理员获取所有信息,而其他所有人仅获取在参与者表中找到其用户 ID 或用户角色的任何消息。 (有些角色有多个角色,因此使用三重 if 语句,而不是 elseifs

    我已经对不同用户的所有可能配置进行了测试。奇迹般有效。

    没有 LIKE,没有通配符,也没有 PHP 循环。

    感谢大家的帮助。

    【讨论】:

      猜你喜欢
      • 2011-11-10
      • 2015-04-23
      • 2013-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多