Select items by column value - sql

I have a table in SQL used to store result like so
Result
---------------
ID | DateCreated
1 | 2014-10-10
The Items under the result above
ResultItems
---------------
ResultID | StudentID | SubjID | Test1 | Test2 | Exam
1 | 1 | 1 | 7 | 7 | 30
1 | 2 | 1 | 8 | 8 | 35
1 | 1 | 2 | 5 | 5 | 45
1 | 2 | 2 | 6 | 6 | 40
I need to select from this tables so that each subject is in its own column, with the score of each subject summed under it
Result items
Result Output
---------------
StudentID| SubjID-1 | SubjID-2
1 | 44 | 55
2 | 51 | 52
I did try quiet some queries, such as this one below, which didn't give the result I needed
SELECT r.*,
ri.StudentID,
ri.Test1,
ri.Test2,
ri.Exam,
( ri.Test1+ ri.Test2 + ri.Exam ) Total
FROM Result r
LEFT JOIN ResultItems ri
ON ri.ResultID = r.id
WHERE ri.Test1 <> '-'
AND
ri.Test2 <> '-'
AND
ri.exam <> '-';
How can I amend this query?
Edit
I read about Pivot and saw this question SQL - columns for different categories, in which case the names/id of the subject has to be know before hand, which would not work for my case

