select max group by without id for join - sql

I have table like this :
id name value
1 roger 43
2 phil 12
3 zac 14
4 phil 42
5 maurice 450
...
and i'm trying to retrieve the max value for each name in order to do a join later.
I'm expecting the intermediate result to be something like this :
name value
roger 43
zac 14
phil 42
maurice 450
And this is easily achieved using and select name,max(value) from table group by name
My issue is that i NEED the id in order to later be able to do my join. but if i add my id to the aggregate/ group by it will mess up the result and will show all values since the id will be different.
So the true expect result is more like this :
id name value
1 roger 43
3 zac 14
4 phil 42
5 maurice 450
I have seen many question regarding similar issues but none where the id need to be retrieved but not included in the group by since i want uniqness for the name and only need the id for my join.

From what you describe, you just want the max of id:
select max(id) as id, name, max(value)
from table
group by name;
Or, what I think you want is the row with the max value:
select distinct on (name) t.*
from t
order by name, value desc;

With not exists:
select min(t.id) id, t.name, t.value
from tablename t
where not exists (
select 1 from tablename
where name = t.name and value > t.value
)
group by t.name, t.value
order by id
See the demo.
Results:
| id | name | value |
| --- | ------- | ----- |
| 1 | roger | 43 |
| 3 | zac | 14 |
| 4 | phil | 42 |
| 5 | maurice | 450 |

Try this:
WITH X (n, v) AS (
SELECT name, MAX(value) FROM tbl GROUP BY name
)
SELECT T.*
FROM tbl AS T INNER JOIN X ON T.name = X.n AND T.value = X.v

Related

How to make sure the sql result is continued range?

I have table like:
id | low_number | high_number
-------------------------------
1 | 12 | 32
-------------------------------
2 | 13 | 33
-------------------------------
3 | 15 | 36
-------------------------------
4 | 33 | 50
-------------------------------
5 | 35 | 52
...
-------------------------------
17 | 52 | 80
I want to get result like:
id | low_number | high_number
-------------------------------
1 | 12 | 32
-------------------------------
4 | 33 | 50
-------------------------------
17 | 52 | 80
that is because the low_number bigger than the pervious row high_number.
How to write sql to get these result? I use postgresql
This seems like a recursive CTE problem. You want to choose the first row (by id) and then choose the next row based on that.
The idea is to cycle through the rows, one at a time. Then when the condition is met, transition to that row. And so on.
As a query, this looks like:
with recursive tt as (
select id, low_number, high_number, row_number() over (order by id) as seqnum
from t
),
cte as (
select id, low_number, high_number, seqnum, true as is_change, id as grouping_id
from tt
where seqnum = 1
union all
select tt.id, tt.low_number, tt.high_number, tt.seqnum, tt.low_number > t.high_number,
(case when tt.low_number > t.high_number then tt.id else cte.grouping_id end)
from cte join
t
on cte.grouping_id = t.id join
tt
on tt.seqnum = cte.seqnum + 1
)
select *
from cte
where is_change;
Here is a db<>fiddle.
Use the window function LAG() to get a value of a previous row, e.g.
WITH j AS (
SELECT
id,low_number,high_number,
LAG(high_number) OVER (ORDER BY id) AS prev_high_number
FROM t)
SELECT id,low_number,high_number FROM j
WHERE low_number > prev_high_number OR prev_high_number IS NULL;
Demo: db<>fiddle

Order By Id and Limit Offset By Id from a table

I have an issue similar to the following query:
select name, number, id
from tableName
order by id
limit 10 offset 5
But in this case I only take the 10 elements from the group with offset 5
Is there a way to set limit and offset by id?
For example if I have a set:
|------------------------------------|---|---------------------------------------|
| Ana | 1 | 589d0011-ef54-4708-a64a-f85228149651 |
| Jana | 2 | 589d0011-ef54-4708-a64a-f85228149651 |
| Jan | 3 | 589d0011-ef54-4708-a64a-f85228149651 |
| Joe | 2 | 64ed0011-ef54-4708-a64a-f85228149651 |
and if I have skip 1 I should get
|------------------------------------|---|---------------------------------------|
| Jana | 2 | 589d0011-ef54-4708-a64a-f85228149651 |
| Jan | 3 | 589d0011-ef54-4708-a64a-f85228149651 |
I think that you want to filter by row_number():
select name, number, id
from (
select t.*, row_number() over(partition by name order by id) rn
from mytable t
) t
where
rn >= :number_of_records_per_group_to_skip
and rn < :number_of_records_per_group_to_skip + :number_of_records_per_group_to_keep
The query ranks records by id withing groups of records having the same name, and then filters using two parameters:
:number_of_records_per_group_to_skip: how many records per group should be skipped
:number_of_records_per_group_to_skip: how many records per group should be kept (after skipping :number_of_records_per_group_to_skip records)
This might not be the answer you are looking for but it gives you the results your example shows:
select name, number, id
from (
select * from tableName
order by id
limit 3 offset 0
) d
where id > 1;
Best regards,
Bjarni

