PHP,PDO,MYSQL查询WHERE子句的绑定语法涉及3个表

时间:2017-11-09 23:01:42

标签: php mysql pdo

我构建了一个SQL查询来提供股票持有的摘要,即使用PHP,PDO,MySQL对给定股票的所有股票交易进行分组。在测试PHPMyAdmin时,SQL工作正常(通过将命名参数从:c_code更改为c.code,:交换到" LSE",:p_portfolio_id到p.portfolio_id)

我不确定如何在这种情况下构造bindParam语句,或者如果我必须在查询中使用JOIN语句。我比较新,但学习速度很快,非常感谢任何帮助,Cheers Colin在这里是代码。

更改了代码返回没有错误但没有记录..

    <?php
include('db.php');
include('function.php');
$query = '';
$output = array();

$query .= "SELECT 
                t.user_id AS tuser_id,
                p.user_id AS puser_id,
                t.exchange AS exchange, 
                t.code AS code,
                c.name AS name, 
                p.name AS portfolio,
                CEILING(c.price * t.quantity / 100) AS value,
                DATE_FORMAT(t.trade_date,'%d%m%y') AS trade_date, 
                t.type AS type,
                SUM(t.quantity) AS quantity, 
                t.price AS price, 
                SUM(t.commission) AS commission,
                SUM(t.total_cost) AS total_cost
                FROM 
                transaction t, company c, portfolio p 
                WHERE
                t.code = :c_code
                AND
                t.exchange = :t_exchange
                AND
                t.portfolio_id = :p_portfolio_id 
                GROUP BY 
                t.code
                ORDER BY 
                t.code ";

if(isset($_POST["search"]["value"]))
{
 $query .= 'AND trade_date     LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR exchange         LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR code             LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR type             LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR quantity         LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR price            LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR commission       LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR total_cost       LIKE "%'.$_POST["search"]["value"].'%" ';
}
if(isset($_POST["order"]))
{
 $query .= 'ORDER BY '.$_POST['order']['0']['column'].' '.$_POST['order']['0']['dir'].' ';
}
else
{
 $query .= 'ORDER BY id DESC ';
}
if($_POST["length"] != -1)
{
 $query .= 'LIMIT ' . $_POST['start'] . ', ' . $_POST['length'];
}

$exchange = "LSE";

$statement = $connection->prepare($query);
$statement->bindParam(':t_exchange', $exchange, PDO::PARAM_STR);
$statement->bindParam(':c_code', $_POST['company.code'], PDO::PARAM_STR);
$statement->bindParam(':p_portfolio_id', $_POST['portfolio.id'], PDO::PARAM_STR);

$statement->execute();
$result = $statement->fetchAll();
$data = array();
$filtered_rows = $statement->rowCount();
foreach($result as $row)
{
 $sub_array = array();
 $sub_array[] = $row["trade_date"];
 $sub_array[] = $row["exchange"];
 $sub_array[] = $row["code"];
 $sub_array[] = $row["type"];
 $sub_array[] = $row["quantity"];
 $sub_array[] = $row["price"];
 $sub_array[] = $row["commission"];
 $sub_array[] = $row["total_cost"];
 $sub_array[] = '<button type="button" name="update" id="'.$row["id"].'" class="btn btn-warning btn-xs update">Update</button>';
 $sub_array[] = '<button type="button" name="delete" id="'.$row["id"].'" class="btn btn-danger btn-xs delete">Delete</button>';
 $data[] = $sub_array;
}
$output = array(
 "draw"    => intval($_POST["draw"]),
 "recordsTotal"  =>  $filtered_rows,
 "recordsFiltered" => get_total_all_records(),
 "data"    => $data
);
echo json_encode($output);
?>

下面的SQL在PHPMyAdmin中运行SQL工作正常

