create a matrix of subtractions in sql - sql-server

I have a table like this:
id_nbr t1 t2 t3 t4 t5 t6
1111 10 20 30 40 50 40
2222 40 10 50 20 70 90
And I would like to build a function that for each id_nbr will return the max substraction and the time diffrence for that substraction - for example:
for id_nbr 1111 the max is 40 from t1 to t5 so the function will return 40 and 4 (5-1) in diffent cells.
for id_nbr 2222 the max is 60 from t2 to t5 so the function will return 60 and 3 (5-2) in diffrennt cells.
Output:
exec find_max_and_time (1111)
max_diff time
40 4
Any help creating such kind of function?
the function suggested here in a query:

Here one possible solution via unpivot. The idea is to turn your six columns into rows, geth their rowindex which represents the time (according to your example) and then get min and max value of the corresponding columns. However, you should consider handling of duplicates: it might happen, that two columns have the same value and with this you can have multiple matching records for min and max.
DECLARE #t TABLE(
id_nbr INT
,t1 INT
,t2 INT
,t3 INT
,t4 INT
,t5 INT
,t6 INT
);
INSERT INTO #t VALUES (1111,10,20,30,40,50,40), (2222,40,10,50,20,70,90);
DECLARE #SearchID INT = 2222;
DECLARE #TimeIdxFrom INT = 1;
DECLARE #TimeIdxTo INT = 5;
WITH cteBase AS(
SELECT id_nbr, ROW_NUMBER() OVER (PARTITION BY id_nbr ORDER BY (SELECT 1)) rn, times
FROM (
SELECT id_nbr, t1, t2, t3, t4, t5, t6
FROM #t t) t
UNPIVOT(
times FOR cTime IN (t1, t2, t3, t4, t5, t6)
) u
),
cteMinMax AS(
SELECT id_nbr, MIN(times) MinTime, MAX(times) MaxTime
FROM cteBase
WHERE rn >= #TimeIdxFrom
AND rn <= #TimeIdxTo
GROUP BY id_nbr
)
SELECT --*,
cbMax.rn - cbMin.rn AS TimeSpan,
cbMax.times - cbMin.times AS ValDiff
FROM cteMinMax AS cmm
JOIN cteBase AS cbMin ON cmm.id_nbr = cbMin.id_nbr AND cbMin.times = cmm.MinTime
JOIN cteBase AS cbMax ON cmm.id_nbr = cbMax.id_nbr AND cbMax.times = cmm.MaxTime
WHERE cbMax.id_nbr = #SearchID;

NULL return all calculate values
CREATE TABLE dbo.T
(
id_nbr INT,
t1 INT,
t2 INT,
t3 INT,
t4 INT,
t5 INT,
t6 INT
)
GO
INSERT INTO dbo.T (id_nbr, t1, t2, t3, t4, t5, t6)
VALUES
( 1111, 10, 20, 30, 40, 50, 40 ),
(2222, 40, 10, 50, 20, 70, 90 )
GO
CREATE FUNCTION dbo.find_max_and_time(#Id INT)
RETURNS TABLE
AS RETURN
WITH Desce AS
(
SELECT TOP 1 WITH TIES a.id_nbr , b.*
FROM dbo.T a
CROSS APPLY
(
SELECT 1, a.T1 UNION ALL
SELECT 2, a.T2 UNION ALL
SELECT 3, a.T3 UNION ALL
SELECT 4, a.T4 UNION ALL
SELECT 5, a.T5 UNION ALL
SELECT 6, a.T6
) as b(N,Val)
WHERE id_nbr = #Id OR #Id IS NULL
ORDER BY ROW_NUMBER() OVER(PARTITION BY id_nbr ORDER BY Val DESC)
), Asce AS
(
SELECT TOP 1 WITH TIES a.id_nbr,b.*
FROM dbo.T a
CROSS APPLY
(
SELECT 1, a.T1 UNION ALL
SELECT 2, a.T2 UNION ALL
SELECT 3, a.T3 UNION ALL
SELECT 4, a.T4 UNION ALL
SELECT 5, a.T5 UNION ALL
SELECT 6, a.T6
) as b(N,Val)
WHERE id_nbr = #Id OR #Id IS NULL
ORDER BY ROW_NUMBER() OVER(PARTITION BY id_nbr ORDER BY Val ASC)
)
SELECT
a.id_nbr,
a.Val - b.Val as Val,
a.N - b.N as N
FROM
Desce a
INNER JOIN
Asce b
ON a.id_nbr = b.id_nbr
GO
SELECT * FROM dbo.find_max_and_time (1111)
DROP TABLE dbo.T
DROP FUNCTION dbo.find_max_and_time

Related

Join tables but allow use of records once only

CREATE TABLE #A (UpperLimit NUMERIC(4))
CREATE TABLE #B (Id NUMERIC(4), Amount NUMERIC(4))
INSERT INTO #A VALUES
(1000), (2000), (3000)
INSERT INTO #B VALUES
(1, 3100),
(2, 1900),
(3, 1800),
(4, 1700),
(5, 900),
(6, 800)
Given these 2 tables, I want to join Table A to B ON B.Amount < A.UpperLimit but each record from Table B can only be used once, so the desired output would be:
I could easily do this by plopping Table B's records into a temp table, cursor over table A taking top record < UpperLimit and Deleting that record from the temp table or some other programmatic solution, but I'd like to avoid that and I'm pretty sure this could be done with a "normal" (recursive CTE? Partition?) query.
You could achieve your desired output using below recursive CTE
WITH
DATA AS
(
SELECT * FROM #A A1 INNER JOIN #B B1 ON A1.UpperLimit >= B1.Amount
),
MA AS
(
SELECT MIN(UpperLimit) AS MinLimit, MAX(UpperLimit) AS MaxLimit FROM #A
),
RESULT AS
(
-- Get the first record corresponding with maximum upper limit
SELECT *
FROM DATA D1
WHERE NOT EXISTS
(SELECT 1
FROM DATA D2
WHERE D2.UpperLimit = D1.UpperLimit AND D2.Amount > D1.Amount)
AND D1.UpperLimit = (SELECT MaxLimit FROM MA)
-- Recursive get remain record corresponding with other upper limit
UNION ALL
SELECT D1.*
FROM RESULT R1 INNER JOIN DATA D1
ON (R1.UpperLimit > D1.UpperLimit AND R1.Id != D1.Id)
WHERE D1.UpperLimit >= (SELECT MinLimit FROM MA)
AND NOT EXISTS
(SELECT 1
FROM DATA D2
WHERE D2.UpperLimit = D1.UpperLimit AND D2.Amount > D1.Amount AND D2.Id != R1.Id)
)
SELECT DISTINCT * FROM RESULT ORDER BY UpperLimit DESC;
Demo: https://dbfiddle.uk/Y-m0K6Mk
Might be a bit lengthy but hopefully clear enough.
with a as
(select -- order and number rows in table A in some way
row_number() over (order by UpperLimit) as RnA,
*
from #a),
b as
(select -- order and number rows in table B in the same way
row_number() over (order by Amount) as RnB,
*
from #b),
m as
(select -- get and number all possible pairs of values from both tables considering the restriction
row_number() over (order by a.UpperLimit desc, b.Amount desc) as RnM,
*
from a
join b on
b.Amount < a.UpperLimit),
r as
(select -- use recursion to get all possible combinations of the value pairs with metrics of interest for comparison
convert(varchar(max), RnA) as ListA,
convert(varchar(max), RnB) as ListB,
RnA,
RnB,
1 as CountB,
convert(int, Amount) as SumB
from m
where RnM = 1
union all
select
r.ListA + ' ' + convert(varchar(max), m.RnA),
r.ListB + ' ' + convert(varchar(max), m.RnB),
m.RnA,
m.RnB,
r.CountB + 1,
r.SumB + convert(int, m.Amount)
from m
join r on
m.RnA < r.RnA and
m.RnB < r.RnB),
e as
(select top(1) -- select combinations of interest using metrics
ListA,
ListB
from r
order by CountB desc, SumB desc),
ea as
(select -- turn id list into table for table A
ea.Rn,
ea.Value
from e
cross apply(select row_number() over (order by (select null)) as Rn, Value from string_split(e.ListA, ' ')) as ea),
eb as
(select -- turn id list into table for table B
eb.Rn,
eb.Value
from e
cross apply(select row_number() over (order by (select null)) as Rn, Value from string_split(e.ListB, ' ')) as eb)
select -- get output table with actual values from the original tables
a.UpperLimit,
b.Amount,
b.Id
from ea
join eb on
ea.Rn = eb.Rn
join a on
ea.Value = a.RnA
join b on
eb.Value = b.RnB;
You can use an APPLY with a TOP 1 for this. Each row in the outer table gets only one row from the APPLY.
SELECT
*
FROM #A a
OUTER APPLY (
SELECT TOP (1) *
FROM #B b
WHERE b.Amount < a.UpperLimit
) b;
To simulate an inner-join (rather than a left-join) use CROSS APPLY.
This query returns very close to desired outcome.
WITH CTE AS (SELECT B.*,
ROW_NUMBER() OVER (PARTITION BY B.Value ORDER BY B.Value DESC) AS RowNum
FROM #B B),
cc as (SELECT A.Limit, CTE.*
FROM #A A
LEFT JOIN CTE ON CTE.Value < A.Limit AND CTE.RowNum = 1),
cc2 as (select *, MAX(Value) OVER ( PARTITION BY cc.Limit) as l1 from cc)
select Limit, ID, Value
from cc2
where Value = l1
This query use 3 Common Table Expressions. First sort Table B with ROW_NUMBER() function and PARTITION BY clause, second one JOIN Table A with Table B with the condition given and the third one filters the record that is in Limit on Table A and use the Limit only once.

T-SQL: Need Top N to always return N rows, even if null or blank

T-SQL: Need Top N to always return N rows, even if null or blank
Typically, the command
Select Top 5 * FROM ourTable
will return up to 5 rows, but less, depending whether the rows exist.
I want to ensure that it always returns 5 rows, (or in general N rows).
What is the syntax to achieve this?
The idea is to sort of generalize the LINQ concept of "FirstOrDefault" to "First_N_OrDefault", but using TSQL not LINQ.
Clearly, the 'extra' rows would have null or empty columns.
This is for Microsoft SQL Server 2014 using SSMS 14.0.17
I want to use the "TOP" syntax, if at all possible, therefore it is different than the possible duplicate. Also, as noted below, this is possibly something that could be solved at a different layer in the system, but it would be nice to have for TSQL as well.
select top (5) c1, c2, c3 from (
select top (5) c1, c2, c3, 0 as priority from ourTable
union all
select c1, c2, c3, 1 from (values (null, null, null), (null, null, null), (null, null, null), (null, null, null), (null, null, null)) v (c1, c2, c3)
) t
order by priority
You can use another dummy table with rows to generate empty rows of your table with a not matching JOIN. So you don't have to repeat the columns and rows in the UNION ALL part:
SELECT TOP 5 * FROM (
SELECT 0 AS isDummy, * FROM table_name
-- WHERE column_name = value
UNION ALL
SELECT 1 AS isDummy, t1.* FROM table_name t1
RIGHT JOIN INFORMATION_SCHEMA.COLUMNS ON t1.id = -1000 -- not valid condition so t1 columns are empty.
) t2
ORDER BY isDummy ASC
In this case the INFORMATION_SCHEMA.COLUMNS table is used to generate the additional rows. You can choose any other table with rows. You can use a TOP N value up to the count of rows in the right table (here: INFORMATION_SCHEMA.COLUMNS).
You can also generate a table with many rows (like on a calendar table):
SELECT TOP 5 * FROM (
SELECT 0 isDummy, * FROM table_name
-- WHERE column_name = value
UNION ALL
SELECT 1 isDummy, t1.* FROM table_name t1 RIGHT JOIN (
SELECT * FROM
(SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0,
(SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1,
(SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2,
(SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3,
(SELECT 0 t4 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4
) t2 ON t1.id = -1000 -- not valid condition so t1 columns are empty.
)x
ORDER BY isDummy ASC
You can use a limited tally table together with a gaplessly generated row-number like here:
The SELECT is always the same. The only thing changing is the amount of rows in the mockup-table:
DECLARE #TopCount INT=5;
--Case 1: More then 5 rows in the table
DECLARE #tbl TABLE(ID INT IDENTITY,SomeValue VARCHAR(100));
INSERT INTO #tbl VALUES
('Value1'),('Value2'),('Value3'),('Value4'),('Value5'),('Value6'),('Value7');
WITH Tally(Nmbr) AS(SELECT TOP(#TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM #tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
--Case 2: Less than 5 rows in the table
DELETE FROM #tbl WHERE ID BETWEEN 2 AND 5;
WITH Tally(Nmbr) AS(SELECT TOP(#TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM #tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
--Case 3: Exactly one row in the table
DELETE FROM #tbl WHERE ID <> 6;
WITH Tally(Nmbr) AS(SELECT TOP(#TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM #tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
--Case 4: Table is empty
DELETE FROM #tbl;
WITH Tally(Nmbr) AS(SELECT TOP(#TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM #tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
This will return all rows from the source, but at least the specified count.
If you want to limit the set to exactly 5 rows (e.g. in "Case 1"), you can use SELECT TOP(#TopCount) * and place an appropriate ORDER BY. This would return the specified row count in any case.

T-SQL Group by multiple batches of 2

So the wonderful people here on stackoverflow helped me with a "find consecutive failures" type query. (Status =4 is a failure). I thought I had cracked the second part of my problem because my test case seems to work fine but whenever I run it on our test environment I get dodgy results, so I must be doing something wrong. The goal is to find X number of consecutive failures. So the below is set to find 2 consecutive failures. I'm using SQL Server 2008 R2
DECLARE #t TABLE (
[InstructionId] INT,
[InstructionDetailId] INT,
[Sequence] INT,
[Status] INT
)
INSERT INTO #t SELECT 222,111,1, 2
INSERT INTO #t SELECT 222,112,2,2
INSERT INTO #t SELECT 222,113,3,4
INSERT INTO #t SELECT 222,114,4,4
INSERT INTO #t SELECT 222,115,5,2
INSERT INTO #t SELECT 222,116,6,4
INSERT INTO #t SELECT 222,117,7,2
INSERT INTO #t SELECT 222,118,8,4
INSERT INTO #t SELECT 222,119,9,4
INSERT INTO #t SELECT 222,120,10,2
INSERT INTO #t SELECT 222,121,11,2
INSERT INTO #t SELECT 222,124,12,4
INSERT INTO #t SELECT 222,126,13,4
INSERT INTO #t SELECT 222,128,14,4
INSERT INTO #t SELECT 223,126,13,4
INSERT INTO #t SELECT 223,128,14,4
INSERT INTO #t SELECT 223,129,15,2
INSERT INTO #t SELECT 223,130,16,4
INSERT INTO #t SELECT 224,111,17,4
INSERT INTO #t SELECT 224,112,18,4
INSERT INTO #t SELECT 223,160,33,4
INSERT INTO #t SELECT 223,161,34,4
INSERT INTO #t SELECT 223,162,35,4
INSERT INTO #t SELECT 223,163,40,4
;with HardcoreCTE AS
(
select t.*,
t.[Sequence] - ROW_NUMBER() OVER(PARTITION BY t.instructionId ORDER BY
t.InstructionDetailId) AS ItemCount
from #t t outer apply
( select top (1) t1.*
from #t t1
where t1.InstructionId = t.InstructionId and
t1.Sequence < t.Sequence
order by t1.Sequence desc
) t1 outer apply
( select top (1) t2.*
from #t t2
where t2.InstructionId = t.InstructionId and
t2.Sequence > t.Sequence
order by t2.Sequence
) t2
where t.status = 4 and (t.status = t1.status or t.status = t2.status)
)
,
HardCoreCTE2
AS
(
select *, Count(1) OVER(PARTITION BY ItemCount) AS ItemCount2 from
HardcoreCTE
)
select * from HardCoreCTE2
where ItemCount2 =2
So the above works brilliants to find results where there are specifically only 2 consecutive failures with these
results:
Now from the above results the only ones it finds are the records where there are 2 consecutive failures but whenever I convert the above to the actual test environment tables it doesn't seem to work.
Test Env Results: As you can see for the "InstructionId" of 2518380 it brought back one record and the for "InstructionId" 2614351. It's meant to bring back sets of 2 records.
Test Env Query: (Pretty much identical)
;with InitialDataCTE
AS
(
SELECT Instruction.InstructionID,InstructionDetail.InstructionDetailID,
InstructionDetail.InstructionDetailStatusID AS [Status],
InstructionDetail.Sequence
FROM Instruction INNER JOIN
InstructionDetail ON Instruction.InstructionID =
InstructionDetail.InstructionID
where InstructionDetailStatusID =4
and InstructionDetail.PaymentDateOriginal between '2015-01-05'
AND '2018-09-08'
),
HardCoreCTE
AS
(
select t.*,
t.Sequence - ROW_NUMBER() OVER(PARTITION BY t.instructionId ORDER BY
t.InstructionDetailId) AS ItemCount
from InitialDataCTE t outer apply
( select top (1) t1.*
from InitialDataCTE t1
where t1.InstructionId = t.InstructionID and
t1.Sequence < t.Sequence
order by t1.Sequence desc
) t1 outer apply
( select top (1) t2.*
from InitialDataCTE t2
where t2.InstructionId = t.InstructionId and
t2.Sequence > t.Sequence
order by t2.Sequence
) t2
where t.Status = 4 and (t.Status = t1.Status or t.Status = t2.Status)
)
,
HardCoreCTE2
AS
(
select *, Count(1) OVER(PARTITION BY ItemCount) AS ItemCount2 from
HardCoreCTE
)
select * from HardCoreCTE2
where ItemCount2 =2
order by InstructionID, Sequence
Really appreciate if someone can tell me where I am going wrong, I've been messing around with variations of the Count(*) but nothing successful yet. Thanx alot
I came to the next query:
with
a as (
select *,
row_number() over(partition by InstructionId order by Sequence)-
row_number() over(partition by InstructionId, [Status] order by Sequence) g
from #t
),
b as (
select *,
count(*) over(partition by InstructionId, [Status], g) c
from a
where [Status] = 4
)
select *
from b
where c > 2
order by 1, 3;
For your test data, I got the following result:
InstructionId InstructionDetailId Sequence Status g c
222 224 312 4 6 3
222 226 413 4 6 3
222 228 514 4 6 3
223 161 84 4 2 3
223 162 95 4 2 3
223 163 140 4 2 3
You can test this query here.

SQL Server: How do I get the highest value not set of an int column?

Let's take an example. These are the rows of the table I want get the data:
The column I'm talking about is the reference one. The user can set this value on the web form, but the system I'm developing must suggest the lowest reference value still not used.
As you can see, the smallest value of this column is 35. I could just take the smaller reference and sum 1, but, in that case, the value 36 is already used. So, the value I want is 37.
Is there a way to do this without a loop verification? This table will grow so much.
This is for 2012+
DECLARE #Tbl TABLE (id int, reference int)
INSERT INTO #Tbl
( id, reference )
VALUES
(1, 49),
(2, 125),
(3, 35),
(4, 1345),
(5, 36),
(6, 37)
SELECT
MIN(A.reference) + 1 Result
FROM
(
SELECT
*,
LEAD(reference) OVER (ORDER BY reference) Tmp
FROM
#Tbl
) A
WHERE
A.reference - A.Tmp != -1
Result: 37
Here is yet another place where the tally table is going to prove invaluable. In fact it is so useful I keep a view on my system that looks like this.
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 cross join E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a cross join 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
Next of course we need some sample data and table to hold it.
create table #Something
(
id int identity
, reference int
, description varchar(10)
)
insert #Something (reference, description)
values (49, 'data1')
, (125, 'data2')
, (35, 'data3')
, (1345, 'data4')
, (36, 'data5')
, (7784, 'data6')
Now comes the magic of the tally table.
select top 1 t.N
from cteTally t
left join #Something s on t.N = s.reference
where t.N >= (select MIN(reference) from #Something)
and s.id is null
order by t.N
This is ugly, but should get the job done:
select
top 1 reference+1
from
[table]
where
reference+1 not in (select reference from [table])
order by reference
I used a table valued express to get the next value. I first left outer joined the table to itself (shifting the key in the join by +1). I then looked only at rows that had no corresponding match (b.ID is null). The minimum a.ReferenceID + 1 gives us the answer we are looking for.
create table MyTable
(
ID int identity,
Reference int,
Description varchar(20)
)
insert into MyTable values (10,'Data')
insert into MyTable values (11,'Data')
insert into MyTable values (12,'Data')
insert into MyTable values (15,'Data')
-- Find gap
;with Gaps as
(
select a.Reference+1 as 'GapID'
from MyTable a
left join MyTable b on a.Reference = b.Reference-1
where b.ID is null
)
select min(GapID) as 'NewReference'
from Gaps
NewReference
------------
13
I hope the code was clearer than my description.
CREATE TABLE #T(ID INT , REFERENCE INT, [DESCRIPTION] VARCHAR(50))
INSERT INTO #T
SELECT 1,49 , 'data1' UNION ALL
SELECT 2,125 , 'data2' UNION ALL
SELECT 3,35 , 'data3' UNION ALL
SELECT 4,1345, 'data4' UNION ALL
SELECT 5,36 , 'data5' UNION ALL
SELECT 6,7784, 'data6'
SELECT TOP 1 REFERENCE + 1
FROM #T T1
WHERE
NOT EXISTS
(
SELECT 1 FROM #T T2 WHERE T2.REFERENCE = T1.REFERENCE + 1
)
ORDER BY T1.REFERENCE
--- OR
SELECT MIN(REFERENCE) + 1
FROM #T T1
WHERE
NOT EXISTS
(
SELECT 1 FROM #T T2 WHERE T2.REFERENCE = T1.REFERENCE + 1
)
How about using a Tally table. The following illustrates the concept. It would be better to use a persisted numbers table as opposed to the cte however the code below illustrates the concept.
For further reading as to why you should use a persisted table, check out the following link: sql-auxiliary-table-of-numbers
DECLARE #START int = 1, #END int = 1000
CREATE TABLE #TEST(UsedValues INT)
INSERT INTO #TEST(UsedValues) VALUES
(1),(3),(5),(7),(9),(11),(13),(15),(17)
;With NumberSequence( Number ) as
(
Select #start as Number
union all
Select Number + 1
from NumberSequence
where Number < #end
)
SELECT MIN(Number)
FROM NumberSequence n
LEFT JOIN #TEST t
ON n.Number = t.UsedValues
WHERE UsedValues IS NULL
OPTION ( MAXRECURSION 1000 )
You could try using a descending order:
SELECT DISTINCT reference
FROM `Resultsados`
ORDER BY `reference` ASC;
As far as I know, there is no way to do this without a loop. To prevent multiple values from returning be sure to use DISTINCT.

Insert row for each integer between 0 and <value> without cursor

I have a source table with id and count.
id count
a 5
b 2
c 31
I need to populate a destination table with each integer up to the count for each id.
id value
a 1
a 2
a 3
a 4
a 5
b 1
b 2
c 1
c 2
etc...
My current solution is like so:
INSERT INTO destination (id,value)
source.id
sequence.number
FROM
(VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9)) AS sequence(number)
INNER JOIN
source ON sequence.number <= source.count
This solution has an upper limit and is plain lame. Is there anyway to replace the sequence with a set of all integers? Or another solution that does not use looping.
this should work:
WITH r AS (
SELECT id, count, 1 AS n FROM SourceTable
UNION ALL
SELECT id, count, n+1 FROM r WHERE n<count
)
SELECT id,n FROM r
order by id,n
OPTION (MAXRECURSION 0)
Unfortunately, there is not set of all integers in SQL Server. However, using a little trickery, you can easily generate such a set:
select N from (
select ROW_NUMBER() OVER (ORDER BY t1.object_id) AS N
from sys.all_objects t1, sys.all_objects t2
) AS numbers
where N between 1 and 1000000
will generate a set of all numbers from 1 through 1000000. If you need more than a few million numbers, add sys.all_objects to the cross join a third time.
You can find many examples in this page:
DECLARE #table TABLE (ID VARCHAR(1), counter INT)
INSERT INTO #table SELECT 'a', 5
INSERT INTO #table SELECT 'b', 3
INSERT INTO #table SELECT 'c', 31
;WITH cte (ID, counter) AS (
SELECT id, 1
FROM #table
UNION ALL
SELECT c.id, c.counter +1
FROM cte AS c
INNER JOIN #table AS t
ON t.id = c.id
WHERE c.counter + 1 <= t.counter
)
SELECT *
FROM cte
ORDER BY ID, Counter

Resources