如何内爆这些条件来实现这种查询结构?

2022-08-30 23:26:17

以下是我的工作查询,它在表单提交后生效。仅当所有文本框都已填满时,我的查询才有效,因此目前所有内容都是必需的。

工作查询

SELECT behaviour.hash, 
       Sum(behaviour.timespent) AS timeSpent, 
       new_table.percentile_rank, 
       Count(*) AS total 
FROM   behaviour, 
       audience, 
       new_table 
WHERE  ( $url ) 
       AND behaviour.timestamp >= Date_sub(Curdate(), INTERVAL $last_visit day) AND behaviour.timestamp < Date_add(Curdate(), INTERVAL 1 day) 
       AND behaviour.hash = audience.hash 
       AND behaviour.hash = new_table.hash 
       AND audience.country = '$from_country' 
GROUP  BY behaviour.hash 
HAVING Count(*) >= $more_than 
       AND timespent >= $time_spent 
       AND new_table.percentile_rank >= $lead_scoring 

我想实现的不是要求用户填写所有文本框才能提交,而只是要求他喜欢的文本框。所以我构建了以下内容,但它有一些错误。

我的问题是我的查询有一个子句,所以不是每个子句都像我现在这样连接(看看我的代码下面)。因此,提交的 第一个 or 或 文本框中,它必须具有 而不是 .havingconditionAND$more_than$time_spent$lead_scoringHAVINGAND

如何编辑我的代码才能达到这个“特殊条件”?

我的代码

$url= 'url="'.implode('" OR url="', $vals).'"';

$conditions = array();

if (!empty($last_visit)) $conditions[] = "behaviour.TIMESTAMP >= DATE_SUB( CURDATE( ) , INTERVAL '".$last_visit."' DAY) AND behaviour.TIMESTAMP < DATE_ADD( CURDATE( ) , INTERVAL 1 DAY ) ";
if (!empty($from_country)) $conditions[] = "audience.country = '".$from_country."'";
if (!empty($more_than)) $conditions[] = "COUNT( * ) >= '".$more_than."'"; 
if (!empty($time_spent)) $conditions[] = "timeSpent >= '".$time_spent."'";
if (!empty($lead_scoring)) $conditions[] = "new_table.percentile_rank >= '".$lead_scoring."'";


$conditionString = implode(' AND ', $conditions);


$sql = "SELECT behaviour.hash, 
       Sum(behaviour.timespent) AS timeSpent, 
       new_table.percentile_rank, 
       Count( * ) AS total 
FROM   behaviour, 
       audience, 
       new_table 
WHERE  ($url) AND ".$conditionString;

电流输出

在下面的示例中,除文本框外的所有文本框都已填充。问题是,相反,应该是more_thanAND timespent >= '20'HAVING timespent >= '20'

SELECT behaviour.hash, 
       SUM(behaviour.timespent) AS timeSpent, 
       new_table.percentile_rank, 
       Count(*) AS total 
FROM   behaviour, 
       audience, 
       new_table 
WHERE  ( url = "/10369" ) 
       AND behaviour.timestamp >= Date_sub(Curdate(), interval '3' day) 
       AND behaviour.timestamp < Date_add(Curdate(), interval 1 day) 
       [missing]     AND behaviour.hash = audience.hash
       [missing]     AND behaviour.hash = new_table.hash
       AND audience.country = 'it' 
       [missing]     GROUP BY behaviour.hash
       [wrong]       AND timespent >= '20' ////// it should be HAVING /////
       AND new_table.percentile_rank >= '30'

答案 1

首先,必须确保无法进行 SQL 注入。为此,让我们使用PDO。

