开发者

(Recursive) SQL query instead of loops

I'm looking for a query to get the following output:

Id Number
-- ------
 1 241100
 2 241110
 2 241111
 2 241112
 2 241113
 2 241114
 2 241115

Table strucutre:

Id Number From To
-- ------ ---- ----
 1 241100 NULL NULL
 2 241110  111  115

Rows without a from/to range has to return the number. The other ones have to return the number followed by SUBSTRING(Number, 1, 3) + <from/to range>

One possible solution would be 开发者_如何学Cusing while-loops. But thats not the way I'd prefer. And it's quite slow. And there's no way to change the data structure. We query data from a third party supplier.


On application site I've a (very small) list of numbers such as '241113', '241000', ... and need to know to which id this number is assigned to.

The query I will use to get a result is:

SELECT Id, Number FROM MyView WHERE Number IN ('241113', '241000')


You can create an auxiliary numbers table

CREATE TABLE Numbers
(
N int primary key
)


  WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1),
        E02(N) AS (SELECT 1 FROM E00 a, E00 b),
        E04(N) AS (SELECT 1 FROM E02 a, E02 b),
        E08(N) AS (SELECT 1 FROM E04 a, E04 b),
        E16(N) AS (SELECT 1 FROM E08 a, E08 b),
        E32(N) AS (SELECT 1 FROM E16 a, E16 b),
   cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY N) FROM E32)

   INSERT INTO Numbers
   SELECT N FROM cteTally
  WHERE N <= 1000000;

then to generate your desired results

;WITH T (Id, Number, [From], [To]) AS
(
 SELECT 1, 241100, NULL, NULL UNION ALL
 SELECT 2, 241110,  111,  115
)

SELECT Id, Number + N-1  AS Number
FROM T
JOIN Numbers ON N <= 1 + ISNULL(1 + [To] - [From],0)


The query I will use to get a result is:

SELECT Id, Number FROM MyView WHERE Number IN ('241113', '241000')

This is how you can write that query instead. There is no need to generate the numbers.

declare @T table
(
  Id int,
  Number int,
  [From] int,
  [To] int
)

insert into @T values
(1, 241100, NULL, NULL),
(2, 241110,  111,  115)

select T.Id, V.Number
from @T as T
  inner join (values (241113), 
                     (241100)) as V(Number)
    on V.Number between T.Number and T.Number + isnull(T.[To], 0)

A version where you put the numbers you look for in a table variable instead.

declare @V table(Number int)
insert into @V values(241100)
insert into @V values(241113)

select T.Id, V.Number
from @T as T
  inner join @V as V
    on V.Number between T.Number and T.Number + isnull(T.[To], 0)

I have not used From anywhere because it is unclear to me what values is possible in that column other then null and number+1.

And a version where you generate the numbers before filtering them out. The result is the same and I believe performance is not as good.

;with C as
(
  select T.Id,
         T.Number
  from @T as T
  union all
  select T.Id,
         C.Number + 1
  from @T as T
    inner join C
      on C.Id = T.Id
  where stuff(C.Number, 1, 3, '') < T.[To]
)
select Id, Number
from C
where Number in ('241113', '241100')


I know that you're using tsql, but out of curiosity I wanted to see how I could hack together a solution in pgsql:

create schema arrays;
set search_path = 'arrays';

create table ranges
(
    "Id"     bigint primary key,
    "Number" int not null,
    "From"   int,
    "To"     int
);

insert into ranges("Id", "Number", "From", "To") values
    (1,  241100, null, null),
    (2,  241110,  111,  115),
    (3, 2411200, 1281, 1293);

create view ranges_gen as
select
    "Id",
    (row_number() over(partition by "Id") - 1 + "Bottom") as "Number"
from
(
    select
        "Id",
        coalesce(round("Number", -length("From"::text)) + "From", "Number") as "Bottom",
        unnest(array_fill(0, array[coalesce("To" - "From" + 1, 1)]))
    from ranges
) as ranges_duped;

select * from ranges_gen;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