skip to Main Content

In a ColdFusion application I have a query with several joins and I need one MAX number from one of the tables. I am getting all tangled up in ‘only_full_group_by’. I cannot disable it in etc or in the Administrator, because I am on a shared system and have no access to those. I’d like to disable it at run time. Here’s a simplified version of the query:

<cfquery name = 'test' datasource = 'whatever'> 
select  PersActExt,PersActPaid,PersActMdate,PersActbl,
   PersTrId,PersTrMas,PersTrSub,PersTrCode,
   MAX(PersTrPaid),  MAX(PersTrDate)
  from PersActiv          
      left join PersTrans on
           PersTrId = PersActId and
           PersTrMas = PersActMas and
           PersTrSub = PersActSub and
           PersTrCode = PersActCode
  where PersActMas = 'bill'
  group by PersTrId,PersTrMas,PersTrSub,PersTrCode
  </cfquery>

The fact is the condition PersActMas = ‘bill’ reduces this to one record per Person in the PersAct table. I suppose I could put a MAX on all those Persact type fields, but there are other tables that also have to be joined in; and frankly I don’t want a MAX on everything just to pick up one Max number in PersTr.

Is there a way to disable the ONLY_FULL_GROUP_BY mode within the query? Or better yet within the application? Or can anyone suggest another way around this problem. I know I can split this up into several queries and then stitch everything back together in ColdFusion, but that’s a lot of code to get around this bad MySql mode.

2

Answers


  1. Thinking laterally about the problem, could you restructure your query to join the PersAct table to a pre-aggregated subquery? This removes the need to perform a grouping on the outer query and running up against the limitation of your setup.

    SELECT
      PersActExt,
      PersActPaid,
      PersActMdate,
      PersActbl,
      PersTrId,
      PersTrMas,
      PersTrSub,
      PersTrCode,
      MaxPersTrPaid,
      MaxPersTrDate
    FROM
      PersActiv          
    LEFT JOIN
      (
        SELECT
          PersTrId,
          PersTrMas,
          PersTrSub,
          PersTrCode,
          MAX(PersTrPaid) AS MaxPersTrPaid,
          MAX(PersTrDate) AS MaxPersTrDate
        FROM
          PersTrans
        GROUP BY
          PersTrId,
          PersTrMas,
          PersTrSub,
          PersTrCode
      ) AS PersTr ON
        PersTrId = PersActId and
        PersTrMas = PersActMas and
        PersTrSub = PersActSub and
        PersTrCode = PersActCode
    WHERE PersActMas = 'bill'
    

    Edit:
    For those wanting to know how to remove the ‘ONLY_FULL_GROUP_BY’ on an ad hoc basis, you need to add the below snippet to your query.

    SET SESSION sql_mode = sys.list_drop(@@session.sql_mode, ‘ONLY_FULL_GROUP_BY’);

    This will only work if the DB user used in ColdFusion has the correct permissions and the option to allow multiple queries in a cfquery call is set.

    Login or Signup to reply.
  2. Tested the query on MySQL 5.7 here: https://dbfiddle.uk/E1em1_k2

    I tested with your query, with the last line as you wrote it:

    ...
    group by PersTrId,PersTrMas,PersTrSub,PersTrCode;
    

    This resulted in the error due to the ONLY_FULL_GROUP_BY:

    Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘test.PersActiv.PersActExt’ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

    Then I tested with an alternative last line:

    ...
    group by PersActId,PersActMas,PersActSub,PersActCode;
    

    This works with the ONLY_FULL_GROUP_BY at the default value. See the demo for proof. Note that I had to do my alternative query first, because the dbfiddle aborts when it gets an error.

    The reason is that MySQL 5.7 introduced the capability to infer functional dependencies when using GROUP BY on the primary key.

    https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

    SQL:1999 and later permits such nonaggregates per optional feature T301 if they are functionally dependent on GROUP BY columns: If such a relationship exists between name and custid, the query is legal. This would be the case, for example, were custid a primary key of customers.

    MySQL 5.7.5 and later implements detection of functional dependence.

    Since the four columns in the GROUP BY are in fact the primary key of the PersActive table, MySQL can tell that other columns of that table are guaranteed to have one value per grouping.

    There are multiple rows in PersTrans, but the functional dependencies are still analyzed, so the columns that are known to be equal to primary key columns of PersActiv are still okay to use in the query without aggregation. The other columns of PersTrans that are not functionally dependent are already in MAX() expressions in your query, which also satisfies the requirement.

    I conclude that functional dependency analysis works in MySQL 5.7, and this would be an effective workaround in the case you show.

    You wrote:

    I’m used to a system where if there is an aggregated field and some fields are not aggregated, the first or last value of that the non-aggregated field is automatically used.

    In my experience, this includes only MySQL prior to 5.7, or SQLite. It’s illegitimate in the ANSI/ISO SQL standard, and other SQL implementations conform to the standard.

    MySQL 5.7 does not have a "bad sql mode" — it has been corrected to enforce the rule per the SQL standard, like most other brands of SQL also do.

    Back to your original question:

    Is there a way to disable the ONLY_FULL_GROUP_BY mode within the query? Or better yet within the application?

    You can change the sql mode globally, either in the my.cnf options file, or using SET GLOBAL sql_mode='...';. I would do both, because if you change options dynamically with SET GLOBAL, they are reset to whatever is in the options file the next time you restart the MySQL Server.

    You can also change the sql mode in a session with SET SESSION sql_mode='...'; and it takes effect for the remainder of the current session. But SET SESSION is a separate statement that you would have to execute in the session prior to your query. It can’t be combined with a query.

    There is an option in MySQL 8.0 to use the "hint" syntax to set certain options for the scope of a single SQL query. But in your case, you’re using MySQL 5.7, so you’d have to upgrade to get this feature.

    A similar feature exists in Percona Server 5.6 and later to set a variable for one statement. This does not apply if you use stock MySQL. You’d have to use the Percona branch of MySQL.

    Regardless, I recommend you do not change the sql mode. It’s proper that it behaves the way it does, because it prevents queries that may inadvertently produce wrong results.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search