Oracle : merging a row and its next one into the same row - sql

I have an issue here.
I have these 4 rows of data :
Origin Destination Distance Carrier Price
Miami New-York 800 BF 500
Dallas Chicago 300 AL 200
Dallas Chicago 300 KH 200
Miami New-York 800 JH 500
What i want is to merge rows 2 and 3 into one row like this :
Dallas Chicago 300 AL, KH 200 (All information is the same except the Carrier)
The problem is that I have to check if the previous row is containing the same information except carriers, for all rows.
How can I achieve that ? with LEAD and LAG ?
Thanks for your help.

Do a self join:
select t1.Origin, t1.Destination, t1.Distance, t1.Carrier, t2.Distance, t2.Carrier
from table t1
join table t2 on t1.Origin = t2.Origin
and t1.Destination = t2.Destination
and t1.Carrier < t2.Carrier
Row order unimportant here. (Of course, that's the dbms way!)
If you want to return alone flights too, do LEFT JOIN instead of just JOIN.

Here you go. But it will add Miami New-York row too. If you want only 2 adjecent rows to be merged, then you need another column like ID or InsertDate or something like that. Then we can modify the given query to aggregate based on that.
with tbl (Origin, Destination, Distance ,Carrier ,Price)
as
(select 'Miami','New-York',800,'BF ', 500 from dual union
select 'Dallas','Chicago',300,' AL', 200 from dual union
select 'Dallas','Chicago',300,' KH', 200 from dual union
select 'Miami','New-York',800,'JH',500 from dual)
select Origin,Destination,Distance,listagg(carrier,',') WITHIN GROUP (ORDER BY origin ) as AggCarrier,Price from tbl
group by Origin,Destination,Distance,Price
Output
Origin Destination Distance AggCarrier Price
Miami New-York 800 BF ,JH 500
Dallas Chicago 300 AL, KH 200
EDIT: Unless we have any column to identify the insert order of the data, you cannot achieve what you want. See the below example. I tried addidng rownum to your data. But it will not assign the rownum in the way you want exactly. It has to come from the table which you want to use. See the example below.
with tbl (Origin, Destination, Distance ,Carrier ,Price)
as
(select 'Miami','New-York',800,'BF', 500 from dual union
select 'Dallas','Chicago',300,'AL', 200 from dual union
select 'Dallas','Chicago',300,'KH', 200 from dual union
select 'Miami','New-York',800,'JH',500 from dual
)
select rownum,tbl.* from tbl
The Output doestn't return Miami first row before Dallas.
ROWNUM ORIGIN DESTINATION DISTANCE CARRIER PRICE
1 Dallas Chicago 300 AL 200
2 Dallas Chicago 300 KH 200
3 Miami New-York 800 BF 500
4 Miami New-York 800 JH 500
So you need anything. ID/InsertTime or any other identifier to find that. Else DB will never know which record was inserted first. Please ask for it to achieve what you want.

Related

Get next value for each of values from next table CTE

I have the following table:
dbo.split
Name Time
Alex 120
John 80
John 300
Mary 500
Bob 900
And then another table dbo.travel
Name Time
Alex 150
Alex 160
Alex 170
John 90
John 100
John 310
Mary 550
Mary 600
Mary 499
Bob 800
Bob 700
For each value in table split I need to find the next value in table travel. I tried to do it with CTE a with ROW_NUMBER() to get next by group, but there's no way I can group by correct value, since dbo.split can containt multiple values for the same name.
I'm looking for the following output:
Name Time TravelTime
Alex 120 150
John 80 90
John 300 310
Mary 500 550
Bob 900 NULL
Here's what I have so far but it fails because split table can have multiple records per person:
;with result as (
select t.*,
ROW_NUMBER() OVER (Partition BY t.Name order by t.Time) as rn
from travel t join split s
on t.Name = s.Name and t.TIME>s.Time
)
I would use apply:
select s.*, t.time
from split s outer apply
(select top (1) t.*
from travel t
where t.name = s.name and t.time > s.time
order by t.time asc
) t;
In this case, apply is doing essentially the same thing as a correlated subquery, so you could phrase it that way as well.
You can try as below
Select * from(Select
Name,t.time,t1.time,
Row_number() over (partition by
Name,t.time order by t1.time) rn
from split t
Join travel t1 on t.time <t1.time and
t.name =t1.name)
where
rn=1;

Get sum of consecutive rows in one row

I have a table as:
Emp_id Emp_Name Department Score
123 Raju D1 300
124 Ravi D2 400
125 Annie D3 600
126 Ajay D4 200
127 Amey D5 500
128 Akil D6 100
I need output as
Sum of Score of row1 and row2 in row1
Sum of Score of row2 and row3 in row2,
and so on
So the output should be:
700
1000
800
700
600
I need the output through a SQL query and not a procedure or something else.
It is very easy to do this with an analytic function that uses a sliding window from the current row to the next one (ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING):
SELECT Emp_id, Emp_Name, Department, Score, sum(Score)
OVER (ORDER BY Emp_id ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) sum
FROM score;
When executing this query on your data the result is as follows:
123 Raju D1 300 700
124 Ravi D2 400 1000
125 Annie D3 600 800
126 Ajay D4 200 700
127 Amey D5 500 600
128 Akil D6 100 100
See Database SQL Language Reference, Analytic Functions for further details.
You need Lead function, which will show second row with first row and so on. Name it nxt_score. Then you can add score and nxt_score to get your output. You will also need nvl as for last row, the nxt_score will be null. So in that case, it will add the score with 0.
Remove other columns from select clause if you want.
SELECT
t1.*,
score + nvl(nxt_score, 0) AS cum_score
FROM
(SELECT
t.*,
LEAD(score, 1) OVER (ORDER BY emp_id) AS nxt_score
FROM table1 t
) t1

An oracle sql query to find value from the attributes

Suppose I have the following table 'player' in oracle database:
P_ID P_NAME C_ID DEBUT MATCH RUNS
101 amla 204 2003 190 5000
102 mushi 200 2001 240 7500
103 sakib 200 1999 150 5000
104 ricky 205 1993 180 7000
105 sachin 203 1990 250 8000
106 yuvi 203 1999 150 6900
I need a query to display the c_id, total runs done by all batsmen of the country which has the maximum run scorer. (in this case maximum run scorer is
sachin. so the query should return : c_id = 203, runs = 14900).
I have only been able to find the maximum run scorer and the country which he belongs to. the query:
select c_id, runs from player where runs = (select max(runs) from player);
does that. However, I am not been able to proceed further.
When you need condition on aggregate function, you must use sub-query or having clause.
This return always one row, but it's wrong when more then one groups have same total:
SELECT *
FROM (
SELECT c_id, Sum(runs) total
FROM player
GROUP BY c_id
ORDER BY total DESC
) WHERE ROWNUM =1;
But you can get all using this:
SELECT c_id, Sum(runs) total
FROM player
GROUP BY c_id
HAVING Sum(runs) = (
SELECT Max(t) from (
SELECT Sum(runs) t
FROM player
GROUP BY c_id))
;

Select statement with multiple rows from condition on values in single column

I have the table below.Using salary as condition I want to get multiple rows.
Below is current table call it employee.
empid name salary
-----------------------------------
1 A1 alex 20000
2 B2 ben 4500
3 C1 carl 14000
compare the salary to certain fixed values, and every time the salary is larger than the fixed value, show a record in output.My attempt condition case is close to this:
incometype= case When salary<6000 then 101 When salary Between 6000 And 18000 Then
102 Else 103 End
Desired ouput would be:
empid name salary incometype
------------------------------------------
1 A1 alex 20000 101
2 A1 alex 20000 102
3 A! alex 20000 103
4 B2 ben 4500 101
5 C1 carl 14000 101
6 C1 carl 14000 102
I have tried using union but union will give me 3 rows for each record even when value meets 1st condition.
Your question is unclear, because your logic implies that you should only have 3 output rows for 3 input rows. Your output however implies that you want to compare the salary to certain fixed values, and every time the salary is larger than the fixed value, show a record in output.
If the former is the case, Minh's query is all you need. In the latter case, you can do something like this:
select e.*, m.incometype
from employee e
left join
(
select 0 as threshold, 101 as incometype
union
select 5999 as threshold, 102 as incometype
union
select 17999 as threshold, 103 as incometype
) m
on e.salary > m.threshold
order by e.empid
If you want to add a calculate column i.e. one with values calculated using columns in this query, you can simply add it as a column in the select clause, like so:
select e.*,
m.incometype,
case
when <first condition> then <business logic here>
....
else <handle default case>
end as yourcomputedcolumn
from
...
This returns 3 rows and enough for your need:
SELECT empid, name, salary,
case When salary<6000 then 101
When salary Between 6000 And 18000 Then 102
Else 103 End as incometype
FROM employee;
Not very clear on the requirement, however the following worked for me:
Select
EmpId,Name,Sal,101 IncomeType
from Emp
Union all
Select
EmpId,Name,Sal,102
from Emp
Where Sal > 6000
union all
Select
EmpId,Name,Sal,103
from Emp
Where Sal > 18000;

Selecting only rows with the highest value of one field, grouped by another field

I have a table that has information structured like this:
ID Points Name School
1 123 James A
2 534 Henry B
3 56 Henry B
4 153 Chris B
5 95 Chris B
6 83 Chris B
7 421 James A
And I need to get out of a query the rows that have the same name, but only the highest points for each like this:
ID Points Name School
7 421 James A
2 534 Henry B
4 153 Chris B
Any ideas on how this could be accomplished with a query? I've spent way too much time trying to figure this out.
select name,school,max(points) from table group by name,school
That will give you the max points per name/school combination. Join it to itself if you want the ID:
select table.* from table inner join
(select name,school,max(points) as points from table group by name,school) a
on a.name = table.name and a.school = b.school and a.points = table.points
edit : sorry, this is a SQL solution...just saw the MSACCESS tag. Logic is right, but you'll need to convert to access syntax.
edit to correct the second query, missed a column inh my join
SELECT
(SELECT TOP 1 ID FROM Table
WHERE
Name = t.Name AND
School=t.School AND
Points=t.Points
) as Id, t.Name, t.Points, t.School
FROM
(SELECT Name, School, max(Points) as Points
FROM Table
GROUP BY Name, School) t