Aggregate rows based on ordered columns in SQL - sql

I have a table with ordered columns by name, from:
Name from to code
name1 0 1 cal
name1 1 2 cal/top
name1 2 4 cal
name1 4 5 top
name1 5 6 cal
name2 0 3 top
name2 3 5 cal
name2 5 7 cal/top
where I want to aggregate them into a table based on code like CAL:
Name minFrom MaxTo
name1 0 4
name1 5 6
name2 3 7
I have tried this:
SELECT name, MIN(from) AS minfrom, MAX(to) AS maxto
FROM table1
GROUP BY name, code, From
HAVING code like "*CAL*"
ORDER BY name, From;
and searched for a like question on stackoverflow with no luck. I think I'll have to do a select subquery but I have no idea where to start... Please help! BTW I'm using postgresql...

You can identify the groups of similar records by using a difference of row numbers. This gives you a grouping column, which can then be used for aggregation:
select name, min(from) as minfrom, max(to) as maxto
from (select t.*,
(row_number() over (partition by name order by from) -
row_number() over (partition by name,
(case when code like 'col%' then 1 else 0 end)
order by from
)
) as grp
from table1 t
) t
where code not like 'col%'
group by grp, name;

Have you been able to reproduce without the need for a partition table? I think just dropping your having statement may make sense for a where statement.
SELECT name, MIN(from) AS minfrom, MAX(to) AS maxto
FROM table1
Where code like "*CAL*"
GROUP BY name, code, From
ORDER BY name, From;

Related

Condition filtering SQL

I have a table
Table name - commands
id
name
status
group_id
id - number
name - string
status - 0 or 1
group_id - number
I need to sort as follows: for all elements with the same group_id I have to check if at least one has a status of 1, if so, then leave, if not, then remove such a group and so on for all group_id
I tried to do it through GROUP BY, and then using HAVING to remove unnecessary groups, but this way I don't get the whole table to be displayed or a query that does not work.
I think it should look like:
SELECT COUNT(*) FROM commands GROUP BY group_id HAVING *condition*
Please let me know if there are any other commands to use.
id
name
status
group_id
1
name1
0
1
2
name2
0
1
3
name3
0
2
4
name4
1
2
5
name5
1
2
6
name6
0
3
7
name7
1
4
Result:
id
name
status
group_id
3
name3
0
2
4
name4
1
2
5
name5
1
2
7
name7
1
4
In Postgres, that's a good spot to use a boolean window function:
select *
from (
select t.*, bool_or(status = 1) over(partition by group_id) has_status_1
from mytable t
) t
where has_status_1
bool_or checks if any row in the group satisfies its predicate ; we can use this information for filtering.
The upside is that the table is scanned only once, as opposed to the correlated subquery solution.
You may use EXISTS operator with a correlated subquery as the following:
SELECT id, name, status, group_id
FROM table_name T
WHERE EXISTS(SELECT 1 FROM table_name D WHERE D.group_id = T.group_id AND D.status=1)
ORDER BY id
See a demo.

How to get MAX Hike in Min month?

below is table:
Name | Hike% | Month
------------------------
A 7 1
A 6 2
A 8 3
b 4 1
b 7 2
b 7 3
Result should be:
Name | Hike% | Month
------------------------
A 8 3
b 7 2
Here is one way of doing this:
SELECT Name, [Hike%], Month
FROM
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY Name ORDER BY [Hike%] DESC, Month) rn
FROM yourTable
) t
WHERE rn = 1
ORDER BY Name;
If you instead want to return multiple records per name, in the case where two or more records might be tied for having the greatest hike%, then replace ROW_NUMBER with RANK.
use correlated subquery
select Name,min(Hike) as Hike,min(Month) as Month
from
(
select * from tablename a
where Hike in (select max(Hike) from tablename b where a.name=b.name)
)A group by Name
You can use something similar to the below:
SELECT Name, MAX(Hike), Month
FROM table
GROUP BY Name, Month
Hope this helps :)