SELECT 
                t.user_id AS tuser_id,
                p.user_id AS puser_id,
                t.exchange AS exchange, 
                t.code AS code,
                c.name AS name, 
                p.name AS portfolio,
                CEILING(c.price * t.quantity / 100) AS value,
                DATE_FORMAT(t.trade_date,'%d%m%y') AS trade_date, 
                t.type AS type,
                SUM(t.quantity) AS quantity, 
                t.price AS price, 
                SUM(t.commission) AS commission,
                SUM(t.total_cost) AS total_cost
                FROM 
                transaction t, company c, portfolio p 
                WHERE
                t.code = c.code
                AND
                t.exchange = "LSE"
                AND
                t.portfolio_id = p.id 
                GROUP BY 
                t.code
                ORDER BY 
                t.code

我试图通过调用holdg_home.php中的holdg_fetch.php来集成一些推荐的代码,以便在表格中显示MySQL数据,但是我收到错误 &#34; DataTables警告:table id = transaction_data - 无效的JSON响应。 有关此错误的详细信息,请参阅http://datatables.net/tn/1 &#34; 我可以成功运行holdg_fetch.php,没有错误。我可以成功运行holdg_home.php调用trans_fetch.php,没有错误。

注意:一旦修复此JSON错误,我将从您的建议中挑选出安全性和结构。非常感谢Colin

trans_fetch.php

    <?php
include('db.php');
include('function.php');
$query = '';
$output = array();
$query .= "
SELECT 
*,
DATE_FORMAT(trade_date,'%d%m%y') AS trade_date
FROM 
transaction ";

if(isset($_POST["search"]["value"]))
{
 $query .= 'WHERE trade_date    LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR exchange         LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR code             LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR type             LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR quantity         LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR price            LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR commission       LIKE "%'.$_POST["search"]["value"].'%" ';
 $query .= 'OR total_cost       LIKE "%'.$_POST["search"]["value"].'%" ';
}
if(isset($_POST["order"]))
{
 $query .= 'ORDER BY '.$_POST['order']['0']['column'].' '.$_POST['order']['0']['dir'].' ';
}
else
{
 $query .= 'ORDER BY id DESC ';
}
if($_POST["length"] != -1)
{
 $query .= 'LIMIT ' . $_POST['start'] . ', ' . $_POST['length'];
}

$statement = $connection->prepare($query);
$statement->execute();
$result = $statement->fetchAll();
$data = array();
$filtered_rows = $statement->rowCount();
foreach($result as $row)
{
 $sub_array = array();
 $sub_array[] = $row["trade_date"];
 $sub_array[] = $row["exchange"];
 $sub_array[] = $row["code"];
 $sub_array[] = $row["type"];
 $sub_array[] = $row["quantity"];
 $sub_array[] = $row["price"];
 $sub_array[] = $row["commission"];
 $sub_array[] = $row["total_cost"];
 $sub_array[] = '<button type="button" name="update" id="'.$row["id"].'" class="btn btn-warning btn-xs update">Update</button>';
 $sub_array[] = '<button type="button" name="delete" id="'.$row["id"].'" class="btn btn-danger btn-xs delete">Delete</button>';
 $data[] = $sub_array;
}
$output = array(
 "draw"    => intval($_POST["draw"]),
 "recordsTotal"  =>  $filtered_rows,
 "recordsFiltered" => get_total_all_records(),
 "data"    => $data
);
echo json_encode($output);
?>

holdg_home.php

<?php include 'db.php'; ?>

<?php include("header-nav2.php"); ?>
<main>        
 <div class="container box">

   <br />
   <div class="table-responsive">
    <br />
    <div align="right">
     <button type="button" id="add_button" data-toggle="modal" data-target="#transactionModal" class="btn btn-warning"><i class="fa fa-plus" aria-hidden="true"></i></button>
    </div>
    <br />
    <table id="transaction_data" class="table table-bordered table-striped">
     <thead class="blue-grey lighten-4">
      <tr>
       <th width="11%">TradeDate</th>
       <th width="11%">Exchange</th>
       <th width="11%">Code</th>
       <th width="11%">Type</th>
       <th width="11%">Quantity</th>
       <th width="11%">Price</th>
       <th width="11%">Commission</th>
       <th width="11%">TotalCost</th>
       <th width="6%">Edit</th>
       <th width="6%">Delete</th>
      </tr>
     </thead>
    </table>
   </div>
  </div>



