How to find rows in SQL that end with the same string? - mysql

I have a question similar to the one found here: How to find rows in SQL that start with the same string (similar rows)?, and this solution works in MySQL 5.6 but not 5.7.
I have a database (t) with multiple columns, the important ones being id and filepath, and what I am trying to accomplish is retrieving all the file paths which have the same last 5 characters. The following works in MySQL5.6, and the second SELECT works fine in 5.7:
SELECT id, filepath FROM t
WHERE SUBSTRING(filepath, -5) IN
(
SELECT SUBSTRING(filepath, -5)
FROM t
GROUP BY SUBSTRING(filepath, -5)
HAVING COUNT(*) > 1
)
But when I try to run it on 5.7 I get the error
Expression #1 of HAVING clause is not in GROUP BY clause and contains
nonaggregated column 't.filepath' which is not functionally dependent on
columns in GROUP BY clause; this is incompatible with
sql_mode=only_full_group_by
Sample data:
id filepath
1 /Desktop/file1.txt
2 /Desktop/file2.txt
3 /Desktop/file1.txt
and I would want to return the rows with id 1 and 3. How can I fix this for MySQL5.7?
EDIT: Also can anybody point me in the right direction for the SQL to remove the duplicates? So I would want to remove the entry for id 3 but keep the entry for id 1 and 2.

