I have a custom module that provides a default view. The view has an argument, set to display a summary (with record count) very similar to the archive view. What I've noticed is that the SQL generated always has the the COUNT(DISTINCT(records)), as opposed to only COUNT(records).

Before posting my views/SQL query, I wanted to see if this could possibly be a bug... In query.inc, note that the check for a distinct field uses an isset():

  function query($get_count = FALSE) {
...
      if (isset($field['distinct'])) {
        $string = "DISTINCT($string)";
      }
...

An argument of User: Name, use the views_handler_argument.inc which has the following:

function summary_basics($count_field = TRUE) {
...
    $distinct = ($this->view->display_handler->get_option('distinct') && empty($this->query->no_distinct));
...

$distinct, or more specifically, the $this->view->display_handler->get_option('distinct') in this case returns FALSE, which when put through an isset() like the one from query.inc will return TRUE, which will then result in the "DISTINCT" function being added to the query. Looking closer at the function get_option(), it seems is where the FALSE return is being generated.

As for a fix, I'm not sure whether it makes sense to remove the isset() and replace it with a more vanilla boolean check (probably not but that what the attached patch does), or if we should just make sure to pass NULL, instead of FALSE, if we don't want a distinct to be added to the query. I guess the other option is to have view->display_handler->get_option('distinct') return NULL, and not FALSE.

Now for the views, and SQL:

$view = new view;
$view->name = 'testing';
$view->description = '';
$view->tag = '';
$view->view_php = '';
$view->base_table = 'sa_reporting';
$view->is_cacheable = FALSE;
$view->api_version = 2;
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
$handler = $view->new_display('default', 'Defaults', 'default');
$handler->override_option('fields', array(
  'timestamp' => array(
    'label' => 'Date of the view',
    'alter' => array(
      'alter_text' => 0,
      'text' => '',
      'make_link' => 0,
      'path' => '',
      'alt' => '',
      'prefix' => '',
      'suffix' => '',
      'help' => '',
      'trim' => 0,
      'max_length' => '',
      'word_boundary' => 1,
      'ellipsis' => 1,
      'html' => 0,
    ),
    'date_format' => 'small',
    'custom_date_format' => '',
    'exclude' => 0,
    'id' => 'timestamp',
    'table' => 'sa_reporting',
    'field' => 'timestamp',
    'relationship' => 'none',
  ),
  'nid' => array(
    'label' => 'Nid',
    'alter' => array(
      'alter_text' => 0,
      'text' => '',
      'make_link' => 0,
      'path' => '',
      'alt' => '',
      'prefix' => '',
      'suffix' => '',
      'help' => '',
      'trim' => 0,
      'max_length' => '',
      'word_boundary' => 1,
      'ellipsis' => 1,
      'html' => 0,
    ),
    'link_to_node' => 0,
    'exclude' => 0,
    'id' => 'nid',
    'table' => 'node',
    'field' => 'nid',
    'relationship' => 'none',
  ),
));
$handler->override_option('arguments', array(
  'name' => array(
    'default_action' => 'summary asc',
    'style_plugin' => 'default_summary',
    'style_options' => array(
      'count' => 1,
      'override' => 0,
      'items_per_page' => '25',
    ),
    'wildcard' => 'all',
    'wildcard_substitution' => 'All',
    'title' => '',
    'default_argument_type' => 'fixed',
    'default_argument' => '',
    'validate_type' => 'none',
    'validate_fail' => 'not found',
    'glossary' => 0,
    'limit' => '0',
    'case' => 'none',
    'path_case' => 'none',
    'transform_dash' => 0,
    'id' => 'name',
    'table' => 'users',
    'field' => 'name',
    'relationship' => 'none',
    'default_options_div_prefix' => '',
    'default_argument_user' => 0,
    'default_argument_fixed' => '',
    'default_argument_php' => '',
    'validate_argument_node_type' => array(
      'page' => 0,
      'story' => 0,
    ),
    'validate_argument_node_access' => 0,
    'validate_argument_nid_type' => 'nid',
    'user_argument_type' => '',
    'restrict_user_roles' => 0,
    'user_roles' => array(),
    'validate_argument_php' => '',
  ),
));
$handler->override_option('access', array(
  'type' => 'none',
));
$handler->override_option('style_plugin', 'table');
$handler->override_option('style_options', array(
  'grouping' => '',
  'override' => 1,
  'sticky' => 0,
  'order' => 'asc',
  'columns' => array(
    'timestamp' => 'timestamp',
    'nid' => 'nid',
  ),
  'info' => array(
    'timestamp' => array(
      'sortable' => 0,
      'separator' => '',
    ),
    'nid' => array(
      'sortable' => 0,
      'separator' => '',
    ),
  ),
  'default' => '-1',
));
$handler = $view->new_display('page', 'Page', 'page_1');
$handler->override_option('path', 'asdf');
$handler->override_option('menu', array(
  'type' => 'none',
  'title' => '',
  'description' => '',
  'weight' => 0,
  'name' => 'navigation',
));
$handler->override_option('tab_options', array(
  'type' => 'none',
  'title' => '',
  'description' => '',
  'weight' => 0,
));

SQL:

SELECT users.name AS users_name,
   COUNT(DISTINCT(sa_reporting.nid)) AS num_records
 FROM sa_reporting sa_reporting 
 LEFT JOIN node node ON sa_reporting.nid = node.nid
 LEFT JOIN users users ON node.uid = users.uid
  GROUP BY users_name
  ORDER BY users_name ASC

If anyone else is facing a similar problem, the workaround I used was to create a custom argument handler, and overrode, function summary_basics and changed this:

...
    $count_alias = $this->query->add_field(NULL, $field, 'num_records',
                                           array('count' => TRUE, 'distinct' => $distinct));
..

To look like this:

...
    $count_alias = $this->query->add_field(NULL, $field, 'num_records',
                                           array('count' => TRUE, 'distinct' => NULL));
...
CommentFileSizeAuthor
#2 distinct_followup.patch665 bytesDavid_Rothstein
distinct.patch628 bytesbcn
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

merlinofchaos’s picture

Status: Active » Fixed

This should use !empty() rather than a simple boolean check; the simple boolean check can cause notices. Otherwise I believe this is likely the correct fix.

Committed.

David_Rothstein’s picture

Status: Fixed » Needs review
FileSize
665 bytes

I came across this while trying to help someone at #375298: Pager misses records when multiple value fields are displayed with other fields. It looks to me like there is actually one more case of isset() vs !empty() in the same function that was missed in the above commit.

A followup patch is attached. It should be pretty trivial, but those are famous last words :)... The effect of the patch should be that the count_query will now be optimized in situations where it was needlessly un-optimized before.

merlinofchaos’s picture

Status: Needs review » Fixed

Committed! Thanks!

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.