First off, thank you to everyone who has worked diligently on developing this module. I have made some amazing use of it and still do. Version 2 was a major leap forward for me. I am looking toward using version 3 and the D7 branch soon enough.

With the pleasantries aside, here comes the fun problem I recently ran into. I should predicate this with a disclaimer: I've spent days searching the net, your issues and several docs on how to deal with the VIEWS 2 API and trying to override a native feature of the module. I've also attempted to resolve my issue by installing a swath of modules that compliment VIEWS to try and solve this problem (things like views_groupby, views_or, data and the companion schema, and a few others), all in vein. I could not get the output, essentially the SQL query to match my requirements.

To solve this problem I eventually turned to the trusted hook_views_data() and hook_views_handlers() to get what I needed. I have no problems with that. Absolutely love the API. Where things broke down were when I started to play with the $this->query->add_groupby() function.

Here is a snippet of the handler routine I created:

/**
 * @defgroup views_field_handlers Views' field handlers
 * @{
 * Handlers to tell Views how to build and display fields.
 *
 */

/**
 * Specific SLSA field handler that renders an unformatted ceiling price.
 */
class slsa_handler_field_ceiling_price extends views_handler_field {
  
  /**
   * Called to add the field to a query.
   */
  function query() {
    $this->ensure_my_table();
    // Add the field.
    $this->field_alias = $this->query->add_field(NULL, "MAX(CAST(".$this->table_alias.".".$this->real_field." AS DECIMAL(14,2))) AS " . $this->table_alias . "_" . $this->real_field);

    $this->add_additional_fields();
    
    $groupby_fields = array('uc_products_model', 'node_data_field_slsa_manufacturer_field_slsa_manufacturer_value');
    $groupby = implode(', ', $groupby_fields);
    
    $this->query->add_groupby($groupby);
    $this->query->no_aggregates_in_groupby = TRUE;
  }
  
  /**
   * Render the field.
   *
   * @param $values
   *   The values retrieved from the database.
   */
  function render($values) {
    $value = $values->{$this->table_alias . "_" . $this->real_field};
    
    return check_plain($value);
  }
}

There are several things going on here, and I'll explain what I'm doing.

Using an example I found on the web, the line $this->field_alias = $this->query->add_field(NULL, "MAX(CAST(".$this->table_alias.".".$this->real_field." AS DECIMAL(14,2))) AS " . $this->table_alias . "_" . $this->real_field); is tricking views so I can have a MAX() statement in the query. No problem here.

The $groupby_fields stuff is no problem either in terms of it working. Where I did have problems was with what this function ($this->query->add_groupby($groupby)) does.

Here is an example of what VIEWS does with this when I do not have my boolean flag in ($this->query->no_aggregates_in_groupby):

  GROUP BY uc_products_model, node_data_field_slsa_manufacturer_field_slsa_manufacturer_value, nid, node_title, node_language, uc_products_model, node_data_field_slsa_manufacturer_field_slsa_manufacturer_value, node_type, node_vid, node_data_field_slsa_product_id_field_slsa_product_id_nid, node_data_field_slsa_product_id_field_slsa_supplier_ref_nid, MAX(CAST(content_type_product_supplier_join.field_slsa_ceiling_price_value AS DECIMAL(14,2))) AS content_type_product_supplier_join_field_slsa_ceiling_price_value

The key things to remark here are the extra GROUP BY fields I do not need and the inclusion of my altered field alias. Rather, I need the GROUP BY to read:

  GROUP BY uc_products_model, node_data_field_slsa_manufacturer_field_slsa_manufacturer_value

To get this to work, I had to patch the includes/query.inc with a small conditional boolean. What I was wondering is if there was already something like this I wasn't aware of? If so, how can I accomplish this without this patch? If not, is this of any use and could we have this rolled into the next release?

Thank you.

CommentFileSizeAuthor
no_aggregates_in_groupby.patch868 byteswilco

Comments

merlinofchaos’s picture

Status: Needs review » Closed (won't fix)

The final paramater to add_field() method you can put array('aggregate' => TRUE) to get the behavior you're trying to get.

wilco’s picture

Status: Closed (won't fix) » Needs review

Hi Merlin,

I did what you noted and I'm still getting the same result as before, just without the "MAX(CAST(content_type_product_supplier_join.field_slsa_ceiling_price_value AS DECIMAL(14,2))) AS content_type_product_supplier_join_field_slsa_ceiling_price_value" appearing in the GROUP BY. I do not consider this problem resolved. If you read closely, what I am actually asking about is how to remove all the aggregates except for the two that I added in the $this->query->add_groupby() function.

Hence the need for the flag. To ensure that all those previous fields do not get added to the final GROUP BY statement.

Is there another way around this?

merlinofchaos’s picture

Status: Needs review » Closed (won't fix)

You can't. ANSI SQL (and pgsql) requires that when GROUP BY is in use, any field in the SELECT must either 1) be an aggregate or 2) be in the GROUP BY. While it is true that MySQL allows this behavior to be ignored, Postgres does not. Therefore, Views absolutely enforces the rule.

And in Views 2, you don't have any control over aggregation from the UI.

If you search the queue, this has been complained about endlessly, and I don't feel like repeating the arguments over and over again. This isn't going to change.

If you want to hook_views_query_alter to set the aggregate flag on non-aggregate fields so you can cheat for mysql, feel free, but I'm not adding hacky flags like your patch.

wilco’s picture

Status: Closed (won't fix) » Needs review

I believe I now understand the problem. Could you please link a specific point in your discussions with others that highlights this point effectively? I would like to read further into it. I'm just not sure where to begin looking.

The other thing I'd like to understand here is the statement you wrote "hook_views_query_alter to set the aggregate flag on non-aggregate fields". What would be the best approach to solving this, something like this? Note: I am adding this code in a handler routine, so I'm not really using the hook_views_query_alter() function.

$this->field_alias = $this->query->add_field(NULL, 'node_title', array('aggregate' => TRUE));
$this->add_additional_fields();

As per trying to remove the "node_title" from the GROUP BY result?

merlinofchaos’s picture

Status: Needs review » Closed (won't fix)

You would probably need to use an alter anyway, because you don't really have any control over the other fields that get added. What's more, Views 2 will *always* add the primary key as a field even if it's not actually needed. (Views 3 doesn't do this, specifically because of aggregation).

If you look at $query->fields each field is an array. One of the keys in that array might be 'aggregate' => TRUE. You can, in a query alter, loop over $query->fields and add the aggregate flag to any field you want to fool Views into thinking is a group by.

This won't work on pgsql and I wouldn't recommend it in a module you want to publish, but if you want to make that view work properly on your site, that's an acceptable solution.

I don't have any links handy, but searching the views issue queue for group by will probably find a whole ton of issues, many of which contain various people unhappy about Views because they only know mysql's handling of GROUP BY and SELECT.

wilco’s picture

Thank you for this concise response. I will investigate.

wilco’s picture

Issue summary: View changes

Typos fixed