Please read the mysql documentation on the subject GROUP BY and sql_mode only_full_group_by (like your error message says):
https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
I think changing the inner query to this might fix the problem:
SELECT SUBSTRING(filepath, -5) AS fpath
FROM t
GROUP BY fpath
HAVING COUNT(fpath) > 1
Edit:
As to your question of why adding the "AS fpath" works:
Adding the alias "fpath" is just a clean way to do this. The point of ONLY_FULL_GROUP_BY is that each field you use in the SELECT, HAVING, or ORDER BY must also be in the GROUP BY.
So I added the fpath-alias for multiple reasons:
For performance: The query you wrote had SUBSTRING(filepath, -5) twice, which
is bad for performance. Mysql has to execute that SUBSTRING call twice,
while in my case it has to do it only once (per row).
To fix the group-by issue: You had COUNT() in the having, but "" was not in your GROUP BY statement (I'm not even sure whether that would be possible). You had to count "something", so since "fpath" was in your SELECT and in your GROUP BY, using that as your COUNT() would fix the problem.

I prefer not to put subqueries in an IN() predicate because MySQL tends to run the subquery many times.
You can write the query differently to put the subquery in the FROM clause as a derived table. That will make MySQL run the subquery just once.
SELECT id, filepath
FROM (
SELECT SUBSTRING(filepath, -5) AS suffix, COUNT(*) AS count
FROM t
GROUP BY suffix
HAVING count > 1
) AS t1
JOIN t AS t2 ON SUBSTRING(t2.filepath, -5) = t1.suffix
This is bound to do a table-scan though, so it's going to be a costly query. It can't use an index when doing a substring comparison like that.
To optimize this, you might create a virtual column with an index.
ALTER TABLE t
ADD COLUMN filepath_last VARCHAR(10) AS (SUBSTRING_INDEX(filepath, '/', -1)),
ADD KEY (filepath_last);
Then you can query it like this, and at least the subquery uses an index:
SELECT id, filepath
FROM (
SELECT filepath_last, COUNT(*) AS count
FROM t
GROUP BY filepath_last
HAVING count > 1
) AS t1
STRAIGHT_JOIN t AS t2 ON t2.filepath_last = t1.filepath_last

The solution that ended up working for me was found here: Disable ONLY_FULL_GROUP_BY
I ran SELECT ##sql_mode then SET ##sql_mode = followed by a string containing all the values returned by the first query except for only_full_group_by, but I'm still interested in how this is to be accomplished without changing the SQL settings.

Related

SELECT SUBSTRING(column) ... HAVING SUBSTRING(column) cause unknown column error in mysql

SELECT SUBSTRING(column, ..)
FROM someTable
GROUP BY SUBSTSRING(column, ..)
HAVING SUBSTRING(column, ..) IS NULL;
The query written above resulted in an error. But,
SELECT SUBSTRING(column, ..) AS otherName
FROM someTable
GROUP BY SUBSTSRING(column, ..)
HAVING otherName IS NULL;
The query written above is OK. What causes this result?
Read mysql doc here
from doc: The SQL standard requires that HAVING must reference only columns in the GROUP BY clause or columns used in aggregate functions. However, MySQL supports an extension to this behavior, and permits HAVING to refer to columns in the SELECT list and columns in outer subqueries as well.

MySQL 5.7 - Error sql_mode=only_full_group_by

After Updating to MySQL 5.7.11 we are getting a number of errors related to the sql_mode including only_full_group_by.
Research shows we can turn this off but it would probably be better to know how to achieve the intended goal with properly structured SQL statements.
Error:
#1140 - In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'tblslideshow.slideImage'; this is incompatible with sql_mode=only_full_group_by
Query:
SELECT
tblslideshow.slideImage,
COUNT(tblslideshow.slideID) AS countVal
FROM
tblslideshow
WHERE
tblslideshow.parentID = 3424
The goal is to return a list of values but also a Count for all returned records.
What's the best way to achieve this goal without having to change the MySQL my.ini ? And why is it a Group By error is we're not even grouping? Or is Grouping part of the proper solution?
Expected Result should be:
slideImage | countVal
Image1.jpg | 3
Image2.jpg | 3
Image3.jpg | 3
Edit: Since we are using PHP and MySQLi it may be wiser to exclude the Count() and just use "mysqli_num_rows"
When you use Aggregate function, you need to add non-aggregate columns on Group by clause.
SELECT
tblslideshow.slideImage,
COUNT(tblslideshow.slideID) AS countVal
FROM
tblslideshow
WHERE
tblslideshow.parentID = 3424
Group by tblslideshow.slideImage
EDIT
You can try to use subquery on select
SELECT t.slideImage,
(
SELECT
COUNT(tblslideshow.slideID) AS countVal
FROM
tblslideshow
WHERE
tblslideshow.parentID = 3424
) countVal
FROM tblslideshow t
WHERE t.parentID = 3424
What you are looking for is COUNT OVER, available as of MySQL 8.0:
SELECT
tblslideshow.slideImage,
COUNT(*) OVER () AS countVal
FROM
tblslideshow
WHERE
tblslideshow.parentID = 3424;
This keeps the single rows and adds the aggregate value to them.

Mysql: Is it possible to use a subquery inside a from clause in order to pick the table name from another table

I was wondering if there's any way to add a subquery with a switch case to the form clause of my select query in order to select a table based on a condition.
For example:
select a.*
from (select (case when (table2.column = 'something')
then (table2.tablename1)
else (table2.tablename2)) as tablename
from table2
where table2.column2 = 'blabla'
limit 1
) a
I tried to write that in many variation & so far non of them worked.
On the most successful tryouts (when I got no mysql errors) it returned the name of the table as the result itself (for example: the value that's in table2.tablename2). I understand why it did that (because I selected everything from a select results...) but how can I use the tablename from the results in order to set the table on the main query?
Hope that make sense...
Any idea?

Why does DISTINCT has to go first in MySQL?

I have a query that works when I do
SELECT DISTINCT(table.field.id), 1 FROM ...
but fails when I do
SELECT 1, DISTINCT(table.field.id) FROM ...
Is this a known behavior?
Why does the first one work while the second doesn't?
Unfortunately I'm not able to add a comment yet.
What #Gordon Linoff has written is exactly right.
You are getting error as DISTINCT in general works as part of SELECT clause or AGGREGATE function. It is used to return unique rows from a result set and it can be used to force unique column values within an aggregate function.
Examples: SELECT DISTINCT * ... COUNT(DISTINCT COLUMN) or SUM(DISTINCT COLUMN).
More information's about DISTINCT in popular DB engines:
PostgreSQL:https://www.postgresql.org/docs/9.0/static/sql-select.html#SQL-DISTINCT
SQL Server: https://www.techonthenet.com/sql_server/distinct.php
Oracle: https://www.techonthenet.com/oracle/distinct.php
MySQL:
https://dev.mysql.com/doc/refman/5.7/en/distinct-optimization.html
https://dev.mysql.com/doc/refman/5.7/en/select.html

Can not Access Aliased Columns in Select Statement (MySQL)

I have a problem with Aliased Columns in MySQL!
My Query:
SELECT Price AS Pr, (Pr*10/100) FROM MyTable;
MySQL WorkBench Error: UnKnown Column 'Pr' in Field List !!!
I tested my query in W3Schools with no error !
I tested my query in W3Schools with no error!
This doesn't prove that your query is valid.
You can only use aliases in GROUP BY, ORDER BY or HAVING clauses. Your usage variant is not allowed, because the value of alias is not known when MySQL is selecting the 2-nd column.
I've got a suspicion that W3Schools uses MS Access to run user queries, and MS Access does allow such atrocity as referencing column aliases in a SELECT clause that are defined in the same SELECT clause.
The standard doesn't allow this and MySQL does follow standard in this particular case.
As for solution to your problem, I can see two options.
The more generic solution, which would run in probably any SQL product, would be to use a derived table:
SELECT
Pr,
(Pr * 10 / 100) AS SomethingElse
FROM
(
SELECT
SomeComplexExpression AS Pr
FROM MyTable
) AS sub
;
The other option would be to use a variable, which is MySQL-specific:
SELECT
#Pr := SomeComplexExpression AS Pr,
(#Pr * 10 / 100) AS SomethingElse
FROM MyTable
;
Finally, if you need to test/demonstrate if something can/cannot work in MySQL, I'd recommend using SQL Fiddle.