This is a tutorial of creating aggregated SQL queries (the ones with a "GROUP BY" clause) in Views2. At this time, to achieve this task you must install the Views GroupBy contributed module.

Top Terms View

Views is just great most of the time, but sometimes, for complex queries, it falls short. Not all SQL queries can be represented even with a query builder as sophisticated as Views2. One of the things you used to not be able to build was an aggregate query. It's a query when you want to group fields by some criteria and run a function on the group. For instance: group nodes by term name and count how many nodes are in each group. This is a query for Term Frequency.

Real-life example: List top 10 most frequent terms applied to a specific node type and the number of nodes for each term.

Building such view used to be impossible with Views2, unless you wrote some glue code. Not anymore. Following is a step-by-step configuration instructions for building term frequency view. Similar aggregated queries can be built in almost identical way.

  1. Create a new Node View
  2. Add following fields:
    • Taxonomy: Term Term
    • Taxonomy: Vocabulary Name
    • Node: Nid Nid (and rename its label to Nid Count)
    • SQL Aggregation: Group By Fields Group By Fields (must be last field!)
  3. Add a field named "SQL Aggregation: Group By Fields" and configure its settings as follows:
    • Fields to Group On = 'Taxonomy: Term' and 'Taxonomy: Vocabulary Name' (both fields)
    • SQL Aggregation Function = Count
    • Fields to Aggregate with the SQL function = Node: Nid
    • Field to Sort Resultset On (after SQL Function is applied) = Node: Nid
    • Sorting Direction: Descending
  4. Add a filtering criteria for the node type if you need to.

Which will produce the following SQL:

SELECT COUNT(node.nid) AS nid,
   term_data.name AS term_data_name,
   vocabulary.name AS vocabulary_name,
   'term_frequency:page_1' AS view_name
 FROM node node 
 LEFT JOIN term_node term_node ON node.vid = term_node.vid
 LEFT JOIN term_data term_data ON term_node.tid = term_data.tid
 LEFT JOIN vocabulary vocabulary ON term_data.vid = vocabulary.vid
  GROUP BY term_data_name, vocabulary_name, term_data_name, vocabulary_name, view_name
  ORDER BY nid DESC

which is almost an ideal query for the task, granted that we are using a cross-database query builder and it has some natural limitations.

The output of the given query may look something like when rendered with HTML List display plugin:

Term: gaza
Nid Count: 272
Vocabulary: Yahoo Terms
Term: israel
Nid Count: 126
Vocabulary: Yahoo Terms
Term: news articles
Nid Count: 88
Vocabulary: Yahoo Terms
Term: new york times
Nid Count: 50
Vocabulary: Yahoo Terms
Term: hamas
Nid Count: 47
Vocabulary: Yahoo Terms
Term: humanitarian crisis
Nid Count: 44
Vocabulary: Yahoo Terms
Term: financial crisis
Nid Count: 43
Vocabulary: Yahoo Terms

and of course you can theme the result-set any way you want using usual Views2 theming techniques.

Most Active FeedAPI Feeds View

Brief description of the approach:

  1. Let's assume your feed items have node type "feeditem"
  2. Create a view filtered by "feeditem" node type
  3. Add a FeedAPI relationship and add "Parent Feed's nid" and its title as one of the fields
  4. Group by parent feed's title and nid
  5. aggregate (count) by the feeditem's nid

One more thing...

You can also get a nice graphical chart (bar, pie, 3d... you name it) if you install and configure another Views extension, the Views Charts module.

Pretty cool?

Comments

karimahmed’s picture

When I tried to use this module it generated a GROUP BY with an additional field on the end that broke it:

e.g. The node_vid on the end of the GROUP BY field list.


