(递归)SQL查询而不是循环

时间:2011-09-07 12:05:26

标签: sql tsql

我正在寻找一个查询来获得以下输出:

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

表格结构:

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

没有from / to范围的行必须返回该数字。其他人必须返回后跟SUBSTRING(Number, 1, 3) + <from/to range>

的数字

一种可能的解决方案是使用while循环。但那不是我喜欢的方式。它很慢。而且无法改变数据结构。我们从第三方供应商处查询数据。


在应用程序网站上,我有一个(非常小的)数字列表,例如'241113','241000',......并且需要知道该号码被分配给哪个ID。

我将用于获得结果的查询是:

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

3 个答案:

答案 0 :(得分:4)

你可以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;

然后生成您想要的结果

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

答案 1 :(得分:3)

  

我将用于获得结果的查询是:

     

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

这是您可以编写该查询的方式。无需生成数字。

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)

将您查找的数字放在表变量中的版本。

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)

我没有在任何地方使用From因为我不清楚除了nullnumber+1之外的那个列中可能的值。

在过滤出数字之前生成数字的版本。结果是一样的,我相信表现不太好。

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

答案 2 :(得分:0)

我知道你正在使用tsql,但出于好奇,我想知道如何在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;