Filtering duplicate rows based on multiple columns (QueryBuilder 4.2) - mysql

I've ran into a little difficulty when trying to filter top N results for a table.
Assume the following table:
ID, X, Y, Result0, Result1
-------------------------------
0 0 0 1 4
1 0 1 2 5
2 0 1 1 4
3 0 2 2 5
4 0 3 0 1
5 1 3 3 4
6 1 3 2 5
7 1 3 4 6
So, let's say I want to get the top 2 results for the highest Result0 value, using Result1 as a tie breaker if the Result0 values are equal, and having only distinct values for (X,Y),
if I'll run the following query:
$result = DB::table('table')
->orderBy('Result1', 'DSC')
->orderBy('Result0', 'DSC')
->take(300)
->get();
This code will return IDs 5,7, because they have the highest Result0 values, but the X,Y for these fields are identical, and I'd like to get only top result for distinct X,Y values.
I tried adding a
->groupBy('X','Y')
But it grouped the entries based on the database order of the entries (i.e the ID) rather than my sorting of that table.
Anyone has any idea how can I achieve my goal?

Related

How can I replace nested for statement with mysql query?

Environment :
MySQL 5.7.x
Spring MVC
Table Data (name: TableA)
seq
level
name
order
parent_seq
1
1
name1
1
0
2
1
name2
2
0
3
2
sub1-1
1
1
4
2
sub1-2
2
1
5
2
sub2-1
1
2
6
3
third-2-1
1
5
7
3
third-1-1
1
3
Expected Result
seq
level
name
order
parent_seq
next_level
1
1
name1
1
0
2
3
2
sub1-1
1
1
3
7
3
third-1-1
1
3
2
4
2
sub1-2
2
1
1
2
1
name2
2
0
2
5
2
sub2-1
1
2
3
6
3
third-2-1
1
5
1 (last default value: 1)
Now I'm genenrating expected result with nested for statement(JAVA).
Is there any way to generate expected result only with MySQL Query?
The data stacked in random order in the table is sorted by ASC based on the level column, but check the parent_seq column so that it is sorted under the parent data. And if there are multiple data of the same level, sort by ASC based on the sort column value.
Thanks in advance!
++
EmbraceNothingButFuture's answer was great, but the query seems to work on MySQL 8. I'm using MySQL 5.7. Is there any way to use the query on MySQL 5.7?
Summary:
Use REGEXP_SUBSTR(name,"[0-9]+\-?[0-9]*") to extract the numbers and sort the datas using the numbers.
For MySQL v8 above, you can use LEAD() to generate the "next_level" column based on the "level" column
COALESCE() function for the last default value = 1
SELECT
t1.*,
COALESCE(LEAD(t1.level, 1) OVER(ORDER BY REGEXP_SUBSTR(name,"[0-9]+\-?[0-9]*")), 1) AS next_level
FROM TableA t1
ORDER BY REGEXP_SUBSTR(name,"[0-9]+\-?[0-9]*"), t1.level
See db<>fiddle

Сoncatenation of rows into one table

I have a table with 2 columns, one of them is categories:
Category
AvgSum
1
10
2
100
3
1000
4
10000
5
100000
And I need to unite the second and the third row, also the fourth and fifth, and in the received lines perform the operation AVG
So the table has to be like this:
Category
AvgSum
1
10
2
550
3
60000
Any thoughts?
SQL tables represent unordered sets. You can use a case expression to define the groups:
select (case when category in (2, 3) then 2
when category in (4, 5) then 3
else category
end) as new_category,
avg(avgsum) as avgsum
from t
group by new_category;

Average of rows Mysql ignore zero values

Hello I have a problem with the function avg. I have a table like this and I would like to take the average of each row. I also have the zero in some cells and would like to avoid count them.
data rep val1 val2 val3
1 a 0 3 3
2 a 1 4 0
3 a 1 1 1
4 a 1 3 0
And I would like this result
data AVG
1 3
2 2.5
3 1
4 2
thank you
Assuming you have at least one non-zero value:
SELECT data, (val1+val2+val3)/((val1!=0) + (val2!=0) + (val3!=0)) avg
FROM **table_name**
I think divide by zero returns null see manual, depending on your db settings, so you could do:
SELECT data, COALESCE((val1+val2+val3)/((val1!=0) + (val2!=0) + (val3!=0)),0) avg
FROM **table_name**
Any null values in a row will cause each query to always return null and 0 for the row respectively.

Query that returns quantity of repeated values

In mysql, I need a query that returns the quantity of repeated values in the field "Info" of my table "Log".
Table Log:
ID_Log User Info
1 1 3
2 1 3
3 1 3
4 1 5
5 1 6
6 1 6
7 1 7
8 1 8
9 1 8
The query should return "4" (Info 3 appears three times, Info 6 appears two times, Info 8 appears two times).
Any suggestions?
You can get the number of values that have already appeared by using a simple subtraction. Subtract the number of distinct values from the total number of rows:
select count(*) - count(distinct info)
from log;
The difference is the number that "repeat".
This should work. Group the values of info together and only keep the results where the number of occurrences minus 1 is greater than 0. Then sum the numbers of occurrences.
select sum(repeats)
from (SELECT Info, count(*) - 1 AS repeats
FROM Log
GROUP BY Info
HAVING repeats > 0)

SQL - counting rows with specific value

I have a table that looks somewhat like this:
id value
1 0
1 1
1 2
1 0
1 1
2 2
2 1
2 1
2 0
3 0
3 2
3 0
Now for each id, I want to count the number of occurences of 0 and 1 and the number of occurences for that ID (the value can be any integer), so the end result should look something like this:
id n0 n1 total
1 2 2 5
2 1 2 4
3 2 0 3
I managed to get the first and last row with this statement:
SELECT id, COUNT(*) FROM mytable GROUP BY id;
But I'm sort of lost from here. Any pointers on how to achieve this without a huge statement?
With MySQL, you can use SUM(condition):
SELECT id, SUM(value=0) AS n0, SUM(value=1) AS n1, COUNT(*) AS total
FROM mytable
GROUP BY id
See it on sqlfiddle.
As #Zane commented above, the typical method is to use CASE expressions to perform the pivot.
SQL Server now has a PIVOT operator that you might see. DECODE() and IIF() were older approaches on Oracle and Access that you might still find lying around.