SELECT
COUNT(node.nid) AS nid,
node_data_field_contractor.field_contractor_nid AS node_data_field_contractor_field_contractor_nid,
node_data_field_contractor.delta AS node_data_field_contractor_delta,
node.type AS node_type,
node.vid AS node_vid
FROM node node
LEFT JOIN content_field_contractor node_data_field_contractor
ON node.vid = node_data_field_contractor.vid
WHERE node.type IN('enquiry')
GROUP BY node_data_field_contractor_field_contractor_nid, node_data_field_contractor_delta, node_type, node_vid
ORDER BY node_data_field_contractor_field_contractor_nid DESC

nealeyoung’s picture

The extra fields that groupby is generating were also noted in this thread:

http://drupal.org/node/651546

and a patch that works (for me at least) is here:

http://drupal.org/node/651546#comment-2467362

texas-bronius’s picture

The Views Group By module is useful not only in the cases where all you want out of the view is a count but also for general Group By clause needs. You just have to throw away the output of the sum() function.

I have an Ubercart checkout screen, and on it, I am displaying all Orders for the current user along with all order details (node product title, product download links). The issue I was running into without some Group By clause is that the Order Id was repeating for each node represented in the cart.. Views Group By helped me to solve this by Group On Order Id, Aggregate with SQL Function on Order Creation Date. However, this shows Dec 1969 for all order dates... So I tossed in an additional Order Creation Date field (referred to as [created_1] in my Order By rewritten field), and life was good again.

--
http://drupaltees.com
80s themed Drupal T-Shirts

dman’s picture

If you just want to 'group' and list your results by criteria (say day, author or tag), you don't need views_groupby.
This is available in Views already, but not in 'Filter or 'Sort' where you may look for it - it's in the setting for "Style" in the "Basic Settings". There you can find a way to define a "Grouping Field"

texas-bronius’s picture

"For some reason" (yes, I wish I had taken better notes or written them out here when it was fresh), the Group feature provided by Views was just not doing it for me. I think it rewrote some output, making assumptions for me that I didn't need.. I don't recall at the moment. I just know this module saved the day!

To the previous responder: Again, I took horrible notes and have no intentions of looking back at this project I'm referring to until I'm old and grey and need a good cry! Maybe I will though..

--
http://drupaltees.com
80s themed Drupal T-Shirts

jay-dee-ess’s picture

For those of you having a problem with grouping in Views you may want to turn off Theme Developer if you have it enabled...

See: http://drupal.org/node/483080
Also: http://drupal.org/node/361298

Figured I'd post this here, since I wish I'd seen that first thread a couple of hours ago.

ceej23’s picture

Thanks dman - totally forgot about this.

altavis’s picture

You are absolutely right. I've also forgot about that ;)

cwithout’s picture

I just tried out your suggestion to use the built-in Views functionality, but the grouping in views is not the same as using a mysql "GROUP BY". Views grouping doesn't change the sql query. It only affects how the output is formatted. This module changes the sql query in order affect the results returned. Exactly what I was looking for.

A bit about mysql GROUP BY
http://www.w3schools.com/sql/sql_groupby.asp

jainrutgers’s picture

Hi,

I am trying to group the cck field (Type : Select list) but it seems its not working or
might be I am missing something.

Below is my sql code (generated by view)

SELECT COUNT(node.nid) AS nid,
node.type AS node_type,
node_data_field_type.field_type_value AS node_data_field_type_field_type_value,
node.vid AS node_vid
FROM node node
LEFT JOIN content_type_project node_data_field_type ON node.vid = node_data_field_type.vid
GROUP BY node_data_field_type_field_type_value, node_type, node_vid
ORDER BY nid ASC

In the result each node comes as separate unique item and they are not getting group by cck field for
some reason. Any help is highly appreciated.

linebreak’s picture

Hi Chetan

Not sure if you're still on this issue since your comment happens to be exactly one year old, but here are a few thoughts.

I'm having the same problem. Apparently the reason is that, when manipulating CCK fields, node.vid is added to the query in order to perform a JOIN. Group_by then adds this node.vid to the list of fields in the GROUP BY clause, which causes each node to appear as a distinct item.
I don't exactly know how to handle this, maybe group_by should avoid putting the JOINed fields in the GROUP BY clause - but this may not work all the time. I will file an issue in case further developments are made to this module one day.

