0

There have been quite a number of question regarding ranges in sql however i cant find anything that resembles my use case (most refer to a Tally or numbers table or procedures, something im trying to avoid seeing the elegance in the link below which uses neither)

Im trying to generate numbers between 1 and x, x coming for another table whilst excluding certain numbers from yet another table. (although there is a link between the 2, see the join)

Based on the answer for generating a range of numbers located at https://stackoverflow.com/a/64151448/1161646, im trying to do the following (approximation of the problem not the actual query):

select number
from excluding_numbers as exclusions join ranges range on range.id = exclusions.ranges_id,
     (Select 0 + ROW_NUMBER() over (order by (Select null)) as number
      from string_split(replicate(' ', range.limit - 1), ' ')) as numbers
where exclusions.number != number
  and range.id = 1

The problem is the range.limit in the inner query 'numbers'

Example data:

ranges

id limit
1 120000
2 10
3 10000000000

excluding_numbers

id number ranges_id
1 1 2
2 2 2
3 50000 3

This is saying from the range of 1 to 10 (ranges id 2) exclude number 1 and 2, from ranges 1 to 10000000000 (ranges id 3) only exclude number 50000.

Sem
  • 146
  • 9

1 Answers1

0

Assuming I understand correctly, and that your numbers are 1+, you could use a Tally table to do this. An inline method would be one method:

WITH N AS(
    SELECT N
    FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
    SELECT TOP (SELECT x FROM dbo.YourTable) --Table with the number of rows you want. I assume this has one row
            ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
    FROM N N1, N N2, N N3, N N4, N N5, N N6, N N7) --Up to 10,000,000 rows
SELECT T.I
FROM Tally T
WHERE NOT EXISTS (SELECT 1
                    FROM dbo.ExcludeTable ET--Table with numbers to exclude
                    WHERE ET.Number = T.I);

You could, however, create an inline table function for your tally, and then reference that instead:

CREATE FUNCTION [dbo].[Tally] (@LastNumber bigint, @Zero bit) 
RETURNS table
AS RETURN
    WITH N AS(
        SELECT N
        FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
    Tally AS(
        SELECT 0 AS I
        WHERE @Zero = 0
          AND @LastNumber IS NOT NULL --Return 0 rows if NULL
        UNION ALL
        SELECT TOP (ISNULL(@LastNumber,0)) --Return 0 rows if NULL
               ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
        FROM N N1, N N2, N N3, N N4, N N5, N N6, N N7) --Up to 10,000,000 rows
    SELECT I
    FROM Tally T;
GO

SELECT T.I
FROM dbo.YourTable YT --Table with the number of rows you want. I assume this has one row
     CROSS APPLY dbo.Tally(YT.x,1) T
WHERE NOT EXISTS (SELECT 1
                    FROM dbo.ExcludeTable ET--Table with numbers to exclude
                    WHERE ET.Number = T.I);
Thom A
  • 88,727
  • 11
  • 45
  • 75
  • I have updated my initial question to not use a tally/numbers table. The no procedures (or functions) was already in the original prerequisite – Sem Apr 13 '22 at 12:59
  • 1
    @Sem there is an inline version in there for you, that doesn't use a function. – Thom A Apr 13 '22 at 13:00
  • Your inline version mentions: --Table with the number of rows you want. I assume this has one row. However this is not the case, there are numerous lines each with their own limit (and joining exclusions) In essence this was my problem: i refered to this field (range.limit) in the inner query 'numbers' which obviously fails – Sem Apr 13 '22 at 13:03
  • Then alter the queries to reflect that, @Sem . I can't work on information i don't have; you gave no sample data so *you* need to fill in the blanks you left. Put a `WHERE` in the subquery in the `TOP` and add further criteria to the `WHERE` for the exclusion table. – Thom A Apr 13 '22 at 13:04
  • I have added some example data to my question – Sem Apr 13 '22 at 13:17
  • So, presumably, you just need to add `WHERE id = 2`/`WHERE range_id = 2` (with properly qualified columns) respectively, @Sem . And correct any opther object references (such as column and table names to be your objects). – Thom A Apr 13 '22 at 13:19