mysqldrupal-7dynamic-queries

How to preserve the order of the fields to be selected when using $query->addExpression()


I'm using Drupal 7 and I have to make a union on multiple tables. For the union to work some conditions have to be met:

  1. same number of columns
  2. same data type
  3. same order

Some of the tables are missing a column so in order to compensate for that I just add it with something like this: $query->addExpression(':field_1', 'field_1', array(':field_1' => NULL));. So at this point condition 1 & 2 are satisfied, but the order of the fields in the select is different.

See example bellow:

  $query_1 = db_select('table_one', 't1');
  $query_1->fields('t1', array('field_1', 'field_2'));

  $query_2 = db_select('table_two', 't2');
  if (true) {
    $query_2->fields('t2', array('field_1'));
  } else {
    $query_2->addExpression(':field_1', 'field_1', array(':field_1' => NULL));
  }
  $query_2->fields('t2', array('field_2'));        

  $query_3 = db_select('table_three', 't3');
  if (false) {
    $query_3->fields('t3', array('field_1'));
  } else {
    $query_3->addExpression(':field_1', 'field_1', array(':field_1' => NULL));
  }
  $query_3->fields('t3', array('field_2'));

And the result is:

// dpq($query_1);
SELECT t1.field_1 AS field_1, t1.field_2 AS field_2
FROM {table_one} t1

// dpq($query_2);
SELECT t2.field_1 AS field_1, t2.field_2 AS field_2
FROM {table_two} t2

// dpq($query_3);
SELECT t3.field_2 AS field_2, '' AS field_1
FROM {table_three} t3

// dpq($query_1->union($query_2)->union($query_3));
SELECT t1.field_1 AS field_1, t1.field_2 AS field_2
FROM {table_one} t1 
UNION SELECT t2.field_1 AS field_1, t2.field_2 AS field_2
FROM {table_two} t2 
UNION SELECT t3.field_2 AS field_2, '' AS field_1
FROM {table_three} t3

Seems that using $query->addExpression will not guarantee the position of the field to be where you'd expect it to be. Look at the dump of the 3rd query where addExpression has been used. Any ideas on how to solve this issue?


Solution

  • This is the only workaround I found to keep the order. Use an expression even if that field exists or not. That way the order is the one that you'd expect:

    if (TRUE) {
      // To keep the order of the fields we have to use this hacky way.
      // Use a function that will not modify the string
      $query->addExpression("IFNULL(table_name.field_name, '')", 'field_name_alias');
    } else {
      $query->addExpression(':field', 'field_name_alias', array(':field' => NULL));
    }