Insert Rows with Prefix and Incrementing Number - sql-server

I'd like to insert 300 rows where the Username consists of a prefix followed by an incrementing number from 001-300 (Or just 1-300).
For example: PRC001, PRC002, PRC003, PRC004
How would I do this in a single statement?
EDIT: I'm using SSMS 2016 and Microsoft Azure Database

Insert Into YourTable (SomeID)
Select Top 300 Format(Row_Number() Over (Order By Number),'PRC000') From master..spt_values
Another Option would be to create an ad-hoc tally table
;with cte0(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cteN(N) As (Select Row_Number() over (Order By (Select NULL)) From cte0 N1, cte0 N2, cte0 N3)
Select Top 300 Format(N,'PRC000') From cteN
You can use any table (which has more than 300 records) as well
Select Top 300 Format(Row_Number() Over (Order By (Select null)),'PRC000') From AnyLargerTable
The Insert would be

You want to generate those rows and insert them into a table?
Then you can try something like this:
DECLARE #i INTEGER
SET #i = 1
WHILE #i <= 300
BEGIN
PRINT 'PRC' + right('00' + cast(#i AS VARCHAR), 3)
/* add your insert here... */
SET #i = #i + 1
END
Output:
PRC001
PRC002
PRC003
PRC004
PRC005
PRC006
...
PRC298
PRC299
PRC300

Related

SQL select numbers - dynamically with incrementing value in each row

Is there a way to write a dynamic sql select statement (not query a table/column) that will return something like this
id
1
2
3
4
5
...
etc.
I need to write several generic queries that pull values 1 through X
(X will be pre-determined by a different query prior).
Say X is 5 the data will return 5 rows with 1 2 3 4 5
if X is 27 the data will return 1 2 3 ... 27 and so on.
on additional comment this is used in a third party sybase software and i am limited to the complexity of queries it seems creating and dropping tables doesn't seem to work
Sure you can use a tally table. http://www.sqlservercentral.com/articles/T-SQL/62867/
Or maybe all you need is ROW_NUMBER. https://learn.microsoft.com/en-us/sql/t-sql/functions/row-number-transact-sql
You could try a recursive CTE like so
declare #mx integer=4
;with test as (
select 1 as id
union all
select id+1
from test
where id<#mx
)
select * from test
27 selects:
DECLARE #Count int = 1
DECLARE #MaxCount int = 27 -- Whatever your incoming max is
WHILE #Count <= #MaxCount
BEGIN
SELECT #Count
SELECT #Count = #Count + 1
END
One select:
CREATE TABLE #TEMPNUMS (AUTONUM INT IDENTITY (1,1), GHOSTVAL VARCHAR(1))
DECLARE #Count int = 1
DECLARE #MaxCount int = 27 -- Whatever your incoming max is
WHILE #Count <= #MaxCount
BEGIN
INSERT INTO #TEMPNUMS (GHOSTVAL)
SELECT ''
SELECT #Count = #Count + 1
END
SELECT AUTONUM FROM #TEMPNUMS
DROP TABLE #TEMPNUMS
Have you tried to use CTE like this?
WITH OrderedRows (ROWN)
AS (
SELECT 1 AS ROWN
UNION ALL
SELECT r.ROWN + 1 AS ROWN
FROM OrderedRows r
WHERE r.ROWN < 100
)
SELECT *
FROM OrderedRows
Assuming your max number isn't ever going to be greater than 9999. If it is then you will need to restructure the query to add another layer.
DECLARE #MaxCount int = 27 -- Whatever your incoming max is
SELECT ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
where ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n between 1 and #MaxCount
ORDER BY 1
See this answer on another similar question: How to generate a range of numbers between two numbers?

How can I use T-SQL to find all positions that are not in a list of characters?

I need to find all the positions in a string that are not A, G, C, or T (this is genomic data). I have figured out a way to do this using a loop (see below) but honestly I am not sure if there is a 'smarter' way of doing this.
I am using SQL Server.
DECLARE #myTest varchar(max) = 'GGCGATXAATXCCC-GCCT'
DECLARE #pos int =1
DECLARE #table1 TABLE (position int, DiffValue varchar(1))
WHILE (#pos <= LEN(#myTest))
BEGIN
INSERT INTO #table1
SELECT
#pos,
CASE
WHEN SUBSTRING(#myTest, #pos, 1) NOT IN ('A','G','C','T')
THEN SUBSTRING(#myTest, #pos, 1)
END
WHERE
SUBSTRING(#myTest, #pos, 1) NOT IN ('A','G','C','T')
SELECT #pos= #pos + 1
END
SELECT * FROM #table1
Results in
position DiffValue
7 X
11 X
15 -
Just grab a copy of NGrams8K and your all set.
DECLARE #myTest varchar(max) = 'GGCGATXAATXCCC-GCCT'
SELECT Position, Token
FROM dbo.ngrams8K(#myTest, 1)
WHERE token NOT LIKE '[AGCT]';
Results:
Position Token
-------------------- --------
7 X
11 X
15 -
Perhaps with an ad-hoc tally table
DECLARE #myTest varchar(max) = 'GGCGATXAATXCCC-GCCT'
Select N
,S = substring(#myTest,N,1)
From (Select Top (Len(#myTest)) N=Row_Number() Over (Order By (Select NULL)) From master..spt_values n1,master..spt_values n2 ) A
Where substring(#myTest,N,1) not in ('A','G','C','T')
Returns
N S
7 X
11 X
15 -
Are you looking something like this as below:
DECLARE #myTest varchar(max) = 'GGCGATXAATXCCC-GCCTADASFDASDFXASDASFASDFXASDFASDXASDAS'
;with nums as (
select top(len(#myTest)) RowN = Row_number() over(order by (Select NULL))
from master..spt_values s1, master..spt_values s2
)
select RowN as Position, DiffValue = SUBSTRING(#myTest,RowN,1) from nums
where SUBSTRING(#myTest,RowN,1) not in ('A','G','C','T')

Convert strings to integers using PatIndex

I want to return integers from rather complex strings which combined unicode characters such as - and . with characters and integers.
I've come a long way in achieving this, but I still have troubles with some strings of a more complex structure. For instance:
DECLARE #Tabl as table
(
dats nvarchar(15)
)
INSERT INTO #Tabl VALUES
('103-P705hh'),
('115-xxx-44'),
('103-705.13'),
('525-hheef4')
select LEFT(SUBSTRING(REPLACE(REPLACE(dats, '.',''),'-',''), PATINDEX('%[0-9.-]%', REPLACE(REPLACE(dats, '.',''),'-','')), 8000),
PATINDEX('%[^0-9.-]%', SUBSTRING(REPLACE(REPLACE(dats, '.',''),'-',''), PATINDEX('%[0-9.-]%', REPLACE(REPLACE(dats, '.',''),'-','')), 8000) + 'X')-1)
from #tabl
Gives
Raw Input Actual return: Desired return:
103-P705hh 103 103705
115-xxx-44 115 11544
103-705.13 10370513 10370513
525-hheef4 525 5254
I had a topic regarding this yesterday to cover the case when multiple - or . are present, but as seen in the return this is actually taken care of now. However, expanding the databases I work with I encountered much more complex string such as those I presented here.
Does anyone have any idea what to do when characters and integers are "mixed up" in the string?
Regards,
Cenderze
I have seen loads of solutions that use a scalar udf with a loop, but I don't like either of these things, so throwing my hat into the ring with a different approach.
With the help of a numbers table you can deconstruct each value into its individual characters, remove non-numeric characters, then reconstruct it using FOR XML to concatenate rows, e.g.
WITH Numbers (Number) AS
( SELECT ROW_NUMBER() OVER(ORDER BY N1.N)
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N1 (N) -- 100
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N2 (N) -- 100
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N3 (N) -- 1,000
--CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N4 (N) -- 10,000
--CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N5 (N) -- 100,000
--COMMENT OR UNCOMMENT ROWS AS NECESSARY DEPENDING ON YOU MAX STRING LENGTH
)
SELECT t.dats,
Stripped = x.data.value('.', 'INT')
FROM #tabl AS t
CROSS APPLY
( SELECT SUBSTRING(t.dats, n.Number, 1)
FROM Numbers n
WHERE n.Number <= LEN(t.dats)
AND SUBSTRING(t.dats, n.Number, 1) LIKE '[0-9]'
ORDER BY n.Number
FOR XML PATH(''), TYPE
) x (data);
Gives:
dats Stripped
----------------------
103-P705hh 103705
115-xxx-44 11544
103-705.13 10370513
525-hheef4 5254
I haven't done any testing so it could be that the added overhead of expanding each string into individual characters and reconstructing it is actually a lot more overhead than than a UDF with a loop.
I decided to bench mark this
1. Set up functions
CREATE FUNCTION dbo.ExtractNumeric_TVF (#Input VARCHAR(8000))
RETURNS TABLE
AS
RETURN
( WITH Numbers (Number) AS
( SELECT TOP (LEN(#Input)) ROW_NUMBER() OVER(ORDER BY N1.N)
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N1 (N) -- 100
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N2 (N) -- 100
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N3 (N) -- 1,000
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N4 (N) -- 10,000
)
SELECT Stripped = x.data.value('.', 'VARCHAR(MAX)')
FROM ( SELECT SUBSTRING(#Input, n.Number, 1)
FROM Numbers n
WHERE n.Number <= LEN(#Input)
AND SUBSTRING(#Input, n.Number, 1) LIKE '[0-9]'
ORDER BY n.Number
FOR XML PATH(''), TYPE
) x (data)
);
GO
create function dbo.ExtractNumeric_UDF(#s varchar(8000))
returns varchar(8000)
as
begin
declare #out varchar(max) = ''
declare #c char(1)
while len(#s) > 0 begin
set #c = left(#s,1)
if #c like '[0123456789]' set #out += #c
set #s = substring(#s, 2, len(#s) -1)
end
return #out
end
GO
2. Create first set of sample data and log table
CREATE TABLE dbo.T (Value VARCHAR(8000) NOT NULL);
INSERT dbo.T (Value)
SELECT TOP 1000 LEFT(NEWID(), CEILING(RAND(CHECKSUM(NEWID())) * 36))
FROM sys.all_objects a
CROSS JOIN sys.all_objects b;
CREATE TABLE dbo.TestLog (Fx VARCHAR(255), NumberOfRows INT, TimeStart DATETIME2(7), TimeEnd DATETIME2(7))
3. Run Tests
GO
DECLARE #T TABLE (Val VARCHAR(8000));
INSERT dbo.TestLog (fx, NumberOfRows, TimeStart)
VALUES ('dbo.ExtractNumeric_UDF', 1000, SYSDATETIME());
INSERT #T (Val)
SELECT dbo.ExtractNumeric_UDF(Value)
FROM dbo.T;
UPDATE dbo.TestLog
SET TimeEnd = SYSDATETIME()
WHERE TimeEnd IS NULL;
GO 100
DECLARE #T TABLE (Val VARCHAR(8000));
INSERT dbo.TestLog (fx, NumberOfRows, TimeStart)
VALUES ('dbo.ExtractNumeric_TVF', 1000, SYSDATETIME());
INSERT #T (Val)
SELECT f.Stripped
FROM dbo.T
CROSS APPLY dbo.ExtractNumeric_TVF(Value) f;
UPDATE dbo.TestLog
SET TimeEnd = SYSDATETIME()
WHERE TimeEnd IS NULL;
GO 100
4. Get Results
SELECT Fx,
NumberOfRows,
RunTime = AVG(DATEDIFF(MILLISECOND, TimeStart, TimeEnd))
FROM dbo.TestLog
GROUP BY fx, NumberOfRows;
I did the following (using just NEWID() so only a maximum of 36 characters) over 1,000 and 10,000 rows, the results were:
Fx NumberOfRows RunTime
--------------------------------------------------------
dbo.ExtractNumeric_TVF 1000 31
dbo.ExtractNumeric_UDF 1000 56
dbo.ExtractNumeric_TVF 10000 280
dbo.ExtractNumeric_UDF 10000 510
So the TVF coming in at just under half the time of the UDF.
I wanted to test edge cases so put 1,000 rows of longer strings (5,400 characters)
TRUNCATE TABLE dbo.T;
INSERT dbo.T (Value)
SELECT TOP 1000
REPLICATE(CONCAT(NEWID(), NEWID(), NEWID(), NEWID(), NEWID()), 30)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b;
And this is where the TVF came into its own, running over 5x faster:
Fx NumberOfRows RunTime
------------------------------------------------
dbo.ExtractNumeric_TVF 1000 2485
dbo.ExtractNumeric_UDF 1000 12955
I also really don't like the looping solutions so I decided to try my hand at one. This is using a predefined tally table but is quite similar to others posted here already.
This is my tally table. I keep this as a view on my system.
create View [dbo].[cteTally] as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
GO
Because I don't like looping I decided to use the table valued function approach which let me reuse this functionality in other queries with little to no effort. Here is one way to write such a function.
create function GetOnlyNumbers
(
#SearchVal varchar(8000)
) returns table as return
with MyValues as
(
select substring(#SearchVal, N, 1) as number
, t.N
from cteTally t
where N <= len(#SearchVal)
and substring(#SearchVal, N, 1) like '[0-9]'
)
select distinct NumValue = STUFF((select number + ''
from MyValues mv2
order by mv2.N
for xml path('')), 1, 0, '')
from MyValues mv
That looks good but the proof is in the pudding. Let's take this out with our sample data and kick the tires a few times.
DECLARE #Tabl as table
(
dats nvarchar(15)
)
INSERT INTO #Tabl VALUES
('103-P705hh'),
('115-xxx-44'),
('103-705.13'),
('525-hheef4')
select *
from #Tabl t
cross apply dbo.GetOnlyNumbers(t.dats) x
Sure looks nice and tidy. I tested against several of the other solutions posted here and without going into deep testing this appears to be significantly faster than the other approaches posted at this time.
DECLARE #Tabl as table
(
ID INT,
dats nvarchar(15)
)
INSERT INTO #Tabl VALUES
(1, '103-P705hh'),
(2, '115-xxx-44'),
(3, '103-705.13'),
(4, '525-hheef4')
SELECT T.ID, t.dats
,(
SELECT SUBSTRING(tt.dats,V.number,1)
FROM #Tabl tt
JOIN master.dbo.spt_values V ON V.type='P' AND V.number BETWEEN 1 AND LEN(tt.dats)
WHERE tt.ID=T.ID AND SUBSTRING(TT.dats,V.number,1) LIKE '[0-9]'
ORDER BY V.number
FOR XML PATH('')
) S
FROM #Tabl t
ORDER BY T.ID;
Can you use a udf ? If so, try this
create alter function numerals(#s varchar(max))
returns varchar(max)
as
begin
declare #out varchar(max) = ''
declare #c char(1)
while len(#s) > 0 begin
set #c = left(#s,1)
if #c like '[0123456789]' set #out += #c
set #s = substring(#s, 2, len(#s) -1)
end
return #out
end
to use it on your temp table...
select dbo.numerals(dats) from #Tabl
another solution, that does not use a UDF, but will work only if your table has a primary key, uses a recursive CTE. It is:
DECLARE #Tabl as table
(pk int identity not null, -- <=== added a primary key
dats nvarchar(max) )
INSERT INTO #Tabl VALUES
('103-P705hh'),
('115-xxx-44'),
('103-705.13'),
('525-hheef4');
with newVals(pk, pos, newD) as
(select pk, 1,
case when left(Dats,1) like '[0123456789]'
then left(Dats,1) else '' end
from #tabl
Union All
Select t.pk, pos + 1, n.newD +
case when substring(dats, pos+1, 1) like '[0123456789]'
then substring(dats, pos+1, 1) else '' end
from #tabl t join newVals n on n.pk = t.pk
where pos+1 <= len(dats) )
Select newD from newVals x
where pos = (Select Max(pos)
from newVals
where pk = x.pk)

SQL SERVER INSERT STATEMENT WITH LOOP Needs Optimized

I have SQL Statement running in SQL server 2014.
I have 3 columns: id, text1, text2
Inserting records in text1, text2
text1 is nvarchar
text2 is varchar
so far inserted 1.2 million rows in about 3.5 hrs
trying to insert 3 million need help in reduce time for insert
CODE:
DECLARE #i as int
SET #i = 0
WHILE #i < 3000000
BEGIN
SET #i = #i + 1
insert into test (text1 , text2)
values(N'你好','VJ'+ cast(#i as varchar(20)))
END
Here is another way to do it ... It finish pretty fast ... less then 5 sec on my SQL server
if object_id('tempdb..#Numbers') is not null drop table #Numbers
create table #Numbers (Num int)
insert into #Numbers (Num)
SELECT TOP (3000000) n = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
OPTION (MAXDOP 1);
if object_id('tempdb..#test') is not null drop table #test
create table #test (text1 nvarchar(50), text2 nvarchar(50))
insert into #test (text1, text2)
select N'你好' [text1], 'VJ' + cast(Num as nvarchar) [text2] from #Numbers
You can try like this
WITH CTE_TEST
AS
(
SELECT N'你好' AS CODE,'VJ'+ cast(1 as varchar(20)) NAME, 1 AS VCOUNT
UNION ALL
SELECT N'你好' ,'VJ'+ cast(VCOUNT+1 as varchar(20)) NAME, VCOUNT+1 AS VCOUNT
FROM CTE_TEST
WHERE VCOUNT+1 < 3000000
)
INSERT INTO test (text1 , text2)
SELECT CODE,NAME FROM CTE_TEST
OPTION (MAXRECURSION 0)
You can use your additional logic inside the CTE, and simply insert the result set to actual table. Here the insert statement is outside the loop (1 insert statement with 3000000 records) , so it will faster than inserting the record inside a loop 3000000 times (3000000 insert statements with 1 record each)
By default the MAXRECURSION is 100 to avoid infinite looping, here you need to override this (but it is not a good practice).
An alternative that does not use recursive CTE is to use a known table with many enough records, so that you can iterate against it:
-- generation parameters
declare #batchCount INT = 100000
declare #totalCount INT = 30000000
declare #loopCount INT = #totalCount / #batchCount
DECLARE #i as int = 0
-- loops are slow, but here we have only a few
WHILE (#i < #loopCount)
BEGIN
-- insert can be put just here to actually perform the insert
-- ROW_NUMBER gives us the numbering, but order does not matter, so using SELECT 1
select TOP (#batchCount) N'你好','VJ'+ cast(#i * #batchCount + ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as varchar(20))
from sys.messages
SET #i = #i + 1
END
sys.messages is a pretty large table (at least 200K records), so it can safely be used for batches of 100K.
Time using recursive CTE: 51s
Time using above solution: 28s
(tested on a SQL Server 2014 Express instance, SELECT only)

How to make this sql query

I have 2 SQL Server tables with the following structure
Turns-time
cod_turn (PrimaryKey)
time (datetime)
Taken turns
cod_taken_turn (Primary Key)
cod_turn
...
and several other fields which are irrelevant to the problem. I cant alter the table structures because the app was made by someone else.
given a numeric variable parameter, which we will assume to be "3" for this example, and a given time, I need to create a query which looking from that time on, it looks the first 3 consecutive records by time which are not marked as "taken". For example:
For example, for these turns, starting by the time of "8:00" chosen by the user
8:00 (not taken)
9:00 (not taken)
10:00 (taken)
11:00 (not taken)
12:00 (not taken)
13:00 (not taken)
14:00 (taken)
The query it would have to list
11:00
12:00
13:00
I cant figure out how to make the query in pure sql, if possible.
with a cursor
declare #GivenTime datetime,
#GivenSequence int;
select #GivenTime = cast('08:00' as datetime),
#GivenSequence = 3;
declare #sequence int,
#code_turn int,
#time datetime,
#taked int,
#firstTimeInSequence datetime;
set #sequence = 0;
declare turnCursor cursor FAST_FORWARD for
select turn.cod_turn, turn.[time], taken.cod_taken_turn
from [Turns-time] as turn
left join [Taken turns] as taken on turn.cod_turn = taken.cod_turn
where turn.[time] >= #GivenTime
order by turn.[time] asc;
open turnCursor;
fetch next from turnCursor into #code_turn, #time, #taked;
while ##fetch_status = 0 AND #sequence < #GivenSequence
begin
if #taked IS NULL
select #firstTimeInSequence = coalesce(#firstTimeInSequence, #time)
,#sequence = #sequence + 1;
else
select #sequence = 0,
#firstTimeInSequence = null;
fetch next from turnCursor into #code_turn, #time, #taked;
end
close turnCursor;
deallocate turnCursor;
if #sequence = #GivenSequence
select top (#GivenSequence) * from [Turns-time] where [time] >= #firstTimeInSequence
order by [time] asc
WITH Base AS (
SELECT *,
CASE WHEN EXISTS(
SELECT *
FROM Taken_turns taken
WHERE taken.cod_turn = turns.cod_turn) THEN 1 ELSE 0 END AS taken
FROM [Turns-time] turns)
, RecursiveCTE As (
SELECT TOP 1 cod_turn, [time], taken AS run, 0 AS grp
FROM Base
WHERE [time] >= #start_time
ORDER BY [time]
UNION ALL
SELECT R.cod_turn, R.[time], R.run, R.grp
FROM (
SELECT T.*,
CASE WHEN T.taken = 0 THEN 0 ELSE run+1 END AS run,
CASE WHEN T.taken = 0 THEN grp + 1 ELSE grp END AS grp,
rn = ROW_NUMBER() OVER (ORDER BY T.[time])
FROM Base T
JOIN RecursiveCTE R
ON R.[time] < T.[time]
) R
WHERE R.rn = 1 AND run < #run_length
), T AS(
SELECT *,
MAX(grp) OVER () AS FinalGroup,
COUNT(*) OVER (PARTITION BY grp) AS group_size
FROM RecursiveCTE
)
SELECT cod_turn,time
FROM T
WHERE grp=FinalGroup AND group_size=#run_length
I think there is not a simple way to achieve this.
But probably there are many complex ways :). This is an approach that should work in Transact-SQL:
CREATE TABLE #CONSECUTIVE_TURNS (id int identity, time datetime, consecutive int)
INSERT INTO #CONSECUTIVE_TURNS (time, consecutive, 0)
SELECT cod_turn
, time
, 0
FROM Turns-time
ORDER BY time
DECLARE #i int
#n int
SET #i = 0
SET #n = 3 -- Number of consecutive not taken records
while (#i < #n) begin
UPDATE #CONSECUTIVE_TURNS
SET consecutive = consecutive + 1
WHERE not exists (SELECT 1
FROM Taken-turns
WHERE id = cod_turn + #i
)
SET #i = #i + 1
end
DECLARE #firstElement int
SELECT #firstElement = min(id)
FROM #CONSECUTIVE_TURNS
WHERE consecutive >= #n
SELECT *
FROM #CONSECUTIVE_TURNS
WHERE id between #firstElement
and #firstElement + #n - 1
This is untested but I think it will work.
Pure SQL
SELECT TOP 3 time FROM [turns-time] WHERE time >= (
-- get first result of the 3 consecutive results
SELECT TOP 1 time AS first_result
FROM [turns-time] tt
-- start from given time, which is 8:00 in this case
WHERE time >= '08:00'
-- turn is not taken
AND cod_turn NOT IN (SELECT cod_turn FROM taken_turns)
-- 3 consecutive turns from current turn are not taken
AND (
SELECT COUNT(*) FROM
(
SELECT TOP 3 cod_turn AS selected_turn FROM [turns-time] tt2 WHERE tt2.time >= tt.time
GROUP BY cod_turn ORDER BY tt2.time
) AS temp
WHERE selected_turn NOT IN (SELECT cod_turn FROM taken_turns)) = 3
) ORDER BY time
Note: I tested it on Postgresql (with some code modification), but not MS SQL Server. I'm not sure about performance compared to T-SQL.
Another set-based solution (tested):
DECLARE #Results TABLE
(
cod_turn INT NOT NULL
,[status] TINYINT NOT NULL
,RowNumber INT PRIMARY KEY
);
INSERT #Results (cod_turn, [status], RowNumber)
SELECT a.cod_turn
,CASE WHEN b.cod_turn IS NULL THEN 1 ELSE 0 END [status] --1=(not taken), 0=(taken)
,ROW_NUMBER() OVER(ORDER BY a.[time]) AS RowNumber
FROM [Turns-time] a
LEFT JOIN [Taken_turns] b ON a.cod_turn = b.cod_turn
WHERE a.[time] >= #Start;
--SELECT * FROM #Results r ORDER BY r.RowNumber;
SELECT *
FROM
(
SELECT TOP(1) ca.LastRowNumber
FROM #Results a
CROSS APPLY
(
SELECT SUM(c.status) CountNotTaken, MAX(c.RowNumber) LastRowNumber
FROM
(
SELECT TOP(#Len)
b.RowNumber, b.[status]
FROM #Results b
WHERE b.RowNumber <= a.RowNumber
ORDER BY b.RowNumber DESC
) c
) ca
WHERE ca.CountNotTaken = #Len
ORDER BY a.RowNumber ASC
) x INNER JOIN #Results y ON x.LastRowNumber - #Len + 1 <= y.RowNumber AND y.RowNumber <= x.LastRowNumber;

Resources