Regards
Antoine

bshensky’s picture

Antoine:

I found a hacky workaround to get the appearance of node.vid from screwing up the aggregation in the SQL statement.

I am going to write some hook_views_query_alter() code to change the occurrence of node.vid in the SELECT list to NULL. The GROUP BY clause will effectively ignore the column then, and the COUNT() will evaluate as expected.

Seems a shame to have to write a module to fix a query, but it is what it is.

Stand by for my example.

-b

Michele Wickham’s picture

Thank you a million times over! This little module was a real time saver for me.

shaktik’s picture

SELECT node.nid AS nid,
node_data_field_from.field_from_nid AS node_data_field_from_field_from_nid,
node.type AS node_type,
node.vid AS node_vid,
node_data_field_to.field_to_nid AS node_data_field_to_field_to_nid,
node_data_field_vehicle_number.field_vehicle_number_value AS node_data_field_vehicle_number_field_vehicle_number_value,
node.created AS node_created,
node_data_field_delivery.field_delivery_value AS node_data_field_delivery_field_delivery_value
FROM node node
INNER JOIN content_type_challan node_data_field_delivery ON node.vid = node_data_field_delivery.vid
LEFT JOIN content_field_from node_data_field_from ON node.vid = node_data_field_from.vid
LEFT JOIN content_field_to node_data_field_to ON node.vid = node_data_field_to.vid
LEFT JOIN content_type_challan node_data_field_vehicle_number ON node.vid = node_data_field_vehicle_number.vid
WHERE (node.status <> 0) AND (node.type in ('challan')) AND (node.nid IN (772,773,618,818,6852,940,6855,771,869,770,768,767,766,86751082)) AND (node_data_field_delivery.field_delivery_value = 'Dispatched')";
ORDER BY node_created DESC

bug is:
Group By only node_data_field_vehicle_number,node_created
node_data_field_from.field_from_nid AS node_data_field_from_field_from_nid,
node.type AS node_type,
node.vid AS node_vid,
node_data_field_to.field_to_nid AS node_data_field_to_field_to_nid,
node_data_field_vehicle_number.field_vehicle_number_value AS node_data_field_vehicle_number_field_vehicle_number_value

shaktik’s picture

SELECT node.nid AS nid,
node_data_field_from.field_from_nid AS node_data_field_from_field_from_nid,
node.type AS node_type,
node.vid AS node_vid,
node_data_field_to.field_to_nid AS node_data_field_to_field_to_nid,
node_data_field_vehicle_number.field_vehicle_number_value AS node_data_field_vehicle_number_field_vehicle_number_value,
node.created AS node_created,
node_data_field_delivery.field_delivery_value AS node_data_field_delivery_field_delivery_value
FROM node node
INNER JOIN content_type_challan node_data_field_delivery ON node.vid = node_data_field_delivery.vid
LEFT JOIN content_field_from node_data_field_from ON node.vid = node_data_field_from.vid
LEFT JOIN content_field_to node_data_field_to ON node.vid = node_data_field_to.vid
LEFT JOIN content_type_challan node_data_field_vehicle_number ON node.vid = node_data_field_vehicle_number.vid
WHERE (node.status <> 0) AND (node.type in ('challan')) AND (node.nid IN (772,773,618,818,6852,940,6855,771,869,770,768,767,766,86751082)) AND (node_data_field_delivery.field_delivery_value = 'Dispatched')";
ORDER BY node_created DESC

bug is:
Group By only node_data_field_vehicle_number,node_created
node_data_field_from.field_from_nid AS node_data_field_from_field_from_nid,
node.type AS node_type,
node.vid AS node_vid,
node_data_field_to.field_to_nid AS node_data_field_to_field_to_nid,
node_data_field_vehicle_number.field_vehicle_number_value AS node_data_field_vehicle_number_field_vehicle_number_value

STNyborg’s picture

This issue has been moved to the Issues section: http://drupal.org/node/1142074