Find first record before or after missing record in one table

I have only one big table Samples with columns Id and Values.
I need get only one record before missing record from table for each gap.
I need help to build query.
Table:
Id | Values
---------
1 | 45
2 | 45
3 | 44
5 | 89
6 | 21
7 | 59
9 | 23
10 | 78
11 | 12
12 | 16
15 | 19
Result of query:
Id | Values
---------
3 | 44
7 | 59
12 | 16
As you do not tagged the database name, you can consider this following logic as generic for any database-
SELECT * FROM your_table A
LEFT JOIN your_table B ON A.Id = B.Id - 1
WHERE B.Id IS NULL
AND A.Id < (SELECT MAX(ID) FROM your_table)
ORDER BY A.Id
If you are using MSSQL 2012 or Newer version, you can use LEAD to achieve your desired output with below script-
SELECT A.Id,A.[Values]
FROM
(
SELECT *,LEAD(ID) OVER(ORDER BY ID) Lead_Value
FROM your_table
)A
WHERE Lead_Value- ID >1
Something like this:
SELECT s1.*
FROM samples s1
WHERE NOT EXISTS (SELECT id
FROM samples s2
WHERE s2.id = s1.id + 1)
In order to avoid the last row to be selected always you should add:
AND EXISTS (SELECT id
FROM samples s3
WHERE s3.id > s1.id)

Sum Decode statement SQL

I am trying to sum a few Decode statements and column names, but am having difficulties.
currently it is showing as
rank | name | points
----------------------
0 | john | 0
0 | john | 40
1 | john | 30
2 | tom | 22
0 | tom | 0
I expect to have this result:
rank | name | points
----------------------
1 | john | 70
2 | tom | 22
Query:
Select Rank, Name, Code, Points
From
(select
decode(Table.name, 'condition1', Table.value) As Points,
decode(Table.name, 'Condition2', Table.value) As Rank,
Employee.name as Name,
Employee.GA1 as Code
from Table
inner Join Employee
on Empolyee.positionseq = name.positionseq
where Table.name IN ('Condition1', 'Condition2')
);
Select MAX(Rank), Name, Code, SUM(Points)
From
(select
decode(Table.name, 'condition1', Table.value) As Points
decode(Table.name, 'Condition2', Table.value) As Rank
,Employee.name as Name
,Employee.GA1 as Code
from Table
inner Join Employee
on Employee.positionseq = name.positionseq
where Table.name IN( 'Condition1', 'Condition2'))
GROUP BY Employee.id;
I added the SUM, MAX (for rank) and GROUP BY statements. Also corrected some misspellings (Empolyee)
I may be understanding your question incorrectly, however, it seems like you are trying to do the following (omitting inner join for simplicity):
Select MAX(rank), name, SUM(points)
FROM UserRanks
GROUP BY name
Based on your data set above, you should get the following results:
rank name points
1 john 70
2 tom 22

PostgreSQL - MAX value for every user

I have a table
User | Phone | Value
Peter | 0 | 1
Peter | 456 | 2
Peter | 456 | 3
Paul | 456 | 7
Paul | 789 | 10
I want to select MAX value for every user, than it also lower than a tresshold
For tresshold 8, I want result to be
Peter | 456 | 3
Paul | 456 | 7
I have tried the GROUP BY with HAVING, but I am getting
column "phone" must appear in the GROUP BY clause or be used in an aggregate function
Similar query logic works in MySQL, but I am not quite sure how to operate with GROUP BY in PostgreSQL. I dont want to GROUP BY phone.
After I have results from "juergen d" solution, I came up with this which gives me the same results faster
SELECT DISTINCT ON(user) user, phone, value
FROM table
WHERE value < 8
ORDER BY user, value DESC;
select t1.*
from your_table t1
join
(
select user, max(value) as max_value
from your_table
where value < 8
group by user
) t2 on t1.user = t2.user and t1.value = t2.max_value
Alternatively, you could use a ranking function:
select * from
(
select *, RANK() OVER (partition by [user] ORDER BY t.value desc ) as value_rank from test_table as t
where t.value < 8
) as t1
where value_rank = 1