<?php include("footer.php"); ?>
 </body>
</html>



    <script type="text/javascript" language="javascript" >
    $(document).ready(function(){
     $('#add_button').click(function(){
      $('#transaction_form')[0].reset();
      $('.modal-title').text("Add Transaction");
      $('#action').val("Add");
      $('#operation').val("Add");
     });

     var dataTable = $('#transaction_data').DataTable({
      "processing":true,
      "serverSide":true,
      "order":[],
      "ajax":{
       url:"holdg_fetch.php",
       type:"POST"
      },
      "columnDefs":[
       {//removes sort from columns given by targets, where 0 - remove Column 1 sort etc.
        "targets":[8, 9],
        "orderable":false,
       },
      ],

     });
...

holdg_fetch.php

  <?php
include('db.php');
include('function.php');
$query = '';
$output = array();
$query .= "
    SELECT 
t.user_id AS tuser_id,
    p.user_id AS puser_id,
    t.exchange AS exchange, 
    t.code AS code,
    c.name AS name, 
    p.name AS portfolio,
    CEILING(c.price * t.quantity / 100) AS value,
    DATE_FORMAT(t.trade_date,'%d%m%y') AS trade_date, 
    t.type AS type,
    SUM(t.quantity) AS quantity, 
    t.price AS price, 
    SUM(t.commission) AS commission,
    SUM(t.total_cost) AS total_cost
    FROM 
    transaction t
    inner join  company c on (t.code=c.code)
    inner join portfolio p  on (t.portfolio_id = p.id)
    GROUP BY
    t.code";

//    WHERE
//    1=1

//if(isset($_POST["search"]["value"]))
//{
// $query .= ' WHERE trade_date    LIKE "%'.$_POST["search"]["value"].'%" ';
// $query .= 'OR exchange         LIKE "%'.$_POST["search"]["value"].'%" ';
// $query .= 'OR code             LIKE "%'.$_POST["search"]["value"].'%" ';
// $query .= 'OR type             LIKE "%'.$_POST["search"]["value"].'%" ';
// $query .= 'OR quantity         LIKE "%'.$_POST["search"]["value"].'%" ';
// $query .= 'OR price            LIKE "%'.$_POST["search"]["value"].'%" ';
// $query .= 'OR commission       LIKE "%'.$_POST["search"]["value"].'%" ';
// $query .= 'OR total_cost       LIKE "%'.$_POST["search"]["value"].'%" ';
//}
//if(isset($_POST["order"]))
//{
// $query .= 'ORDER BY '.$_POST['order']['0']['column'].' '.$_POST['order']['0']['dir'].' ';
//}
//else
//{
// $query .= 'ORDER BY id DESC ';
//}
//if($_POST["length"] != -1)
//{
// $query .= ' LIMIT ' . $_POST['start'] . ', ' . $_POST['length'];
//}

//$query .= ' GROUP BY t.code';

$statement = $connection->prepare($query);
$statement->execute();
$result = $statement->fetchAll();
var_dump($result);

$data = array();
$filtered_rows = $statement->rowCount();
foreach($result as $row)
{
 $sub_array = array();
 $sub_array[] = $row["trade_date"];
 $sub_array[] = $row["exchange"];
 $sub_array[] = $row["code"];
 $sub_array[] = $row["type"];
 $sub_array[] = $row["quantity"];
 $sub_array[] = $row["price"];
 $sub_array[] = $row["commission"];
 $sub_array[] = $row["total_cost"];
// $sub_array[] = '<button type="button" name="update" id="'.$row["id"].'" class="btn btn-warning btn-xs update">Update</button>';
// $sub_array[] = '<button type="button" name="delete" id="'.$row["id"].'" class="btn btn-danger btn-xs delete">Delete</button>';
 $data[] = $sub_array;
}
$output = array(
// "draw"    => intval($_POST["draw"]),
 //"recordsTotal"  =>  $filtered_rows,
 //"recordsFiltered" => get_total_all_records(),
 "data"    => $data
);
echo json_encode($output);
?>

我现在修复了JSON错误,数据和选定列不匹配,请忽略上面的错误,现在已修复

3 个答案:

答案 0 :(得分:1)

在我看来,动态生成的SQL存在问题。

    GROUP BY 
    t.code
    ORDER BY 
    t.code AND trade_date  LIKE "%foo%" OR exchange   LIKE "%foo%"

可能MySQL会接受语法,并允许处理查询。但是这些条件不是WHERE子句中的谓词。 ORDER BY之后的整行将被评估为单个布尔表达式,它将为每一行返回0,1或NULL。 ORDER BY将对布尔值进行操作。 (这是一个聪明的伎俩,一个方便的捷径,但我怀疑这里的代码并不意味着实现。)

如果我们想要为WHERE子句添加条件,那么需要在 GROUP BY之前。但是在WHERE子句中,我们不能引用SELECT列表中指定的别名。 (我们可以在HAVING条款中。)

请注意,AND的优先顺序高于OR

a AND b OR c (a AND b) OR c 相同而不是 a AND (b OR c)

向查询添加另一个ORDER BY绝对不是有效的SQL。像这样的结构,在语句​​末尾有两个ORDER BY子句

    GROUP BY 
    t.code
    ORDER BY 
    t.code ORDER BY whatever

将导致SQL语法错误。

问:如果我必须在查询中使用JOIN语句

查询已指定JOIN操作。逗号是JOIN操作的有效语法。这样:

    FROM 
    transaction t, company c, portfolio p
    WHERE
    t.code = c.code
    AND
    t.exchange = 'LSE'
    AND
    t.portfolio_id = p.id

是老式的写作我们现在写的东西:

    FROM transaction t
    JOIN company c
      ON c.code = t.code
    JOIN portfolio p 
      ON p.id = t.portfolio_id
   WHERE t.exchange = :t_exchange

请注意,我们只需要一个绑定占位符就可以将值绑定到那里。我们无法通过绑定占位符提供标识符(如列名)或其他SQL语法。

鉴于我们的绑定占位符是输入,我们可以使用bindValue代替bindParam

  $sth = $conn->prepare($sql);
  $sth->bindValue(':t_exchange', $exchange, PDO::PARAM_STR);

查询中提供的其他值也应添加为绑定占位符。 从不将可能不安全的值合并到SQL文本中。

我认为(没有规范,我只是在这里猜测)我们打算生成一个如下所示的查询:

    FROM transaction t
    JOIN company c
      ON c.code = t.code
    JOIN portfolio p 
      ON p.id = t.portfolio_id
   WHERE 1=1
     AND (  t.trade_date    LIKE CONCAT('%', :search_01 , '%')
         OR t.exchange      LIKE CONCAT('%', :search_02 , '%')
         OR t.code          LIKE CONCAT('%', :search_03 , '%')
         OR t.type          LIKE CONCAT('%', :search_04 , '%')
         )
   GROUP BY ...
   ORDER BY ... 

(我在字符串文字周围使用了SQL标准单引号。只要sql_mode不包含ANSI_QUOTES,MySQL就允许我们用双引号代替单引号。)

  $sth = $conn->prepare($sql);
  $sth->bindValue(':search_01', $_POST["search"]["value"], PDO::PARAM_STR);
  $sth->bindValue(':search_02', $_POST["search"]["value"], PDO::PARAM_STR);
  $sth->bindValue(':search_03', $_POST["search"]["value"], PDO::PARAM_STR);
  $sth->bindValue(':search_04', $_POST["search"]["value"], PDO::PARAM_STR);

(我将假设$_POST["search"]是一个数组,我们知道我们在这里做了什么,从数组中获取一个值。)

如果我们有条件地附加LIMIT条款......

 if( someconditition ) {
    $sql .= " LIMIT :nskip, :nrows";
 }

然后,当我们绑定参数值时,我们需要确定是否添加了LIMIT子句。检查相同的条件将是方便的(只要我们知道在我们做好准备之后,条件将在稍后评估相同的情况。)

  if( somecondition ) {
    $sth->bindValue(':nskip', (int)$_POST["start"] , PDO::PARAM_INT);
    $sth->bindValue(':nrows', (int)$_POST["length"], PDO::PARAM_INT);
  }

从不将可能不安全的值合并到SQL语句的文本中。

小鲍比表(对妈妈的利用)https://xkcd.com/327/

OWASP SQL注入https://www.owasp.org/index.php/SQL_Injection

答案 1 :(得分:0)

此代码存在一些问题:

  • 在添加更多where子句之前,在代码顶部的查询中按顺序排序依据(在if块中) - 这将导致错误。

  • 将like子句构造为字符串 - 您对SQL注入攻击持开放态度。您可以将like子句绑定为参数:

    $query .= 'where trade_date like :trade_date';
    //then bind it
    $statement->bindParam( ':trade_date', '%' . $_POST['search']['value'] . 
    '%', PDO::PARAM_STR );
    
  • 看起来您需要围绕搜索字词的where子句使用括号。

  • 通过代码预先定义订单并使用某种参数来创建您的查询 - 不要相信用户输入 - 您将被黑客攻击。

  • 使用显式联接 - 我想我已经在下面的代码中将它们更正了。

  • 使用绑定参数构造limit子句。

    $query .= "LIMIT :limitstart,:limitlength";
    $statement->bindValue(':limitstart', (int) trim( $_POST['start'] ), PDO::PARAM_INT );
    $statement->bindValue(':limitlength', (int) trim( $_POST['length'] ), PDO::PARAM_INT );
    

尝试此代码 - 调试除了执行查询,因为我没有你的数据库。

<?php

include('db.php');
include('function.php');

$searchFields = array( 'exchange', 'code','type','quantity', 'price', 'commission','total_cost' );

$output = array();

// i'm guessing at the join for portfolio below.
// i'm guessing that the portfolioid where clause needs to be removed because it was probably used only for joins
// where 1=1 will be optimized out by the query optimizer

$query = "SELECT 
    t.user_id AS tuser_id,
    p.user_id AS puser_id,
    t.exchange AS exchange, 
    t.code AS code,
    c.name AS name, 
    p.name AS portfolio,
    CEILING(c.price * t.quantity / 100) AS value,
    DATE_FORMAT(t.trade_date,'%d%m%y') AS trade_date, 
    t.type AS type,
    SUM(t.quantity) AS quantity, 
    t.price AS price, 
    SUM(t.commission) AS commission,
    SUM(t.total_cost) AS total_cost
    FROM 
    transaction t
    inner join  company c on (t.code=c.code)
    inner join portfolio p  on (t.portfolio_id = p.id)
    WHERE
    1=1
";

$bindSearch = false;
$searchValue = '';
if( array_key_exists( 'search', $_POST ) && array_key_exists( 'value', $_POST['search'] ) && ! empty( $_POST['search']['value'] ) )
{
    $bindSearch = true;
    $searchValue = $_POST['search']['value'];
    // 1=1 will be optimized out
    $query .= "\tAND (\n\t\t1=1\n";
    foreach( $searchFields as $field )
    {
            $query .= "\t\tOR $field  LIKE : $field\n";
    }
    $query .= "\t)\n";
}


if( array_key_exists( 'order', $_POST ) && ! empty( $_POST['order'] ) )
{
    switch ( $_POST['order']['0']['column'] )
    {
    case 'code':
        $query .= 'ORDER BY code ';
        break;
    default:
        $query .= 'ORDER BY id DESC ';
        break;
    }
}

$bindLimit = false;
if( array_key_exists( 'length', $_POST ) && $_POST['length'] > 0 )
{
    $bindLimit = true;
    $query .= "\n\tLIMIT :limitstart,:limitlength";
}
?>

<form method="POST">
    search: <input type="text" name="search[value]"><br>
    order: <input type="text" name="order[0][column]"><br>
    Limit Start: <input type="text" name="start"><br>
    Limit Length: <input type="text" name="length"><br>
    <button type="submit">Submit</button>
</form>

<?
echo "<pre>$query";

$statement = $connection->prepare($query);

$exchange = "LSE";
$statement->bindParam( ':exchange', $exchange, PDO::PARAM_STR );

if( $bindSearch )
{
    foreach( $searchFields as $field )
    {
        $statement->bindParam( ':' . $field, $searchValue , PDO::PARAM_STR );
    }
}

if( $bindLimit )
{
    $statement->bindValue(':limitstart', (int) trim( $_POST['start'] ), PDO::PARAM_INT );
    $statement->bindValue(':limitlength', (int) trim( $_POST['length'] ), PDO::PARAM_INT );
}

$statement->execute();
$result = $statement->fetchAll();
print_r( $result );
var_dump( $result );

$data = array();
$filtered_rows = $statement->rowCount();


foreach($result as $row)
{
    $sub_array = array();
    $sub_array[] = $row["trade_date"];
    $sub_array[] = $row["exchange"];
    $sub_array[] = $row["code"];
    $sub_array[] = $row["type"];
    $sub_array[] = $row["quantity"];
    $sub_array[] = $row["price"];
    $sub_array[] = $row["commission"];
    $sub_array[] = $row["total_cost"];
    $sub_array[] = '<button type="button" name="update" id="'.$row["id"].'" class="btn btn-warning btn-xs update">Update</button>';
    $sub_array[] = '<button type="button" name="delete" id="'.$row["id"].'" class="btn btn-danger btn-xs delete">Delete</button>';
    $data[] = $sub_array;
}
$output = array(
 "draw" => intval($_POST["draw"]),
 "recordsTotal"  =>  $filtered_rows,
 "recordsFiltered" => get_total_all_records(),
 "data" => $data
);
echo json_encode($output);

&GT;

这将生成以下查询:

SELECT 
t.user_id AS tuser_id,
p.user_id AS puser_id,
t.exchange AS exchange, 
t.code AS code,
c.name AS name, 
p.name AS portfolio,
CEILING(c.price * t.quantity / 100) AS value,
DATE_FORMAT(t.trade_date,'%d%m%y') AS trade_date, 
t.type AS type,
SUM(t.quantity) AS quantity, 
t.price AS price, 
SUM(t.commission) AS commission,
SUM(t.total_cost) AS total_cost
FROM 
transaction t
inner join  company c on (t.code=c.code)
inner join portfolio p  on (t.portfolio_id = p.id)
WHERE
1=1
AND (
    1=1
    OR exchange  LIKE : exchange
    OR code  LIKE : code
    OR type  LIKE : type
    OR quantity  LIKE : quantity
    OR price  LIKE : price
    OR commission  LIKE : commission
    OR total_cost  LIKE : total_cost
)
ORDER BY id DESC 

答案 2 :(得分:-4)

这太长了你可以在其中创建一个程序,你必须通过程序调用所有参数。 过程对于该类型的查询是完全有用的,并且也是安全的。 如果你只想选择你必须创建一个视图就可以使用sqlyog来帮助你加入你可以在设计的帮助下创建你的连接