2011-09-07 61 views
4

我正在寻找一个查询得到以下的输出:(递归)的SQL查询,而不是循环

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

表strucutre:

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

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

一种可能的解决方案是使用while循环。但那不是我喜欢的方式。而且速度很慢。而且没有办法改变数据结构。我们查询来自第三方供应商的数据。


在应用程序的网站我有数字的(很小)列表,如“241113”,“241000”,...和需要知道哪个ID这个数字被分配到。

查询我会用得到的结果是:

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

“FROM”始终是“RIGHT(数字,3)+1还是NULL”? –

+0

你是否有必要生成这个范围的数字?这些数字的消费者是什么? – Reinderien

+0

恕我直言,这应该被标记间隙和岛屿。事实上,有一个递归查询可能。 – wildplasser

回答

3

我会用得到的结果的查询是:

SELECT识别码,从MyView的数,其中数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') 
+0

我认为丹尼希望根据示例表中的范围信息生成*这些值,例如241113。 – Reinderien

+0

@Reinderien - 增加了一个版本,其中的数字*生成*。 –

+0

很好 - 我以前没有碰过tsql;我想在pgsql – Reinderien

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

我知道你正在使用T-SQL,但出于好奇,我想看看我怎么可能共同解决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;