Using LIMIT within GROUP BY to get N results per dynamic group - php

Using LIMIT within GROUP BY to get N results per dynamic group
Hello everyone, firstly I read about questions like this problem.
But didn't get the solution. All of this SQL's are designed for static columns.
But I have dynamic columns.
Table:
id Name Group Level
2 Jonathan A 5
5 David A 10
6 Alex C 10
7 Kemal A 71
8 John D 21
9 Celin F 100
12 Alexis G 15
13 Noone A 23
I want to get the first 2 highest Level from each group.
But query must be dynamic because there will be more Groups, which is where I am stuck.
Solutions I tried:
Select the top N rows from each group Not giving true result it's broken.
Only work in static columns.

DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,name VARCHAR(12) NOT NULL
,group_name CHAR(1) NOT NULL
,level INT NOT NULL
);
INSERT INTO my_table VALUES
( 2,'Jonathan','A',5),
( 5,'David','A',10),
( 6,'Alex','C',10),
( 7,'Kemal','A',71),
( 8,'John','D',21),
( 9,'Celin','F',100),
(12,'Alexis','G',15),
(13,'Noone','A',23);
SELECT id
, name
, group_name
, level
FROM
( SELECT x.*
, CASE WHEN #prev = group_name THEN #i:=#i+1 ELSE #i:=1 END i
, #prev:=group_name
FROM my_table x -- technically, ordering should really happen here, in a separate subquery
, ( SELECT #prev:=null,#i:=0 ) vars
ORDER
BY group_name
, level DESC
, id
) a
WHERE i <=2;
+----+--------+------------+-------+
| id | name | group_name | level |
+----+--------+------------+-------+
| 7 | Kemal | A | 71 |
| 13 | Noone | A | 23 |
| 6 | Alex | C | 10 |
| 8 | John | D | 21 |
| 9 | Celin | F | 100 |
| 12 | Alexis | G | 15 |
+----+--------+------------+-------+

You can also do workaround.
Select colums upto 2 rows
FROM TABLE ORDER BY DESCENDING GROUP LEVEL
regards,
Umar Abdullah

Related

PHP Total a multidimensional array

I have the following mysql query:
SELECT sub_nine, sum(sub.sub_low_nett) AS points, sub_pid FROM
(SELECT c.cn_nine as sub_nine, min(rh.rh_nett) AS sub_low_nett, p.id AS sub_pid
FROM round r, round_hole rh, player p, course_nine c, course_hole ch
WHERE r.r_id = rh.rh_rid
AND p.id = r.r_pid
AND c.cn_nine = r.r_nine
AND ch.ch_nine = c.cn_nine
AND rh.rh_hid = ch.ch_no
GROUP BY p.id, c.cn_nine, ch.ch_no
) AS sub
GROUP BY sub_pid, sub_nine
ORDER BY sub_pid
Which returns the following result:
|sub_nine|points|sub_pid
| 1 | 2 | 1
| 2 | 17 | 1
| 3 | NULL | 1
| 1 | NULL | 2
| 2 | NULL | 2
| 3 | NULL | 2
| 1 | NULL | 3
| 2 | 26 | 3
| 3 | 4 | 3
| 1 | 26 | 5
| 2 | 36 | 5
The result set has been looped into a multi-dimensional array, using the fetch_assoc() function.
I need to display each of the points totals per player as well as the total of those points (distinctly / uniquely) for each player in a table
I think I need to loop through the initial multi-dimensional array, and probably place into a new array that has been pivoted around the columns rather than rows, but stuck as to how to do this.
The initial query is sorted by pid, so I know that as you loop through the array, the rows can be cumulatively totaled.
I also need the final array to be sorted by the new total value per player as well.
pid | score1 | score2| score3 | total
the data structure is laid out here:
Gold DB ERD Diagram

SQL PHP Display values from database in Groups with two Columns [duplicate]

This question already has an answer here:
How can i list has same id data with while loop in PHP? [closed]
(1 answer)
Closed 8 years ago.
I have table called myTable and it looks like:
restid | menu | prod | price
__________________________________________
10 | m1 | prod1 | 15
10 | m1 | prod2 | 21
10 | m2 | prod3 | 36
10 | m2 | prod4 | 45
10 | m3 | prod5 | 12
10 | m3 | prod6 | 5
and I want to group and display the results as below:
m1
_________________________________________
prod1 | 15
prod2 | 21
_________________________________________
m2
_________________________________________
prod3 | 36
prod4 | 45
_________________________________________
m3
_________________________________________
prod5 | 12
prod6 | 5
I already grouped them using group_concat
SELECT DISTINCT `menu`, GROUP_CONCAT( DISTINCT `prod`
ORDER BY `prod` ) AS prod_list
FROM `myTable` WHERE restid = '10'
GROUP BY `menu`
ORDER BY `menu`
and then used php to display it properly...
but I want to include price in my results. I've been searching for an answer, but any ideas been found.
I'd be great if someone could give me some solution what can be used to achieve that result.
You can just add the price column in selection list. Somthing like this:-
SELECT DISTINCT `menu`, GROUP_CONCAT( DISTINCT `prod`
ORDER BY `prod` ) AS prod_list, `price`
FROM `myTable` WHERE restid = '10'
GROUP BY `menu`,`price`
ORDER BY `menu`;

Select all from table even null and join

I want to SELECT the all rows from meeting table even its null. My table structure:
attandance table:
id | meeting_id | person_id | time
1 | 1 | 12 | 02:02
2 | 1 | 15 | 02:05
3 | 1 | 22 | 02:05
4 | 2 | 1 | 02:20
5 | 2 | 12 | 02:01
6 | 3 | 12 | 02:03
and meeting table:
id | date
1 | 15-12-2014
2 | 17-12-2014
3 | 19-12-2014
4 | 21-12-2014
The output should be:
If I SELECT the person_id 12 then it should return:
date |time
15-12-2014 | 02:02
17-12-2014 | 02:01
19-12-2014 | 02:03
21-12-2014 | NULL
If I SELECT the person_id 1 then it should return:
date |time
15-12-2014 | NULL
17-12-2014 | 02:20
19-12-2014 | NULL
21-12-2014 | NULL
It is a pretty straightforward outer join between the tables:
select
m.id,
m.date
a.time,
c.someColumn
from
meetings m
left outer join attendance a
on m.id=a.meeting_id
and a.person_id=:person
left outer join someTable c
on m.id=c.id
I have written a more detailed answer on these sorts of joins in the question and answer: How can an SQL query return data from multiple tables
Edit: As per the comment by Andrew, the clause for the personID is in the join rather than in a normal where clause because it is an outer join. If the condition was put into the where clause as normal, it would in fact negate the outer join completely.

Skip 0 and NULL in SQL

My previous problem and solution:
Get max and min from fields
This working OK, but i would like skip 0 and NULL in this examples.
For example:
First:
id | title
1 | aaa
2 | bbb
3 | ccc
Second:
id | first_id | one | two | three | four
1 | 1 | 3 | 0 | 4 | 6
2 | 2 | 4 | 4 | 1 | 2
3 | 3 | 1 | NULL | 3 | 4
this should show me:
id | title | min | max
1 | aaa | 3 | 6
2 | bbb | 1 | 4
3 | ccc | 1 | 4
and not:
id | title | min | max
1 | aaa | 0 | 6
2 | bbb | 1 | 4
3 | ccc | 0 | 4
In which example from my previous question is the best way to implement skip 0 and NULL?
Pop these into your clause
SELECT
f.id,
f.title
MIN(LEAST(greatest(coalesce(s.one,0),1), greatest(coalesce(s.two,0),1), greatest(coalesce(s.three,0),1), greatest(coalesce(s.four,0),1))) as min,
MAX(GREATEST(greatest(coalesce(s.one,0),1), greatest(coalesce(s.two,0),1), greatest(coalesce(s.three,0),1), greatest(coalesce(s.four,0),1))) as max
FROM
First f
INNER JOIN Second s
on f.id = s.first_id
GROUP BY
f.id,
f.title
You can use coalesce(fieldName, 1) to turn a null into a 1.
Again, as said in your previous question, this is HORRIBLE use of a query to force an answer. You should be changing the layout of the database.
Edit: I have nutted out the data you want, but before you look at it, be aware that if one of my colleagues wrote a script like this, he would be sacked on the spot. This is HIDEOUS and should NOT BE USED.
select
f.id,
f.title,
(select min(z.myVal) from
(
select
b.id,
b.first_id,
b.one as myVal
from
second b
where
b.one is not null
and b.one > 0
union
select
b.id,
b.first_id,
b.two as myVal
from
second b
where
b.two is not null
and b.two > 0
union
select
b.id,
b.first_id,
b.three as myVal
from
second b
where
b.three is not null
and b.three > 0
union
select
b.id,
b.first_id,
b.four as myVal
from
second b
where
b.four is not null
and b.four > 0
) z
where
f.id=z.first_id) as miniVal,
greatest(
coalesce(s.one,0),
coalesce(s.two,0),
coalesce(s.three,0),
coalesce(s.four,0)
) as maxiVal
from
first f,
second s
where
f.id=s.first_id
output Data
+------+-------+---------+---------+
| id | title | miniVal | maxiVal |
+------+-------+---------+---------+
| 1 | aaaa | 3 | 6 |
| 2 | bbbb | 1 | 4 |
| 3 | cccc | 1 | 4 |
+------+-------+---------+---------+
3 rows in set (0.00 sec)
Running this query made me throw up a little in my mouth. That's how wrong it is to write SQL like this.
While seemingly clunky, this solution should work:
SELECT
a.id, a.title, MIN(b.num) AS min, MAX(b.num) AS max
FROM
first a
LEFT JOIN
(
SELECT first_id, one AS num FROM second UNION ALL
SELECT first_id, two FROM second UNION ALL
SELECT first_id, three FROM second UNION ALL
SELECT first_id, four FROM second
) b ON
a.id = b.first_id AND
b.num IS NOT NULL AND
b.num > 0
GROUP BY
a.id, a.title
What this does is it actually gets each number column into its own row, but only the numbers that are not null and > 0. Before the GROUP BY, the result of the LEFT JOIN would look something like:
id | title | num
---------------------
1 | aaa | 3
1 | aaa | 4
1 | aaa | 6
2 | bbb | 1
2 | bbb | 2
2 | bbb | 4
2 | bbb | 4
3 | ccc | 1
3 | ccc | 3
3 | ccc | 4
Then by the groupings of each first (GROUP BY a.id, a.title), we can use the MIN() and MAX() aggregate functions on the num column to extract minimum and maximum values per first group:
id | title | min | max
----------------------------
1 | aaa | 3 | 6
2 | bbb | 1 | 4
3 | ccc | 1 | 4
In the case that a first_id had all four columns having NULL's or 0's, the min and max values would show up as NULL due to using a LEFT JOIN instead of an INNER JOIN as I believe this is would be a better behavior for your situation:
id | title | min | max
----------------------------
4 | ddd | NULL | NULL
USE:
WHERE COLUMN IS NOT NULL AND COLUMN <> 0;
I think you just need to nest the LEAST expressions:
LEAST(
NULLIF(one,0),
LEAST(
NULLIF(two,0),
LEAST(
NULLIF(three,0),
LEAST(
NULLIF(four,0),
null ))))
Edit I just looked it up. The LEAST function takes multiple arguments:
LEAST( NULLIF(one,0), NULLIF(two,0), NULLIF(three,0), NULLIF(four,0))
Edit 2 I see you want both min and max. Obviously you'd just change LEAST to GREATEST or MIN to MAX as needed.
This may be more straightforward or you may not have a handy least function.
SELECT
f.id, f.title,
(
SELECT MIN(NULLIF(val, 0))
FROM
(
SELECT one AS val UNION ALL
SELECT two UNION ALL
SELECT three UNION ALL
SELECT four
) AS vals
)
) as minval
FROM First f INNER JOIN Second s on f.id = s.first_id
You haven't specified if it's possible for all four columns to be null/0. We may need to tweak for that case.
You can use IFNULL():
WHERE IFNULL(fieldname, 0) != 0