sql - select single ID for each group with the lowest value

Consider the following table:
ID GroupId Rank
1 1 1
2 1 2
3 1 1
4 2 10
5 2 1
6 3 1
7 4 5
I need an sql (for MS-SQL) select query selecting a single Id for each group with the lowest rank. Each group needs to only return a single ID, even if there are two with the same rank (as 1 and 2 do in the above table). I've tried to select the min value, but the requirement that only one be returned, and the value to be returned is the ID column, is throwing me.
Does anyone know how to do this?
Use row_number():
select t.*
from (select t.*,
row_number() over (partition by groupid order by rank) as seqnum
from t
) t
where seqnum = 1;

How to declare a row as a Alternate Row

id Name claim priority
1 yatin 70 5
6 yatin 1 10
2 hiren 30 3
3 pankaj 40 2
4 kavin 50 1
5 jigo 10 4
7 jigo 1 10
this is my table and i want to arrange this table as shown below
id Name claim priority AlternateFlag
1 yatin 70 5 0
6 yatin 1 10 0
2 hiren 30 3 1
3 pankaj 40 2 0
4 kavin 50 1 1
5 jigo 10 4 0
7 jigo 1 10 0
It is sorted as alternate group of same row.
I am Using sql server 2005. Alternate flag starts with '0'. In my example First record with name "yatin" so set AlternateFlag as '0'.
Now second record has a same name as "yatin" so alternate flag would be '0'
Now Third record with name "hiren" is single record, so assign '1' to it
In short i want identify alternate group with same name...
Hope you understand my problem
Thanks in advance
Try
SELECT t.*, f.AlternateFlag
FROM tbl t
JOIN (
SELECT [name],
AlternateFlag = ~CAST(ROW_NUMBER() OVER(ORDER BY MIN(ID)) % 2 AS BIT)
FROM tbl
GROUP BY name
) f ON f.name = t.name
demo
You could use probably an aggregate function COUNT() and then HAVING() and then UNION both Table, like:
SELECT id, A.Name, Claim, Priority, 0 as AlternateFlag
FROM YourTable
INNER JOIN (
SELECT Name, COUNT(*) as NameCount
FROM YourTable
GROUP BY Name
HAVING COUNT(*) > 1 ) A
ON YourTable.Name = A.Name
UNION ALL
SELECT id, B.Name, Claim, Priority, 1 as AlternateFlag
FROM YourTable
INNER JOIN (
SELECT Name, COUNT(*) as NameCount
FROM YourTable
GROUP BY Name
HAVING COUNT(*) = 1 ) B
ON YourTable.Name = B.Name
Now, this assumes that the Names are unique meaning the names like Yatin for example although has two counts is only associated to one person.
See my SqlFiddle Demo
You can use Row_Number() function with OVER that will give you enumeration, than use the reminder of integer division it by 2 - so you'll get 1s and 0s in your SELECT or in the view.

How to distinguish between the first and rest for duplicate records using sql?

These are the input table and required output table.
Input table
ID Name
-------------
1 aaa
1 ababaa
2 bbbbbb
2 bcbcbccbc
2 bcdbcdbbbbb
3 ccccc
Output table
ID Name Ord
-----------------------------
1 aaa first
1 ababaa rest
2 bbbbbb first
2 bcbcbccbc rest
2 bcdbcdbbbbb rest
3 ccccc first
First and Rest is based on the occurrence of an ID field.
Is there a way to write a SQL query to achieve this ?
P.S. - This question is somewhat similar to what I am looking for.
select id, name, case rnk when 1 then 'first' else 'rest' end ord
from(
select *, RANK() over(partition by id order by id,name) rnk
from input
) X
You can also try this
SELECT id, name,
Decode(ROW_NUMBER() OVER (partition by id order by id,name),1,'First','Rest') Ord
FROM Input_table;
You can use this query as this is much simple and yields good performance