SQL: how to select next id given an id - sql

There is one table T ( id integer, primary key ( id).
I want a parameterized query that, given id i:
will return next consecutive id,
if i = biggest id in T, query should return the smallest id in T (cyclical)

You can select the smallest id over the value #i (if any), and the smallest id, then get the largest:
select max(id)
from (
select top 1 id
from T
where id > #i
order by id
union all
select top 1 id
from T
order by id
) x
Or perhaps:
select max(id)
from (
select min(id) as id
from T
where id > #i
union all
select min(id)
from T
) x

This appears to be what you're looking for:
CREATE PROCEDURE dbo.ProcName
(
#ID INTEGER
)
AS
SELECT TOP 1 id
FROM table
WHERE id > #ID
ORDER BY id

Related

Select until condition in sql

I've got a table
CREATE TABLE Table1(
Id INT NOT NULL IDENTITY(1,1),
EvType INT NOT NULL,
CreatedByUserId INT NOT NULL
)
Initial data:
And i wonna get only rows which meet the next condition: We select row until Id of the row will be less than first row with EvType == 200 per createdByUserId. So we need to select firsly all first rows for each user with evType == 200, which i've done in this way:
WITH EVS1 AS (
SELECT evs.Id, evs.EvType, evs.CreatedByUserId
ROW_NUMBER() OVER (PARTITION BY evs.CreatedByUserId ORDER BY evs.CreatedDate DESC) as rk
FROM [dbo].Table1 evs)
select *
From EVS1
WHERE EVS1.rk=1
Which produces the following result:
And then somehow i need to select rows for each user until Id is greater then row from CTE for that user, Is that possible to do that?
So we need to retrieve from that table rows until 4th included. Skip the 5th row cause it goes after the first user row with evType 200
Expected Result:
Find min(id) first and then the row having lower or equal id
SELECT *
FROM EVS1
WHERE id <= (SELECT MIN(id) FROM EVS1 WHERE evType = 200)
I assume that you define the ordering according to the id attribute.
If it is necessary to do it for each CreatedByUserId then use a dependent subquery for the minimal id computation
SELECT *
FROM EVS1 e1
WHERE id <= (
SELECT MIN(id)
FROM EVS1 e2
WHERE e2.evType = 200
and e1.CreatedByUserId = e2.CreatedByUserId
)
DBFIDDLE DEMO
I believe that this solution will be faster then a window function for a large data if you will have an index
CREATE INDEX ix_evs1_evType_CreatedByUserId ON evs1(evType, CreatedByUserId) INCLUDE(id)
You can do a window max:
select Id, EvType, CreatedByUserId
from (
select
t.*,
max(case when EvType = 200 then 1 else 0 end)
over(partition by CreatedByUserId order by Id) flagEvType
from [dbo].Table1
)
where flagEvType = 0
You want to select all rows created by a user except for those where an event type 200 occurred before:
select *
from mytable t1
where not exists
(
select null
from mytable t2
where t2.evtype = 200
and t2.createdbyuserid = t1.createdbyuserid
and t2.id < t1.id
);

Select number of IDs in more than one table (from three tables)

I need the count of this:
select distinct ID
from (
select ID from A
union all
select ID from B
union all
select ID from C
) ids
GROUP BY ID HAVING COUNT(*) > 1;
but I have no idea how to do it.
Use a subquery:
select count(*)
from (select ID
from (select ID from A
union all
select ID from B
union all
select ID from C
) ids
group by ID
having count(*) > 1
) i;
SELECT DISTINCT is almost never needed with GROUP BY and definitely not in this case.
You just want to find the id that appear 2 more times in the A,B,C table, the SQL is below:
select count(1) from (
select
id,
count(1)
from
(
select ID from A
union all
select ID from B
union all
select ID from C
)
group by id having(count(1)>1)
) tmp

Inserting a value into a table, determining the minimum available

I have a Postgres table which holds information for users, the fields in this table are:
id (int)
name (string)
I would like to be able to add a user, and providing him an ID that will be deferred from the current values that are in the table:
The ID that will be given to the added user should be the minimal that is available in the table i.e if I have a table with the following IDs: 1, 3, 4, 5, 8,
I would like the ID of the user to be 2, next time I'll add a user its ID will be 6, and the next one will be 7, and so on.
What would be the right query?
Following select query can be used to find out missed ids from table
SELECT t
FROM generate_series((
SELECT min(id) + 1
FROM tb
), (
SELECT max(id) + 1
FROM tb
)) t
WHERE NOT t IN (
SELECT id
FROM tb
)
ORDER BY t ASC
and the insert can be done by following ways,
INSERT INTO tb
VALUES (
(
SELECT t
FROM generate_series((
SELECT min(id) + 1
FROM tb
), (
SELECT max(id) + 1
FROM tb
)) t
WHERE NOT t IN (
SELECT id
FROM tb
)
ORDER BY t ASC limit 1
)
,'B'
)
OR
Create a function like this
CREATE OR replace FUNCTION missed_id ()
RETURNS INTEGER AS $$
SELECT t
FROM generate_series((
SELECT min(id) + 1
FROM tb
), (
SELECT max(id) + 1
FROM tb
)) t
WHERE NOT t IN (
SELECT id
FROM tb
)
ORDER BY t ASC limit 1;;$$
LANGUAGE sql
and insert should be
insert into tb values (missed_id(),'B')
insert into auth_user(id, user_name)
values((select a from generate_series((select min(id) from auth_user)
,(select max(id)+1 from auth_user)
) as a
left join auth_user on (a = id )
where id is null order by a limit 1)
, 'new user')
this is a bad idea, and its not transaction safe if you have simultaneous inserts

get the previous row of a query

Doing a select I get a row, and I would want to get the previous row in the table.
create table t1
id char(3),
dat datetime
);
id dat
a 2014-04-21
b 2014-10-01
c 2014-10-15
select id from t1 where id='c'
and wanted to find the previous row i.e. b
What about:
DECLARE #id CHAR(3)
SET #id = 'c'
select
id
from
table
where
id=#id
OR
id = (SELECT MAX(id) FROM table WHERE id < #id)
Get all the rows where id is not greater than the specified value, then limit the results to just the top two rows:
SELECT TOP (2)
id
FROM
t1
WHERE
id <= 'c'
ORDER BY
id DESC
;

Return multiple result sets AND use result set 1 to filter result set 2

/* result 1 */
select Id, Name
from Items
/* result 2 */
select Id,
Alias
from ItemAliases
where Id in (
select Id, Name
from table abc
)
We use SQL Server 2008.
Using the above example, it should be pretty straightforward what I'm trying to do.
I need to return the results of query 1... and return the results of query 2.
Query 2 however, needs to filter to only include records from result 1.
Here is my attempt to show what I would like to end up with.
VAR A = (
select Id, Name
from Items
)
/* result 1 */
select A.*
/* result 2 */
select Id,
Alias
from ItemAliases
where Id in ( A.Id )
I think you just want to store Result1 and use it to compose Result2:
declare #Result1 table (Id int primary key, Name varchar(100));
insert into #Result1
-- store Result1
select Id, Name
from Items
--return Result1
select Id, Name
from #Result1;
--return Result2 using stored Result1
select Id,
Alias
from ItemAliases
where Id in (select Id from #Result1);
--Result 1
SELECT ID, Name
FROM Items
[You WHERE Clause here if any]
--Result 2
SELECT Id, Alias
FROM ItemAliases ia
INNER JOIN Items i ON ia.ID = i.ID
OR
--Using temporay in memory table
DECLARE #abc AS TABLE (
ID AS Int,
Name AS varchar(25)
)
SELECT ID, Name
INTO #abc
FROM Items
[You WHERE Clause here if any]
--Result 1
SELECT * FROM #abc
--Result 2
SELECT Id, Alias
FROM ItemAliases ia
INNER JOIN #abc i ON ia.ID = i.ID