find top 3 values with an inner join

I have a table called users_preferred_zips that looks like this:
username | price | zip | program | active
-----------+---------+---------+-----------+---------
joe | 5 | 92108 | dog | 1
tom | 7 | 92108 | dog | 1
mary | 5 | 92108 | dog | 1
paul | 6 | 92108 | dog | 1
ron | 6 | 92108 | dog | 1
I have another table called users that looks like this
username | balance
-----------+----------
joe | 10
tom | 12
mary | 2
paul | 14
ron | 3
I need a query to pull AND sum the 3 highest values from the users_preferred_zips table where the username from the users table has a balance value greater than or equal to 5. I know i need to do some sort of inner join but my query below is not working. Here is the query i have:
SELECT SUM(price) AS SumOfTopValues
FROM (
SELECT users_preferred_zips . * , users.last_purchase, users.lesson_type, users.pref_acct_balance
INNER JOIN users ON ( users_preferred_zips.username = users.username )
WHERE users_preferred_zips.zip = '92108'
AND users_preferred_zips.program = 'dog'
AND users_preferred_zips.active = 1
AND users.pref_acct_balance >= '5'
ORDER BY price DESC
LIMIT 3
) AS sub
So the correct query would pull the following:
3 highest:
joe | 5
tom | 7
paul | 6
Sum of 3 highest values = 18
I feel like this should be pretty simple but i'm having a tough time! Thanks for your help
You can check this using:
SELECT SUM(price) AS SumOfTopValues
FROM users_preferred_zips
WHERE username IN (
SELECT username
FROM users
WHERE pref_acct_balance >= 5
)

Categories