When i use a views field handler and invoke $this->query->add_groupby('node_vid') in the query function, 'node_vid' is properly added to the query->groupby array.
However, when the SQL gets generated, the GROUP BY clause contains ALL the fields in the SELECT clause.
When I remove the add_groupby('node_vid') call, the GROUP BY clause disappears completely.
I tracked this issue down to this clause in views/includes/query.inc, lines 942 - 947:
if ($has_aggregate || $this->groupby) {
$groupby = "GROUP BY " . implode(', ', array_unique(array_merge($this->groupby, $non_aggregates))) . "\n";
if ($this->having) {
$having = $this->condition_sql('having');
}
}
Here's the SQL that that generates (unexpected):
SELECT node.nid AS nid,
my_module.fid AS my_module,
node.title AS node_title,
image_attach.iid AS image_attach_iid,
event.event_start AS event_event_start,
event.has_time AS event_has_time,
location.lid AS location_lid,
node_revisions.teaser AS node_revisions_teaser,
node_revisions.format AS node_revisions_format,
node.vid AS node_vid FROM node node
LEFT JOIN event event ON node.nid = event.nid
LEFT JOIN flag_content my_module ON node.nid = my_module.content_id
LEFT JOIN image_attach image_attach ON node.nid = image_attach.nid
LEFT JOIN location_instance location_instance ON node.vid = location_instance.vid
LEFT JOIN location location ON location_instance.lid = location.lid
LEFT JOIN node_revisions node_revisions ON node.vid = node_revisions.vid
WHERE (node.type in ('event')) AND (node.status <> 0) AND (UNIX_TIMESTAMP(event.event_start) >= 1233464400)
GROUP BY node_vid, nid, boboforce_flagimage_fid, node_title, image_attach_iid, event_event_start, event_has_time, location_lid, node_revisions_teaser, node_revisions_format
ORDER BY event_event_start DESC
This issue is solved 100% for me when I change the block above to the following:
if ($has_aggregate || $this->groupby) {
$groupby = "GROUP BY " . implode(', ', array_unique($this->groupby)) . "\n";
if ($this->having) {
$having = $this->condition_sql('having');
}
}
And here's the SQL that that generates (expected):
SELECT node.nid AS nid,
my_module.fid AS my_module,
node.title AS node_title,
image_attach.iid AS image_attach_iid,
event.event_start AS event_event_start,
event.has_time AS event_has_time,
location.lid AS location_lid,
node_revisions.teaser AS node_revisions_teaser,
node_revisions.format AS node_revisions_format,
node.vid AS node_vid FROM node node
LEFT JOIN event event ON node.nid = event.nid
LEFT JOIN flag_content my_module ON node.nid = my_module.content_id
LEFT JOIN image_attach image_attach ON node.nid = image_attach.nid
LEFT JOIN location_instance location_instance ON node.vid = location_instance.vid
LEFT JOIN location location ON location_instance.lid = location.lid
LEFT JOIN node_revisions node_revisions ON node.vid = node_revisions.vid
WHERE (node.type in ('event')) AND (node.status <> 0) AND (UNIX_TIMESTAMP(event.event_start) >= 1233464400)
GROUP BY node_vid
ORDER BY event_event_start DESC
I'm confused about this behavior, and i'm not sure if this is a bug or whether my lack of in-depth SQL knowledge is cropping up.
Why are the fields in $non_aggregates getting added to the GROUP BY clause?
Can anyone explain?
In any case, I'm attaching a diff in case anyone runs into this issue and wants to solve it, however temporarily or incorrectly.
Comment | File | Size | Author |
---|---|---|---|
views_group_by_hackaround.diff | 696 bytes | AaronBauman | |
Comments
Comment #1
merlinofchaos CreditAttribution: merlinofchaos commentedYes, and the 'expected' SQL that you generate is not ANSI legal SQL.
When using GROUP BY items that appear in the SELECT must either use aggregate functions OR appear in the GROUP BY. This is enforced by postgres, therefore Views must enforce this.
Comment #2
AaronBaumanmerlin:
And, as far as I can tell, there's no workaround that doesn't involve hacking the views core?
In other words, even though I use mysql exclusively, which doesn't mind my SQL query, there's no way for me to override this behavior?
thanks for the quick response,
/a
Comment #3
merlinofchaos CreditAttribution: merlinofchaos commentedThat is correct. Views enforces ANSI SQL where necessary. Whatever you're doing should not be adding a GROUP BY on node_vid.
Comment #4
andreiashu CreditAttribution: andreiashu commentedHi and sorry to reopen this up.
I need the same functionality from views and I'm banging my head to the desk because I cannot find a way to make this working.
I made a custom module that implements a Views Field from the {sessions} table: hostname.
Here is the query that gets generated:
but if a user is logged in from more than 1 browser/computer/location at the same this means that he will appear more than 1 time.
I tried using DISTINCT(users.uid) but it is not working. Also the add_groupby function does what aaronbauman said above.
So how can I mimic this behaviour ?
Comment #5
andreiashu CreditAttribution: andreiashu commentedI solved my issue with a prequery like: "SELECT sid FROM {sessions} GROUP BY uid" and then selected only those sids.
Thanks for this wonderful module merlin :)
Comment #6
andreiashu CreditAttribution: andreiashu commentedsetting the previous status
Comment #7
whitelancer CreditAttribution: whitelancer commentedOK, here's a question -- I've run into the same issue here, but can we change the order and have things work? My question is whether this would still be legal SQL.
If you take line 957:
$groupby = "GROUP BY " . implode(', ', array_unique(array_merge($this->groupby, $non_aggregates))) . "\n";
and flip the order of the merge:
$groupby = "GROUP BY " . implode(', ', array_unique(array_merge($non_aggregates, $this->groupby))) . "\n";
Comment #8
bwynants CreditAttribution: bwynants commentedI do not understand how I can fix this.
I have a table that has multiple matches for the relation with vid of node. I want to take out the match where a certain field is the highest /lowest value.
in the past I did
This won't work anymore now and I have no clue how to fix it....
Comment #9
mdixoncm CreditAttribution: mdixoncm commentedJust stumbled across this post when looking into an issue with views and the search index filter. Our view is pretty standard stuff, exposing the search index field and sorting by the search score - BUT - we are running into issues because the query generated by views adds every field in the SELECT portion of the query into the GROUP BY. We have this
The problem being that the groupby is too "strict" and the having condition is never met even if both search terms are found in the search index.
Comment #10
bendenoz CreditAttribution: bendenoz commentedJust spent some time solving the same problem...
The answer to bwynants' problem is to add an aggregate parameter to the add_field call :
At least it worked for me.
Comment #11
bwynants CreditAttribution: bwynants commentedthis is exactly what I did :-) thanks anyway!
Comment #12
mixman CreditAttribution: mixman commentedOK, this is a bug - the add_groupby() DOESN'T NEED TO ADD ALL THE SELECT FIELDS to the GROUP BY statement.
Take a look at this:
http://www.oreillynet.com/databases/blog/2007/05/debunking_group_by_myth...
Comment #13
lotyrin CreditAttribution: lotyrin commentedThe linked article simply explains how a query like this can make sense in a specific subset of situations. However, I don't see how those situations can't also be handled with aggregation.
This doesn't make sense in most cases, and doesn't work on many SQL servers. (It even will or will not work on MYSQL, depending on the configuration of the server.)
The whole thing reads as a feature request for the ability to make MYSQL-specific views which will have undefined behavior.
Comment #14
Luciuz CreditAttribution: Luciuz commented#10 does not working on D7?
Comment #15
Luciuz CreditAttribution: Luciuz commentedsup
Comment #16
dasjonot sure if its directly related, but for d7, i had success using the following code.
Comment #17
acontia CreditAttribution: acontia commentedI had the same problem and finally solved it implementing
hook_query_alter
(note that is different thanhook_views_query_alter
).source: http://lechronicles.blogspot.co.uk/2011/11/how-to-use-hookqueryalter-to-...
Comment #18
HansKuiters CreditAttribution: HansKuiters commentedSame problem here. I guess my view doesn't have a tag, so I couldn't use hook_query_alter. Combining all sorts of help on the web, I came to add $query->add_having() to the function:
Comment #19
dahousecat CreditAttribution: dahousecat commented#17 works perfectly - it's just impossible to add a group by in hook_views_query_alter but easy in hook_query_alter.
Shame that took so long to work out!
Comment #20
-enzo- CreditAttribution: -enzo- commented#17 works perfectly
Comment #21
guardiola86 CreditAttribution: guardiola86 commented#17 works perfect, even in D7. Thanks @acontia !
Comment #22
Richard15 CreditAttribution: Richard15 commented#17 works perfect D7. I love you @acontia!
Comment #23
Chris Matthews CreditAttribution: Chris Matthews as a volunteer commentedThe Drupal 6 branch is no longer supported, please check with the D6LTS project if you need further support. For more information as to why this issue was closed, please see issue #3030347: Plan to clean process issue queue