Solution for SQL Server
All you need is called Pivot.
CREATE TABLE #ResultItems
(
ResultID INT,
StudentID INT,
SubjID INT,
Test1 INT,
Test2 INT,
Exam INT
)
INSERT INTO #ResultItems (ResultID, StudentID, SubjID, Test1, Test2, Exam)
VALUES(1, 1 , 1 , 7, 7, 30),
(1, 2 , 1 , 8, 8, 35),
(1, 1 , 2 , 5, 5, 45),
(1, 2 , 2 , 6, 6, 40)
SELECT StudentId, [1], [2]
FROM (
SELECT StudentId, SubjID, Test1 + Test2 + Exam AS TmpSum
FROM #ResultItems
) AS DT
PIVOT(SUM(TmpSum) FOR SubjID IN ([1], [2])) AS PT
DROP TABLE #ResultItems
SQLite solution
Use CASE
SELECT StudentId, SUM(Subj1) AS Subj1, SUM(Subj2) As Subj2
FROM (
SELECT t1.StudentId, CASE WHEN SubjID = 1 THEN Test1 + Test2 + Exam ELSE 0 END AS Subj1,
CASE WHEN SubjID = 2 THEN Test1 + Test2 + Exam ELSE 0 END AS Subj2
FROM #ResultItems AS t1
) AS T
GROUP BY T.StudentID
or subqueries:
SELECT t1.StudentId, (SELECT Test1 + Test2 + Exam FROM #ResultItems WHERE StudentID = t1.StudentID AND SubjID = 1) AS Subj1,
(SELECT Test1 + Test2 + Exam FROM #ResultItems WHERE StudentID = t1.StudentID AND SubjID = 2) AS Subj2
FROM #ResultItems AS t1
GROUP BY t1.StudentID

Related

Change Position of Serial Number in SQL

I have a table named students. the structure is given below
______________________________
AdmissionNo RollNo Name
______________________________
1001 1 A
1003 2 B
1005 3 C
1006 4 D
1008 5 E
Now i want to change rollno 4 to 2 and increment forthcoming numbers
so the result should be like below
-------------------------------
AdmissionNo RollNo Name
-------------------------------
1001 1 A
1006 2 D
1003 3 B
1005 4 C
1008 5 E
--------------------------------
How to attain this using sql Query.
Note: Question Edited as per 'The Impaler' said.Admission number is not changing.only Roll no change. The values in table are examples actual values are hundreds of records.
With the omission of a dialect, I have answered this in T-SQL, as I wanted a stab at this.
This isn't pretty, however, I use a couple of updatable CTE's to find the offset for the specific rows, and then update the needed rows accordingly:
USE Sandbox;
GO
CREATE TABLE dbo.YourTable (AdmissionNo int, Rollno tinyint, [Name] char(1));
INSERT INTO dbo.YourTable
VALUES(1001,1,'A'),
(1003,2,'B'),
(1005,3,'C'),
(1006,4,'D'),
(1008,5,'E');
GO
DECLARE #NewPosition tinyint = 2,
#MovingName char(1) = 'D';
WITH Offsetting AS(
SELECT *,
COUNT(CASE Rollno WHEN #NewPosition THEN 1 END) OVER (ORDER BY RollNo ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) -
COUNT(CASE [Name] WHEN #MovingName THEN 1 END) OVER (ORDER BY RollNo ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS LagOffset
FROM dbo.YourTable),
NewNames AS(
SELECT *,
CASE RollNo WHEN #NewPosition THEN #MovingName
ELSE LAG([Name],LagOffset) OVER (ORDER BY RollNo)
END AS NewName
FROM Offsetting)
UPDATE NewNames
SET [Name] = NewName;
GO
SELECT *
FROM dbo.YourTable;
GO
DROP TABLE dbo.YourTable;
Not pretty but you could use some sub queries
DROP TABLE IF EXISTS T;
create table t
(AdmissionNo int, RollNo int, Name varchar(1));
insert into t values
(1001 , 1 , 'A'),
(1003 , 2 , 'B'),
(1005 , 3 , 'C'),
(1006 , 4 , 'D'),
(1008 , 5 , 'E');
select t.*,
case when rollno = 2 then (select name from t where rollno = 4)
when rollno > 2 and
rollno <> (select max(rollno) from t) then (select name from t t1 where t1.rollno < t.rollno order by t1.rollno desc limit 1)
else name
end
from t;
+-------------+--------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 1001 | 1 | A | A |
| 1003 | 2 | B | D |
| 1005 | 3 | C | B |
| 1006 | 4 | D | C |
| 1008 | 5 | E | E |
+-------------+--------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
5 rows in set (0.001 sec)
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(admission_no INT NOT NULL PRIMARY KEY
,roll_no INT NOT NULL
,name CHAR(1) NOT NULL
);
INSERT INTO my_table VALUES
(1001,1,'A'),
(1003,2,'B'),
(1005,3,'C'),
(1006,4,'D'),
(1008,5,'E');
SELECT *
, CASE WHEN roll_no = 4 THEN 2
WHEN roll_no >= 2 AND roll_no < 4 THEN roll_no + 1
ELSE roll_no END x FROM my_table;
+--------------+---------+------+---+
| admission_no | roll_no | name | x |
+--------------+---------+------+---+
| 1001 | 1 | A | 1 |
| 1003 | 2 | B | 3 |
| 1005 | 3 | C | 4 |
| 1006 | 4 | D | 2 |
| 1008 | 5 | E | 5 |
+--------------+---------+------+---+
5 rows in set (0.00 sec)
...or, as an update...
UPDATE my_table x
JOIN
( SELECT *
, CASE WHEN roll_no = 4 THEN 2
WHEN roll_no >= 2 AND roll_no < 4 THEN roll_no + 1
ELSE roll_no END n
FROM my_table
) y
ON y.admission_no = x.admission_no
SET x.admission_no = y.n;
You'd probably want to extend this idea to deal with the fact that rows can be dragged up and down the list, so something like this...
SET #source = 1, #target = 5;
SELECT *
, CASE WHEN roll_no = GREATEST(#source,#target) THEN LEAST(#source,#target)
WHEN roll_no >= LEAST(#source,#target) AND roll_no < GREATEST(#source,#target) THEN roll_no + 1
ELSE roll_no END x
FROM my_table;
Try this below query
; with cte as (select a.AdmissionNo, a.RollNo, b.Name from student a
join student b on a.RollNo=b.RollNo+1
where a.RollNo between 3 and 4
union all
select a.AdmissionNo, a.RollNo, b.Name from student a
left join student b on a.RollNo+2=b.RollNo
where a.RollNo=2)
update a set a.Name = b.name
from student a
join cte b on a.rollno=b.rollno

Splitting dynamically SQL columns into multiple columns based on a different column value

I am trying to convert dynamically a table like this:
+----+---------+-------+
| ID | Subject | Users |
+----+---------+-------+
| 1 | Hi! | Anna |
| 2 | Hi! | Peter |
| 3 | Try | Jan |
| 4 | Try | Peter |
| 5 | Try | Jan |
| 6 | Problem | Anna |
| 7 | Problem | José |
| 8 | Test | John |
| 9 | Test | John |
| 10 | Hi! | Anna |
| 11 | Hi! | José |
| 12 | Hi! | Anna |
| 13 | Hi! | Joe |
+----+---------+-------+
Into something like that:
+----+---------+-------+-------+-------+-------+
| ID | Subject | User1 | User2 | User3 | User4 |
+----+---------+-------+-------+-------+-------+
| 1 | Hi! | Anna | Peter | José | NULL |
| 2 | Try | Jan | Peter | NULL | NULL |
| 3 | Problem | Anna | José | NULL | NULL |
| 4 | Test | John | NULL | NULL | NULL |
+----+---------+-------+-------+-------+-------+
I have been reading the following links, but they are thought for splitting a column into a predefined number of columns:
Splitting SQL Columns into Multiple Columns Based on Specific Column Value
Split column into two columns based on type code in third column
I would need to split it dinamically depending on the content of the table.
SQL:
--【Build Test Data】
create table #Tem_Table ([ID] int,[Subject] nvarchar(20),[Users] nvarchar(20));
insert into #Tem_Table ([ID],[Subject] ,[Users]) values
('1','Hi!','Anna')
,('2','Hi!','Peter')
,('3','Try','Jan')
,('4','Try','Peter')
,('5','Try','Jan')
,('6','Problem','Anna')
,('7','Problem','José')
,('7','Test','John')
,('9','Test','John')
,('10','Hi! ','Anna')
,('11','Hi! ','José')
,('12','Hi! ','Anna')
,('13','Hi! ','Joe')
;
--STEP 1 distinct and ROW_NUMBER
with distinct_table as (
select [Subject],[Users]
,ROW_NUMBER() OVER (PARTITION BY [Subject] order by [Users]) [rank]
from (
select distinct [Subject],[Users] from #Tem_Table
) T00
)
--STEP 2 Group by row_count
,group_table as (
select [Subject]
from distinct_table T
group by [Subject]
)
--STEP 3 Use Left Join and Rank
select
T.[Subject],T1.[Users] as User1, T2.[Users] as User2 , T3.[Users] as User3, T4.[Users] as User4
from group_table T
left join distinct_table T1 on T.[Subject] = T1.[Subject] and T1.[rank] = 1
left join distinct_table T2 on T.[Subject] = T2.[Subject] and T2.[rank] = 2
left join distinct_table T3 on T.[Subject] = T3.[Subject] and T3.[rank] = 3
left join distinct_table T4 on T.[Subject] = T4.[Subject] and T4.[rank] = 4
order by [Subject];
result:
-------------------- -------------------- -------------------- -------------------- --------------------
Hi! Anna Joe José Peter
Problem Anna José NULL NULL
Test John NULL NULL NULL
Try Jan Peter NULL NULL
Update the Dynamic version :
--STEP 1 distinct and ROW_NUMBER
SELECT * into #distinct_table from (
select [Subject],[Users]
,ROW_NUMBER() OVER (PARTITION BY [Subject] order by [Users]) [rank]
from (
select distinct [Subject],[Users] from #Tem_Table
) T00
)T;
--STEP 2 Group by row_count
SELECT * into #group_table from (
select [Subject] ,count(1) [count]
from #distinct_table T
group by [Subject]
)T;
--Use Exec
DECLARE #select_sql AS NVARCHAR(MAX) = ' select T.[Subject] ',
#join_sql AS NVARCHAR(MAX) = ' from #group_table T ',
#max_count INT = (SELECT max([count]) FROM #group_table),
#temp_string NVARCHAR(5),
#temp_string_addone NVARCHAR(5)
;
DECLARE #index int = 0 ;
WHILE #index < #max_count
BEGIN
sELECT #temp_string = Convert(nvarchar(10),#index);
sELECT #temp_string_addone = Convert(nvarchar(10),#index+1);
select #select_sql = #select_sql + ' , T'+#temp_string_addone+'.[Users] as User'+#temp_string_addone+' '
select #join_sql = #join_sql + 'left join #distinct_table T'+#temp_string_addone+' on T.[Subject] = T'+#temp_string_addone+'.[Subject] and T'+#temp_string_addone+'.[rank] = '+#temp_string_addone+' ';
SET #index = #index + 1;
END;
EXEC (#select_sql
+ #join_sql
+' order by [Subject]; ')
;
CREATE TABLE mytable
([ID] int, [Subject] varchar(7), [Users] varchar(5))
;
INSERT INTO mytable
([ID], [Subject], [Users])
VALUES
(1, 'Hi!', 'Anna'),
(2, 'Hi!', 'Peter'),
(3, 'Try', 'Jan'),
(4, 'Try', 'Peter'),
(5, 'Try', 'Jan'),
(6, 'Problem', 'Anna'),
(7, 'Problem', 'José'),
(8, 'Test', 'John'),
(9, 'Test', 'John'),
(10, 'Hi!', 'Anna'),
(11, 'Hi!', 'José'),
(12, 'Hi!', 'Anna'),
(13, 'Hi!', 'Joe')
;
select distinct subject,
(select users from (
select distinct users from mytable where subject=m.subject) a order by users offset 0 rows fetch next 1 row only) user1,
(select users from (
select distinct users from mytable where subject=m.subject) a order by users offset 1 rows fetch next 1 row only) user2,
(select users from (
select distinct users from mytable where subject=m.subject) a order by users offset 2 rows fetch next 1 row only) user3,
(select users from (
select distinct users from mytable where subject=m.subject) a order by users offset 3 rows fetch next 1 row only) user4
from mytable m
you can use below dynamic query to get the result-
create table test_Raw(ID int ,Subject varchar(100), Users varchar(100))
insert into test_Raw
values (1,' Hi!','Anna'),
(2,' Hi!','Peter'),
(3,'Try','Jan'),
(4,'Try','Peter'),
(5,'Try','Jan'),
(6,'Problem','Anna'),
(7,'Problem','José'),
(8,'Test','John'),
(9,'Test','John'),
(10,' Hi!','Anna'),
(11,' Hi!','José'),
(12,' Hi!','Anna'),
(13,' Hi!','Joe')
--select * from test_Raw
select dense_RANK() over( order by Subject) Ranking1, dense_RANK() over(partition by Subject order by users) Ranking2 , Subject , Users
into test
from test_Raw
group by Subject , Users
order by 3
declare #min int , #mx int , #Select nvarchar(max) , #from nvarchar(max) , #vmin varchar(3)
select #min= 1 , #mx = MAX(Ranking2) , #Select= 'select ' , #from = ' from test t1 ' , #vmin = '' from test
while (#min<=#mx)
begin
select #vmin = CAST(#min as varchar(3))
select #Select = #Select + CASE WHEN #min = 1 THEN 't1.Ranking1 as ID , t1.Subject , t1.Users AS User1 ' ELSE ',t' +#vmin+'.Users as User'+#vmin END
select #from = #from + CASE WHEN #min = 1 THEN '' ELSE ' left join test t'+#vmin + ' on t1.Ranking1 = t' + #vmin + '.Ranking1 and t1.Ranking2 + ' + cast (#min-1 as varchar(10)) + ' = t'+#vmin+'.Ranking2' END
set #min = #min + 1
end
select #Select = #Select + #from + ' where t1.Ranking2 = 1'
exec sp_executesql #Select

Update table in Postgresql by grouping rows

I want to update a table by grouping (or combining) some rows together based on a certain criteria. I basically have this table currently (I want to group by 'id_number' and 'date' and sum 'count'):
Table: foo
---------------------------------------
| id_number | date | count |
---------------------------------------
| 1 | 2001 | 1 |
| 1 | 2001 | 2 |
| 1 | 2002 | 1 |
| 2 | 2001 | 6 |
| 2 | 2003 | 12 |
| 2 | 2003 | 2 |
---------------------------------------
And I want to get this:
Table: foo
---------------------------------------
| id_number | date | count |
---------------------------------------
| 1 | 2001 | 3 |
| 1 | 2002 | 1 |
| 2 | 2001 | 6 |
| 2 | 2003 | 14 |
---------------------------------------
I know that I can easily create a new table with the pertinent info. But how can I modify an existing table like this without making a "temp" table? (Note: I have nothing against using a temporary table, I'm just interested in seeing if I can do it this way)
If you want to delete rows you can add a primary key (for distinguish rows) and use two sentences, an UPDATE for the sum and a DELETE for obtain less rows.
You can do something like this:
create table foo (
id integer primary key,
id_number integer,
date integer,
count integer
);
insert into foo values
(1, 1 , 2001 , 1 ),
(2, 1 , 2001 , 2 ),
(3, 1 , 2002 , 1 ),
(4, 2 , 2001 , 6 ),
(5, 2 , 2003 , 12 ),
(6, 2 , 2003 , 2 );
select * from foo;
update foo
set count = count_sum
from (
select id, id_number, date,
sum(count) over (partition by id_number, date) as count_sum
from foo
) foo_added
where foo.id_number = foo_added.id_number
and foo.date = foo_added.date;
delete from foo
using (
select id, id_number, date,
row_number() over (partition by id_number, date order by id) as inner_order
from foo
) foo_ranked
where foo.id = foo_ranked.id
and foo_ranked.inner_order <> 1;
select * from foo;
You can try it here: http://rextester.com/PIL12447
With only one UPDATE
(but with a trigger) you can set a NULL value in count and trigger a DELETE in that case.
create table foo (
id integer primary key,
id_number integer,
date integer,
count integer
);
create function delete_if_count_is_null() returns trigger
language plpgsql as
$BODY$
begin
if new.count is null then
delete from foo
where id = new.id;
end if;
return new;
end;
$BODY$;
create trigger delete_if_count_is_null
after update on foo
for each row
execute procedure delete_if_count_is_null();
insert into foo values
(1, 1 , 2001 , 1 ),
(2, 1 , 2001 , 2 ),
(3, 1 , 2002 , 1 ),
(4, 2 , 2001 , 6 ),
(5, 2 , 2003 , 12 ),
(6, 2 , 2003 , 2 );
select * from foo;
update foo
set count = case when inner_order = 1 then count_sum else null end
from (
select id, id_number, date,
sum(count) over (partition by id_number, date) as count_sum,
row_number() over (partition by id_number, date order by id) as inner_order
from foo
) foo_added
where foo.id_number = foo_added.id_number
and foo.date = foo_added.date
and foo.id = foo_added.id;
select * from foo;
You can try it in: http://rextester.com/MWPRG10961

How do I limit a query with distinct values in MariaDB

I have a table like this:
Id Num Some text
--------------------
1 1 ""
2 1 ""
3 2 ""
4 2 ""
5 2 ""
6 2 ""
7 3 ""
What I want is a query to select the first ten distinct nums, so if I want to get the two first nums I'll get the first six rows. I'm using MariaDB.
Try this approach:
select *
from Table1 as T1
join (
select distinct num
from Table1
order by num
limit 2 ) as T2
on T1.num = T2.num
;
In a fiddle here: http://sqlfiddle.com/#!9/1468ad/5
In MySQL you can use LIMIT. The caveat is that LIMIT is not directly supported in the IN sub-queries and therefore you must use a sub-query inside a IN sub-query:
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE Table1
(`Id` int, `Num` int, `Some text` varchar(2))
;
INSERT INTO Table1
(`Id`, `Num`, `Some text`)
VALUES
(1, 1, '""'),
(2, 1, '""'),
(3, 2, '""'),
(4, 2, '""'),
(5, 2, '""'),
(6, 2, '""'),
(7, 3, '""')
;
Query 1:
select * from Table1
where Num in (select Num FROM(select distinct Num from Table1 order by Num limit 2)a)
Results:
| Id | Num | Some text |
|----|-----|-----------|
| 1 | 1 | "" |
| 2 | 1 | "" |
| 3 | 2 | "" |
| 4 | 2 | "" |
| 5 | 2 | "" |
| 6 | 2 | "" |

Alternate of lead lag function in SQL Server 2008

I want to compare the current row with a value in the next row. SQL has LEAD and LAG functions to get the next and previous values but I can not use them because I am using SQL Server 2008.
So how do I get this?
I have table with output
+----+-------+-----------+-------------------------+
| Id | ActId | StatusId | MinStartTime |
+----+-------+-----------+-------------------------+
| 1 | 42 | 1 | 2014-02-14 11:17:21.203 |
| 2 | 42 | 1 | 2014-02-14 11:50:19.367 |
| 3 | 42 | 1 | 2014-02-14 11:50:19.380 |
| 4 | 42 | 6 | 2014-02-17 05:25:57.280 |
| 5 | 42 | 6 | 2014-02-19 06:09:33.150 |
| 6 | 42 | 1 | 2014-02-19 06:11:24.393 |
| 7 | 42 | 6 | 2014-02-19 06:11:24.410 |
| 8 | 42 | 8 | 2014-02-19 06:44:47.070 |
+----+-------+-----------+-------------------------+
What I want to do is if the current row status is 1 and the next row status is 6 and both times are the same (up to minutes) then I want to get the row where the status is 1.
Eg: Id 6 row has status 1 and Id 7 row has status 6 but both times are the same ie. 2014-02-19 06:11
So I want to get this row or id for status 1 ie. id 6
In your case, the ids appear to be numeric, you can just do a self-join:
select t.*
from table t join
table tnext
on t.id = tnext.id - 1 and
t.StatusId = 1 and
tnext.StatusId = 6 and
datediff(second, t.MinStartTime, tnext.MinStartTime) < 60;
This isn't quite the same minute. It is within 60 seconds. Do you actually need the same calendar time minute? If so, you can do:
select t.*
from table t join
table tnext
on t.id = tnext.id - 1 and
t.StatusId = 1 and
tnext.StatusId = 6 and
datediff(second, t.MinStartTime, tnext.MinStartTime) < 60 and
datepart(minute, t.MinStartTime) = datepart(minute, tnext.MinStartTime);
Just posting a more complex join using two different tables created with Gordon's foundation. Excuse the specific object names, but you'll get the gist. Gets the percentage change in samples from one to the next.
SELECT
fm0.SAMPLE curFMSample
, fm1.SAMPLE nextFMSample
, fm0.TEMPERATURE curFMTemp
, fm1.TEMPERATURE nextFMTemp
, ABS(CAST((fm0.Temperature - fm1.Temperature) AS DECIMAL(4, 0)) / CAST(fm0.TEMPERATURE AS DECIMAL(4, 0))) AS fmTempChange
, fm0.GAUGE curFMGauge
, fm1.GAUGE nextFMGauge
, ABS(CAST((fm0.GAUGE - fm1.GAUGE) AS DECIMAL(4, 4)) / CAST(fm0.GAUGE AS DECIMAL(4, 4))) AS fmGaugeChange
, fm0.WIDTH curFMWidth
, fm1.WIDTH nextFMWidth
, ABS(CAST((fm0.Width - fm1.Width) AS DECIMAL(4, 2)) / CAST(fm0.Width AS DECIMAL(4, 2))) AS fmWidthChange
, cl0.TEMPERATURE curClrTemp
, cl1.TEMPERATURE nextClrTemp
, ABS(CAST((cl0.Temperature - cl1.Temperature) AS DECIMAL(4, 0)) / CAST(cl0.TEMPERATURE AS DECIMAL(4, 0))) AS clrTempChange
FROM
dbo.COIL_FINISHING_MILL_EXIT_STR02 fm0
INNER JOIN dbo.COIL_FINISHING_MILL_EXIT_STR02 fm1 ON (fm0.SAMPLE = fm1.SAMPLE - 1 AND fm1.coil = fm0.coil)
INNER JOIN dbo.COIL_COILER_STR02 cl0 ON fm0.coil = cl0.coil AND fm0.SAMPLE = cl0.SAMPLE
INNER JOIN dbo.COIL_COILER_STR02 cl1 ON (cl0.SAMPLE = cl1.SAMPLE - 1 AND cl1.coil = cl0.coil)
WHERE
fm0.coil = 2015515872
Well, I would suggest a very simple solution if you do not have a sequential row id but a different step (if some records were deleted for example..):
declare #t table(id int, obj_name varchar(5))
insert #t select 1,'a'
insert #t select 5,'b'
insert #t select 22,'c'
insert #t select 543,'d'
---------------------------------
select *from #t
Example Source Table #t:
---------------------------------
id obj_name
1 a
5 b
22 c
543 d
---------------------------------
Select with self join
select obj_name_prev=tt.obj_name, obj_name_next=min(t.obj_name)
from #t t
join #t tt on tt.id < t.id
group by tt.obj_name
Result:
---------------------------------
obj_name_prev obj_name_next
a b
b c
c d
---------------------------------