(Рекурсивный) SQL-запрос вместо циклов - PullRequest
4 голосов
/ 07 сентября 2011

Я ищу запрос, чтобы получить следующий вывод:

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», ... и нужно знатьэтому идентификатору присвоен этот номер.

Запрос, который я буду использовать для получения результата:

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

Ответы [ 3 ]

4 голосов
/ 07 сентября 2011

Вы можете создать таблицу вспомогательных номеров

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)
3 голосов
/ 07 сентября 2011

Запрос, который я буду использовать для получения результата:

SELECT Id, Number FROM MyView WHERE Номер 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в любом месте, потому что мне неясно, какие значения возможны в этом столбце, кроме null и number+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 голосов
/ 07 сентября 2011

Я знаю, что вы используете 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...