Limitation of number of sum() in a query in MySQL 8? - mysql

I recently installed MySQL 8 and found out that some queries no longer run. The error is:
SQL Error (1022): Can't write; duplicate key in table 'C:\WINDOWS\SERVIC~1\NETWOR~1\AppData\Local\Temp\#sql2db8_13_26'
There were no issues with MySQL 5.7 or older versions.
All my research in forums showed that this usually happens in case of foreign keys and constraints. I don't have any, just Primary key and 2 indexes.
My query produces 843 columns with KPIs all of which are aggregated with sum(). What made me impression is that if I remove approx 2/3 of the query content the query runs normally without any errors. Also if I remove group by or all of sums there are no issues either.
So I am wondering whether there is any new limatitaion of number of aggregate operators in MySQL 8.0.
A simplified example of my query is below. Table is running MyISAM.
select Time,eNodeB,
round((sum(`L.E-RAB.SuccEst.QCI.1`)/sum(`L.E-RAB.AttEst.QCI.1`))*100,3) as RAB_Est_SR,
sum(`L.E-RAB.AttEst.QCI.1`) as RAB_Est_att,
...
from h_cell
where enodeb='10006' and time>='2018-01-01'
group by time

No! there is no limitation. As your error message said, you have duplicate key for grouping. This means your usage of GROUP BY is not true! You must use every column (Except scalar ones) in front of select in GROUP clause.
So, you must change your query to:
select Time,eNodeB,
round((sum(`L.E-RAB.SuccEst.QCI.1`)/sum(`L.E-RAB.AttEst.QCI.1`))*100,3) as RAB_Est_SR,
sum(`L.E-RAB.AttEst.QCI.1`) as RAB_Est_att,
...
from h_cell
where enodeb='10006' and time>='2018-01-01'
group by time,eNodeB

Related

Coldfusion how to disable sql-mode 'only_full_group_by" in cfquery

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.
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.
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.

mysql triggers using vs select query

I use mysql trigger to update column in one of ,y DB tables called comments_count but I want to know what is best and faster??
Using mysql triggers or select query like this below:
select count(*) from comments where discussion_id=something
different types of overhead:
with the trigger you will have extra time during insert, and may get out of synch over time for some unforseen reason.
with the query, you will always get the right answer but you will need to calculate at runtime. usually, this should be very fast especially with an index on the discussion_id

Where clause using key column still gives an error

I have a table that serves as a foreign key lookup from another table. The table is very simple, containing a ID column with is the primary key and a JSON column. I wish to remove abandoned entries from this table.
I tried running this script:
DELETE
FROM `ate`.`test_configuration`
WHERE `ate`.`test_configuration`.`ID` NOT IN (SELECT DISTINCT `ate`.`index`.`TestID` from `ate`.`index`);
But encountered an error stating my I wasn't using a where clause that uses the key column:
Error Code: 1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column To disable safe mode, toggle the option in Preferences -> SQL Editor and reconnect.
This is confusing as my where clause does use the primary key column. I am aware that I can disable safe mode as part of my script as a workaround, but would still like to understand why I'm getting this error. I'd like to avoid unsafe updates if possible.
I believe Optimizer just unable to use index effectively for such query - so it does full table scan.
How many rows are in the test_configuration and how many of them will be deleted?
(You might try to use index hints to force optimizer to use index for the query, just not sure if they are supported in your version of mysql).

SQL filtered index not working in MySQL Workbench

Hopefully a very quick question: I'm having some trouble getting the following
index creation statement to work. I'm using MySQL Workbench.
CREATE INDEX HIRE_DATE_INDEX
ON Employee (hiredate)
WHERE hiredate > '2001-01-01';
I'm trying to get the following index creation to work, however regardless of what I look up online the following is listed as the proper syntax but for some reason that is unfathomable at this point, the index is specifically saying that the where clause is in the incorrect place.
I'm clearly missing something in the examples.
(Context, just trying to create a filtered index only interested in dates greater then).
It looks like this should be easy as hell, what am I missing here?
You don't need the WHERE clause. But MySQL doesn't support filtered index. (Reference here)
CREATE INDEX HIRE_DATE_INDEX
ON Employee (hiredate);
Also that command doesn't work if you try to create a primary key. If that's the case, you need to use ALTER TABLE command.

MySQL - Automatic ordering by ID not working

I'm using MySQL database to store student projects (every single project has its own unique ID, as the first screenshot shows).
The 'ID' column is set as auto_increment and PRIMARY KEY.
All projects must be ordered (only) by their ID ascending. But every time I insert a new project into my database and set lower value in the 'Year' field (lower value than I entered last time, when I was inserting my previous project), my projects become automatically ordered by this 'Year' field (as shown on the second screenshot).
Is there any way, how to set my table for automatic ordering all newly added projects only by the 'ID' column? Yeah, I know that I can change the ordering with ALTER TABLE tablename ORDER BY columnname ASC;after I place every new record, but can it be done automatically?
Thx to everyone who helps.
Q: Is there any way, how to set my table for automatic ordering all newly added projects only by the 'ID' column?
A: There is no "automatic ordering" in a MySQL table. (Some storage engines, such as InnoDB, are "index organized" structures, and do store rows in order by the cluster key.) But this organization does not define or specify the order of rows returned by a SELECT statement.
Without an ORDER BY clause on a SELECT statement, then the MySQL server can return rows in any order it chooses to. There is no guarantee of any "automatic" or "default" ordering.
When we run a SQL SELECT statement without an ORDER BY clause, we do observe that rows tend to be returned in a consistent order. This behavior isn't guaranteed, and it isn't "automatic".
This consistent "ordering" behavior we observe is due to the MySQL server performing a consistent set of operations, on a consistent set of data.
Performing an ALTER TABLE statement to rebuild the entire table is not the solution to the "automatic ordering" issue.
If you want the MySQL server to return rows in a specific order, then the solution is to add an ORDER BY clause to the SELECT statement.
The client that executes the SELECT statement is free to do whatever it wants with the rows it retrieves. The client can perform operations such as filtering, ordering, or whatever, to come up with what gets returned in the user interface.
Some clients (like the mysql command line) don't implement any functions for "filtering" or "ordering" rows, so the only way for the client to specify an order that rows should be returned in is the ORDER BY clause on statement itself. (The MySQL command line client returns rows in the user interface in the same order that they are retrieved.)
I expect that phpMyAdmin does the same thing, it displays the rows in the same order that they are returned from the MySQL server.
Order in query results should be determined by the ORDER clause. Don't rely on default order applied by phpmyadmin (or some other tool).
You have to distinct retrieving from inserting/updating.
About automatic order INTO phpmyadmin, maybe using bookmarks on queries : How can I set a default sort for tables in PHPMyAdmin (i.e. always "Primary key - Descending")