1

I have the following sql query

SELECT * FROM jos_jcalpro_events AS e 
  LEFT JOIN jos_jcalpro_events_seats AS s ON e.extid = s.extid
  LEFT JOIN jos_jcalpro_events_types_xref AS t ON e.extid = t.extid

which I execute via PDO from PHP. I create out of this another query to reinsert in the database. But it doesn't work. When I change LEFT from upper to lower case it does:

SELECT * FROM jos_jcalpro_events AS e 
  left JOIN jos_jcalpro_events_seats AS s ON e.extid = s.extid
  left JOIN jos_jcalpro_events_types_xref AS t ON e.extid = t.extid

In my creating routine there is no difference but the created INSERT has actually 2 lines less. How can that be?

Here is the important part of the code:

    $resource = JcalproDataImport::getInstance();

    // IMPORT
    $pdo = $resource->getConnection('import', 'pdo');
    $events = $pdo->query(
        'SELECT * FROM jos_jcalpro_events AS e '
        .'left JOIN jos_jcalpro_events_seats AS s ON e.extid = s.extid '
        .'left JOIN jos_jcalpro_events_types_xref AS t ON e.extid = t.extid;')->fetchAll(PDO::FETCH_ASSOC);

    // MAPPING
    $map = array(
        'picture'=>null, 'cat'=>null, 'day'=>null, 'month'=>null, 'year'=>null, 'recur_type'=>null, 'checked_out'=>null,
      'checked_out_time'=>null, 'payment_type'=>null, 'id'=>null,
      'extid'=>'id', 'contact'=>'contact_info', 'start_date'=>'start', 'end_date'=>'end', 'recur_val'=>'repeat_period',
      'recur_end_type'=>'repeat_end_type_id', 'recur_count'=>'repeat_end_after_occurrences',
      'recur_until'=>'repeat_end_after_date', 'wp_only'=>'only_in_wp', 'eticket'=>'prevent_sending_eticket',
      'region'=>'region_id', 'onlinebooking'=>'online_booking_form', 'organiser'=>'organiser_name'
    );

    // OUTPUT
    $pdo = $resource->switchConnection('export', 'pdo');
    $pdo->query('SET foreign_key_checks = 0');

    foreach ($events as $values)
    {
        $values['chargeable_status'] = (strtolower($values['payment_type']) == 'p') ? 1 : 0;
        $values['repeat_period_type_id'] = $repeat_period_type_map[$values['recur_type']];
        foreach($map as $from=>$to)
        {
            if ($to!== null){ $values[$to] = $values[$from]; }
            unset( $values[$from] );
        }
        $values['created_at'] = '2008-01-01';
        $values['updated_at'] = $values['created_at'];

          if (!$event) {
              foreach ($values as $field=>$value)
              {
                  $sql .= ', '.$field;
                  $sqlValues .= ', "'.mysql_real_escape_string($value).'"';
              }
              $sql = 'INSERT INTO event ( '.substr($sql, 2).' ) VALUES '.PHP_EOL.'( '.substr($sqlValues, 2).' )';
          }else{
              foreach ($values as $value)
              {
                  $sqlValues .= ', "'.mysql_real_escape_string($value).'"';
              }
              $sql .= ', ( '.substr($sqlValues, 2).' )';
          }
        $event[] = $values;
    }
    $pdo->query($sql)

But it works fine for other queries, so that I can't believe in a problem there.

