0

I want to perform a query like to get the last record of any type in DB, at my localhost, I use Maria Db and the query is as follow:

SELECT * 
  FROM table_a
 WHERE column_a=999
    OR column_b=999 
 GROUP 
    BY `group`;

group is a column which I save type in it, for instance: marketing, blog, order, etc

This query works fine on local, but on the server I get the following error:

SQLSTATE[42000]: 
Syntax error or access violation: 
1055 Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column
'db_name.table_a.id' which is not functionally dependent on columns in GROUP BY clause;
this is incompatible with sql_mode=only_full_group_by\n
The SQL being executed was: 
SELECT * FROM `table_a` WHERE (`column_a`=999) OR (`column_b`=999) GROUP BY `group`"

According to MySQL document I can use the following command to make this possible:

SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));

But I don't have the sufficient privilege on Db and get the following error:

#1227 - Access denied; you need (at least one of) the SUPER privilege(s) for this operation

I asked the hosting to do this for me, they replied that they don't want to do this action

I use the YII2 framework, and now I want a way to add this on the option of database_config.php of the framework or change the query to something else with the same result, and not losing performance

GMB
  • 216,147
  • 25
  • 84
  • 135
Arash Rabiee
  • 1,019
  • 2
  • 16
  • 31
  • 1
    `SELECT *` makes no sense. When you do `GROUP BY group` then all rows with the same `group` value are treated as a group. For this rows group only one value for each column must be returned, but a lot of values exists. How to determine what value do you need? First (in what order?)? maximal? random? something else? – Akina Dec 10 '20 at 06:06
  • The hosting company has configured the correct value for `sql_mode`. You should not try to change the `sql_mode` to allow invalid queries. You should fix your query instead. I wrote an answer to explain why this type of query is invalid here: https://stackoverflow.com/a/13999903/20860 – Bill Karwin Dec 10 '20 at 06:13
  • *But I don't have the sufficient privilege on Db* Of course. But if you really want to alter SQL mode and execute your illogical query then you must alter SESSION, not GLOBAL, setting. – Akina Dec 10 '20 at 06:36
  • The simplest option is to reject the premise of the question in its entirety. It's simply nonsensical. – Strawberry Dec 10 '20 at 08:35
  • @Akina this is exactly what I want, I want the last record of each type, maybe 8 types so 8 records – Arash Rabiee Dec 10 '20 at 10:10
  • *I want the last record of each type* The table is a heap, first-next-last terms not exists until you specify some definite ordering. Provide complete CREATE TABLE for your table and ordering expression/criteria which allows to define what row is "last". Also specify precise MySQL version. – Akina Dec 10 '20 at 10:17

2 Answers2

2

ONLY_FULL_GROUP_BY is a good thing, which enforces basic ANSI SQL rules. Don't change it, fix your code instead.

From there one: you want entire records, so you should not think aggregation, but filtering.

Then: in a database table, records are unordered; for your question to just make sense, you need a column that defines the ordering of rows in each group, so it unambiguous what "last" record mean. Let me assume that you have such column, called id.

Here is a typical approach at this top-1-per-group problem, using a correlated subquery for filtering:

SELECT * 
FROM table_a a
WHERE 
    999 IN (column_a, column_b)
    AND id = (
        SELECT MAX(a1.id) 
        FROM table_a a1 
        WHERE 999 IN (a1.column_a, a1.column_b) AND a1.grp = a.grp
    )

Alternatively, if you are running MySQL 8.0, you can use window functions:

SELECT *
FROM (
    SELECT a.*,
        ROW_NUMBER() OVER(PARTITION BY grp ORDER BY id DESC) rn
    FROM table_a a
    WHERE 999 IN (column_a, column_b)
) a
WHERE rn = 1

Side note: group is a language keyword, hence a poor choice for a column name. I renamed it to grp in the queries.

GMB
  • 216,147
  • 25
  • 84
  • 135
  • That is a good approach; however, I rather using Active Record according to https://www.yiiframework.com/doc/guide/2.0/en/db-active-record, rather than writing query for all parts of my code (maybe more than 100 queries need refactoring). And I didn't have this problem with my code using other hosting services, but my current client insist on using this hosting, so if I don't find any other solution I give a try this method. – Arash Rabiee Dec 10 '20 at 12:16
1

There are a few ways to "bypass" the sql_mode but be aware that the result you get might not be correct.

First you can use ANY_VALUE(). Example like this:

SELECT any_value(column_a), any_value(column_b), `group` FROM table_a 
WHERE (column_a=999) OR (column_b=999) GROUP BY `group`;

When using ANY_VALUE() function you have to write all the columns in SELECT from the table and append with ANY_VALUE() except for the column that you use in the GROUP BY.

Using MAX() or MIN() can return result but still it might not be the correct result, especially for any row(s) that have more than 1 count:

SELECT MAX(column_a), MAX(column_b), `group`
FROM table_a 
WHERE (column_a=999) OR (column_b=999) GROUP BY `group`;

Using GROUP_CONCAT will give you a view at what are the values in non-grouped columns. Compare the results with the other queries above and you can see on row(s) that returns more than one count, does the other queries returning according to what you want?

SELECT group_concat(column_a), group_concat(column_b), group_concat(`group`)
FROM table_a 
WHERE (column_a=999) OR (column_b=999) GROUP BY `group`;

I'm not sure if you can do this but you can set the sql_mode off temporarily then you should be able to run your query:

SET sql_mode=""; -- you don't need to set global privilege.
SELECT * FROM table_a 
WHERE (column_a=999) OR (column_b=999) GROUP BY `group`;

Demo here.

Still, the best option is to retain the sql_mode as it is and construct the query according to the requirement.

P/S: GROUP is a reserved word in both MySQL & MariaDB. You can use it as column name but you have to always add back-ticks to define the column or else, running the query will return you an error like

Query: select * from table_a group by group

Error Code: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'group' at line 1

FanoFN
  • 6,815
  • 2
  • 13
  • 33
  • Hi, I did use any_value and add all the column in select, 4 of my columns has a relationship, and I still get the same error. Also did GROUP BY `group` still get the same error. And for your third option, I'm looking for it, I can do it query by query but maybe more than 100 query used group by and I'm looking for a way to add this with setup of Yii2 framework, so far, no solution found. – Arash Rabiee Dec 10 '20 at 10:08