SQL select numbers - dynamically with incrementing value in each row - sql-server

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?

Related

Make 100 random numbers and add them to table as primary key and catch error if duplicate

I have a problem. I am trying to add 100 random primary keys and catch error if the values is duplicate then add counter that counts the amount of errors and at the end prints out how many values are in ID and how many duplicate number errors there were.
I have done this so far but I am really new to T-SQL and I am not sure if this is even close.
Biggest problem is that I don't know how to get the value number from within the WITH and insert the given number to the table.
DECLARE #TABLE TABLE (ID INT NOT NULL PRIMARY KEY)
DECLARE #I INT = 1
DECLARE #ERROR INT = 0
DECLARE #NUMBER INT
BEGIN TRY
WITH CTE_Numbers(number) AS
(
SELECT 1 AS number
UNION ALL
SELECT number + 1
FROM CTE_Numbers
WHERE number < 100
)
SELECT TOP 1 number
FROM CTE_Numbers
ORDER BY NEWID()
OPTION (MAXRECURSION 0)
BEGIN
WHILE #I <= 100
BEGIN
SET #NUMBER = (SELECT number FROM CTE_Numbers)
INSERT INTO #TABLE VALUES(#NUMBER)
SET #I = #I + 1
END
END
END TRY
BEGIN CATCH
SET #ERROR = #ERROR + 1
END CATCH
SELECT COUNT(ID) AS numbers, #ERROR AS errors
FROM #TABLE
I don't fully get what you're doing but this should get you started. #rows is how many rows you want, #high is the highest ID (between 1 and #high).
--==== Parameters
DECLARE
#rows INT = 10, -- Return this many rows
#high INT = 20; -- Number = 1 to #high
--==== Prep
IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t;
--==== Create and Populate #t with random numbers
WITH
e1(x) AS (SELECT 1 FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS x(x)),
t(N) AS (SELECT ABS(CHECKSUM(NEWID())%#high)+1 FROM e1 a, e1 b, e1 c, e1 d) -- up to 10K
SELECT TOP(#rows) t.N INTO #t FROM t;
--==== Counts
SELECT
UniqueIds = SUM(IIF(f.T=1, 1, 0)),
Duplicates = SUM(IIF(f.T=1, 0, 1))
FROM
(
SELECT t.N, COUNT(*)
FROM #t AS t
GROUP BY t.N
) AS f(N,T);
--==== Santiy Check
SELECT t.N, ttl = COUNT(*)
FROM #t AS t
GROUP BY t.N
Returns: (random)
UniqueIds Duplicates
----------- -----------
2 4
N ttl
----------- -----------
2 2
5 1
6 2
8 2
12 2
14 1

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')

Insert Rows with Prefix and Incrementing Number

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

Faster approach to enter rows in a time table in ms sql

I need to fill a time table to use it for joining the data in reporting services.
Generally I do this with this code:
TRUNCATE TABLE tqTimeTable
DECLARE #CNT int
DECLARE #DATE datetime
DECLARE #END int
SET #CNT = 1
SET #DATE = 25567 -- 01.01.1970
SET #END = 20000 -- + 20k days => years 2024
WHILE(#CNT < #END)
BEGIN
INSERT INTO tqTimeTable (Tag, Monat, Jahr)
VALUES (DATEADD(day,#CNT,#DATE), MONTH(DATEADD(day,#CNT,#DATE)), YEAR(DATEADD(day,#CNT,#DATE)))
SET #CNT = #CNT + 1
END;
But this takes a while (on my test system around 2 minutes) so I hope someone had the same issue and solved it better then me.
As I fire this statement from a .NET connection I need a faster solution or if there isn't one to raise the timeout of my connection.
Simply adding
BEGIN TRAN
WHILE(#CNT < #END)
BEGIN
INSERT INTO tqTimeTable (Tag, Monat, Jahr)
VALUES (DATEADD(day,#CNT,#DATE), MONTH(DATEADD(day,#CNT,#DATE)), YEAR(DATEADD(day,#CNT,#DATE)))
SET #CNT = #CNT + 1
END;
COMMIT
will speed it up as you are doing many individual commits (that all require the log to be written to disc). I would do a set based insert in a single statement though.
WITH E1(N) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
) -- 1*10^1 or 10 rows
, E2(N) AS (SELECT 1 FROM E1 a, E1 b) -- 1*10^2 or 100 rows
, E4(N) AS (SELECT 1 FROM E2 a, E2 b) -- 1*10^4 or 10,000 rows
, E8(N) AS (SELECT 1 FROM E4 a, E4 b) -- 1*10^8 or 100,000,000 rows
, NUMS(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) FROM E8)
INSERT INTO tqTimeTable
(Tag,
Monat,
Jahr)
SELECT DATEADD(day, N, #DATE),
MONTH(DATEADD(day, N, #DATE)),
YEAR(DATEADD(day, N, #DATE))
FROM NUMS
WHERE N <= 20000

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