Andreas
  • 2,694
  • 2
  • 21
  • 33
  • 4
    There is no difference between those queries. Please provide the PHP code that creates this problem. – Vincent Savard Aug 16 '11 at 03:40
  • 2
    You're creating an INSERT statement from a SELECT statement, and the INSERT isn't working, but you only posted the SELECT. There's not enough information here to answer the question. Look at the code that converts the SELECT into an INSERT; it probably has a bug. – Wyzard Aug 16 '11 at 03:41
  • As was previously said, SQL is a relatively case-insensitive language especially for keywords. So chances are you are doing something wrong elsewhere (i.e. The insert query being generated in PHP). – judda Aug 16 '11 at 03:55
  • The important part of the code is now there. But believe me or not, the code works fine for 5 other queries, build in the same way. Only this query causes problems and only if I change from lower to upper case. – Andreas Aug 16 '11 at 03:59
  • Mixing PDO with `mysql_real_escape_string()`, ewwwwwwww – Phil Aug 16 '11 at 04:00
  • @Phil: Sorry, but that's not a part of the problem. But if you have a better idea for escaping (this is necessary in this case), please let me know. I appreciate every suggestion for a better way to do it. – Andreas Aug 16 '11 at 04:04
  • @Andreas [`PDOStatement::bindParam()`](http://php.net/manual/en/pdostatement.bindparam.php). See [this question](http://stackoverflow.com/q/7062576/283366) for hints on binding a variable number of parameters – Phil Aug 16 '11 at 04:12
  • Even easier, `PDOStatement::execute()` takes an array of values, and he already has the values in an array `$values`. – Dan Grossman Aug 16 '11 at 04:17
  • @Phil: Many thanks, the escaping is obviously done by PDO::quote, but you are right, binding parameters is highly recommend. I will improve the code with that. – Andreas Aug 16 '11 at 04:18
  • @Dan: Thanks, I also have a look at this method. – Andreas Aug 16 '11 at 04:21
  • @Dan Grossman: Yes, but all those values would be considered to be strings. While this would simply not work for some parameters (i.e. `LIMIT :x` would fail if :x was a string), I find it not appropriate as you don't control the type of your variables. I really prefer the bindValue (or bindParam when necessary) method. – Vincent Savard Aug 16 '11 at 04:22
  • @Andreas No, the variables are not escaped at all. `PDOStatement::execute()` performs the binding at execution time (by value I think) – Phil Aug 16 '11 at 04:33

1 Answers1

2
  1. There is no reason left JOIN would work differently than LEFT JOIN. That's very unlikely to be related to your problem.

  2. Do your column name mapping with column aliases in your SELECT.

  3. Omit columns you don't want by naming those columns you do want, instead of using SELECT *.

  4. Prepare the INSERT once, with named parameters matching the column alias names. Then use execute($values) to insert each row fetched from the SELECT.

Here's approximately how I'd write it:

$map = array(
  'extid'=>'id', 
  'contact'=>'contact_info', 
  'start_date'=>'start', 
  'end_date'=>'end',
  'recur_val'=>'repeat_period', 
  'recur_end_type'=>'repeat_end_type_id', 
  'recur_count'=>'repeat_end_after_occurrences',
  'recur_until'=>'repeat_end_after_date',
  'wp_only'=>'only_in_wp',
  'eticket'=>'prevent_sending_eticket',
  'region'=>'region_id',
  'onlinebooking'=>'online_booking_form',
  'organiser'=>'organiser_name',
  'CURDATE()'=>'created_at',
  'CURDATE()'=>'updated_at',
);

$aliasize = function($alias, $column) { return "$column AS $alias"; }
$select_list = join(",", array_walk($aliasize, $map));

$events = $pdo->query("SELECT $select_list FROM ...")
    ->fetchAll(PDO::FETCH_ASSOC);

$column_list = join(",", array_values($map));
$parameterize = function($alias) { return ":$alias"; }
$param_list = join(",", array_map($parameterize, $map));

$insert = $pdo->prepare("INSERT INTO event ($column_list) VALUES ($param_list)");

foreach ($events as $values) {
    $insert->execute($values);
}
Bill Karwin
  • 538,548
  • 86
  • 673
  • 828
  • Many thanks Bill for the first answer, regarding this phenomenon. I also thought already about changing the general SELECT. You presumed right: Something in this SELECT must have caused the problem. When I changed SELECT * to specific SELECT the different behaviour between 'left JOIN' and 'LEFT JOIN' disappeared. Thanks also for your other points. For me, this is still an unsolved riddle, why in this special case, the lower case makes a difference. Weird! – Andreas Aug 16 '11 at 18:59
  • Case of SQL keywords does not make a difference. It's more likely that some other code change you made at the same time is responsible. – Bill Karwin Aug 16 '11 at 19:02
  • That's what I also thought but believe me or not: If I only change the case, without any other changes, the result is different. Thanks again! – Andreas Aug 16 '11 at 19:17