接下来,要解决实际问题,只需创建两个具有条件的列表即可。一个包含您希望在查询部分具有的条件,另一个具有需要在查询部分中包含的条件。WHEREHAVING

    $pdo = new PDO(/* See http://php.net/manual/en/pdo.construct.php */);

    $whereConditions = [];
    $havingConditions = [];
    $parameters = [];

    if (!empty($last_visit)) {
        $whereConditions[] = "behaviour.TIMESTAMP >= DATE_SUB( CURDATE( ) , INTERVAL :last_visit DAY) AND behaviour.TIMESTAMP < DATE_ADD( CURDATE( ) , INTERVAL 1 DAY ) ";
        $parameters['last_visit'] = $last_visit;
    }
    if (!empty($from_country)) {
        $whereConditions[] = "audience.country = :from_country";
        $parameters['from_country'] = $from_country;
    }
    if (!empty($more_than)) {
        $havingConditions[] = "COUNT( * ) >= :more_than";
        $parameters['more_than'] = $more_than;
    }
    if (!empty($time_spent)) {
        $havingConditions[] = "timeSpent >= :time_spent";
        $parameters['time_spent'] = $time_spent;

    }
    if (!empty($lead_scoring)) {
        $havingConditions[] = "new_table.percentile_rank >= :lead_scoring";
        $parameters['lead_scoring'] = $lead_scoring;
    }

    if (count($vals)) {
        $escapedUrlList = implode(', ', array_map(function ($url) use ($pdo) {
            return $pdo->quote($url);
        }, $vals));
        $whereConditions[] = "url IN($escapedUrlList)";
    }

    $whereClause = count($whereConditions) ? ' AND ' . implode(' AND ', $whereConditions) : '';
    $havingClause = count($havingConditions) ? ' HAVING ' . implode(' AND ', $havingConditions) : '';

    $statement = $pdo->prepare("
        SELECT behaviour.hash, 
            Sum(behaviour.timespent) AS timeSpent, 
            new_table.percentile_rank, 
            Count(*) AS total 
        FROM behaviour, 
            audience, 
            new_table 
        WHERE behaviour.hash = audience.hash 
            AND behaviour.hash = new_table.hash 
            {$whereClause}
        GROUP  BY behaviour.hash
        {$havingClause}
    ");

    $result = $statement->execute($parameters);

答案 2

这里有一个有点“棘手”的方法(虽然看起来很干净),它使用预准备的语句。我添加了一些通用的“功能”,以防将来发生更改。阅读带有解释的评论(我认为这样会更方便):

//assume established PDO connection - example:
try {
    $pdo = new PDO("mysql:dbname={$database_name};host=localhost", $user, $password);
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}

//static: conditional strings without parameters check (no keys required)
//conditional: assoc - keys should match both query placeholders and variable names
$static_where = [];
$optional_where = [
    'last_visit'   => 'behaviour.TIMESTAMP >= DATE_SUB(CURDATE(), INTERVAL :last_visit DAY) AND behaviour.TIMESTAMP < DATE_ADD(CURDATE(), INTERVAL 1 DAY)',
    'from_country' => 'audience.country = :from_country'
];

$static_having = [];
$optional_having = [
    'more_than'    => 'COUNT(*) >= :more_than',
    'time_spent'   => 'timeSpent >= :time_spent',
    'lead_scoring' => 'new_table.percentile_rank >= :lead_scoring'
];

//params: query parameters array - assigned manually + resolved from optional variables
$params = [];

//resolve condition from $urls array
if (count($urls) == 1) {
    $static_where[] = 'url = :url';
    $params['url'] = reset($urls);
} else if (!empty($urls)) {
    foreach ($urls as $idx => $url) {
        $params['url' . $idx] = $url;
    }
    $static_where[] = 'url IN(:' . implode(', :', array_keys($params)) . ')';
}

//filtering existing params used in query
//empty() is not a good idea for general purpose though,
//because some valid values might be recognised as empty (int 0, string '0')
$params += array_filter(
    compact(array_keys($optional_where), array_keys($optional_having)),
    function ($value) { return !empty($value); }
);

//concatenating conditional strings
//with corresponding params that weren't filtered out
//or these without params (static)
$where_clause = implode(' AND ', $static_where + array_intersect_key($optional_where, $params));
$having_clause = implode(' AND ', $static_having + array_intersect_key($optional_having, $params));

//don't need clauses without conditions - same as if (!empty($where)) {...}
empty($where_clause) or $where_clause = 'WHERE ' . $where_clause;
empty($having_clause) or $having_clause = 'HAVING ' . $having_clause;

$sql = "SELECT 
          behaviour.hash,
          Sum(behaviour.timespent) AS timeSpent,
          new_table.percentile_rank,
          Count( * ) AS total 
        FROM behaviour,
        INNER JOIN audience ON behaviour.hash = audience.hash,
        INNER JOIN new_table ON behaviour.hash = new_table.hash 
        {$where_clause}
        GROUP BY behaviour.hash 
        {$having_clause}";

//PDO part
$query = $pdo->prepare($sql);
$result = $query->execute($params);
//...

推荐