powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Рекурсия
25 сообщений из 25, страница 1 из 1
Рекурсия
    #39752942
*Ann*
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добрый день.
Задачка такая:
Есть таблица адресов с полями id, adress, formalname, level, parent_id. Parent_id ссылается на id из этой же таблицы.
id adress formalname level parent_id
1 630123, Новосибирская обл, Новосибирск г, Петухова ул Петухова ул 33 2
2 630123, Новосибирская обл, Новосибирск г Новосибирск г 22 3
3 630123, Новосибирская обл Новосибирская обл 11 null

В итоге должна получиться таблица:
id adress Index 11 22 33
1 630123, Новосибирская обл, Новосибирск г, Петухова ул 630123 Новосбирская обл Новосбирск г Петухова ул

Индекса в поле adress может и не быть.
Получается нужно разбить адрес по уровням level, ссылаясь на родителя.

Тут простым запросом не обойдешься, наверное, нужно писать какую-то процедуру, чего я пока не делала. Подскажите, пожалуйста, в каком направлении нужно действовать, что почитать, с чего начать.
...
Рейтинг: 0 / 0
Рекурсия
    #39752947
*Ann*
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
в первом сообщении не понятно как по столбцам разбивается таблица, немного исправила.
Есть таблица адресов с полями id, adress, formalname, level, parent_id. Parent_id ссылается на id из этой же таблицы.
id | adress | formalname | level | parent_id
1 | 630123, Новосибирская обл, Новосибирск г, Петухова ул | Петухова ул | 33 | 2
2 | 630123, Новосибирская обл, Новосибирск г | Новосибирск г | 22 | 3
3 | 630123, Новосибирская обл | Новосибирская обл | 11 | null

В итоге должна получиться таблица:
id | adress | Index | 11 | 22 | 33
1 | 630123, Новосибирская обл, Новосибирск г, Петухова ул | 630123 | Новосбирская обл | Новосбирск г | Петухова ул
...
Рейтинг: 0 / 0
Рекурсия
    #39753083
Фотография Щукина Анна
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
*Ann*,

Для начала почитайте про рекурсивный CTE. Глядишь, и процедуру писать не понадобиться...
...
Рейтинг: 0 / 0
Рекурсия
    #39754296
*Ann*
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Почитала про рекурсивные cte, во всех статьях примеры с выводом дерева в столбик. А мне надо строкой, при этом каждый элемент поместить с столбец в зависимости от уровня level. При этом количество уровней может быть разное. Может быть например 1,2 3 уровень, а может быть 1,3,4 уровень, тогда столбец со вторым уровнем должен быть пустым. Есть какие-то примеры с выводом дерева в строку?
...
Рейтинг: 0 / 0
Рекурсия
    #39754303
Фотография Щукина Анна
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
*Ann*,

Возможно, вам нужно такое ...

Если же задача всё таки стоит в отдельные столбцы, а не просто одной строкой - то тут прямая дорога к динамическому SQL. А в вашем конкретном случае - сначала вертикальный сбор дерева посредством рекурсивного СТЕ, затем его разворот в строку при помощи динамического PIVOT-а
...
Рейтинг: 0 / 0
Рекурсия
    #39754318
PizzaPizza
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
*Ann*,

если я правильно понял

вы хотите из этого

id adress formalname levelparent_id1 630123 Новосибирская обл. Новосибирск г. Петухова ул Петухова ул 33 22 630123 Новосибирская обл. Новосибирск г Новосибирск г 22 33 630123 Новосибирская обл Новосибирская обл 11 null

собирать это

id adress index formalname1 formalname2 formalnameN1 630123 Новосибирская обл. Новосибирск г. Петухова ул 630123 Новосбирская обл Новосбирск г Петухова ул

хотя полный адрес у вас есть в первой же строке в поле adress, части адреса в полях formalname, и у вас нет нигде индекса как отдельный атрибут

Мне кажется тут нужны некоторые пререквизиты. Вам надо определиться с количеством уровней результирующей таблицы. SQL не может выдать вам строки с произвольным количеством атрибутов, поэтому вам надо понять сколько уровней разбиения адреса у вас есть в базе. Если это всегда четыре (индекс, область/регион, город, улица) то можно будет сделать обычный запрос. Если это более сложное динамическое деление, то вам надо понять по каким признакам вы будете размещать части вашего адреса, иначе у вас в разные колонки пойдут разные данные:

id adress index formalname1 formalname2 formalnameN1 630123 Новосибирская обл. Новосибирск г. Петухова ул 630123 Новосбирская обл Новосбирск г Петухова ул2 Московская обл. Москва г. Ленина ул Московская обл Москва г Ленина ул1 Татарстан. Агрызский район. Васильево пгт. Ленина ул Татарстан Агрызский районВасильево пгт Ленина ул

В любом случае, учитывая
*Ann*Индекса в поле adress может и не быть.
вам придется определиться с тем, как обрабатывать подобные случаи.
...
Рейтинг: 0 / 0
Рекурсия
    #39754361
*Ann*
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
PizzaPizza,

Уровней всего 9. Если родителя какого-то уровня нет, то ячейку надо пропустить. Уровень указан в поле level. Если нет индекса, то ячейка тоже должна остаться пустой.
...
Рейтинг: 0 / 0
Рекурсия
    #39754398
Руслан Дамирович
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
как-то так
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
IF OBJECT_ID( 'tempdb..#temp' ) IS NOT NULL DROP TABLE #temp
;
CREATE TABLE #temp ( [id] INT, [address] VARCHAR(500), [formalname] VARCHAR(100), [level] INT, [parent_id] INT )
;
INSERT INTO #temp
VALUES
( 1, '630123, Новосибирская обл, Новосибирск г, Петухова ул', 'Петухова ул', 33, 2 ),
( 2, '630123, Новосибирская обл, Новосибирск г', 'Новосибирск г', 22, 3 ),
( 3, '630123, Новосибирская обл', 'Новосибирская обл', 11, NULL )
;
WITH
cte AS (
  SELECT
    [base] = tt.[id],
    [lvl] = 1,
    tt.*
  FROM
    #temp tt
    LEFT JOIN #temp tt2 ON (
          tt2.[parent_id] = tt.[id] )
  WHERE
    tt2.[id] IS NULL
  UNION ALL
  SELECT
    [base] = cte.[base],
    [lvl] = cte.[lvl] + 1,
    tt.*
  FROM
    cte
    INNER JOIN #temp tt ON (
          tt.[id] = cte.[parent_id] )
)
SELECT
  [base],
  [address] = MAX( CASE WHEN [lvl] = 1 THEN [address] END ),
  [index]   = MAX( CASE WHEN [lvl] = 1 AND [address] LIKE '[0-9][0-9][0-9][0-9][0-9][0-9],%' THEN LEFT( [address], 6 ) END ),
  [formalname11] = MAX( CASE WHEN [level] = 11 THEN [formalname] END ),
  [formalname22] = MAX( CASE WHEN [level] = 22 THEN [formalname] END ),
  [formalname33] = MAX( CASE WHEN [level] = 33 THEN [formalname] END ),
  [formalname44] = MAX( CASE WHEN [level] = 44 THEN [formalname] END ),
  [formalname55] = MAX( CASE WHEN [level] = 55 THEN [formalname] END ),
  [formalname66] = MAX( CASE WHEN [level] = 66 THEN [formalname] END ),
  [formalname77] = MAX( CASE WHEN [level] = 77 THEN [formalname] END ),
  [formalname88] = MAX( CASE WHEN [level] = 88 THEN [formalname] END ),
  [formalname99] = MAX( CASE WHEN [level] = 99 THEN [formalname] END )
FROM
  cte
GROUP BY
  [base]
;

...
Рейтинг: 0 / 0
Рекурсия
    #39754713
PizzaPizza
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
*Ann*PizzaPizza,

Уровней всего 9. Если родителя какого-то уровня нет, то ячейку надо пропустить. Уровень указан в поле level. Если нет индекса, то ячейка тоже должна остаться пустой.

Если у вас всегда четко 9 уровней, то сделайте на 9ти джойнах например.
Выберете все записи первого уровня и к ним присоединяйте self join Id=parent_id и следующий уровень, а к нему уже по Id=parent_id-предыдущего уровня и так 9 раз. А потом отдельно поиск индекса по каким то правилам в подстроке и формируете запись.
...
Рейтинг: 0 / 0
Рекурсия
    #39754810
Руслан Дамирович
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
PizzaPizza*Ann*PizzaPizza,

Уровней всего 9. Если родителя какого-то уровня нет , то ячейку надо пропустить. Уровень указан в поле level. Если нет индекса, то ячейка тоже должна остаться пустой.

Если у вас всегда четко 9 уровней, то сделайте на 9ти джойнах например.
Выберете все записи первого уровня и к ним присоединяйте self join Id=parent_id и следующий уровень, а к нему уже по Id=parent_id-предыдущего уровня и так 9 раз. А потом отдельно поиск индекса по каким то правилам в подстроке и формируете запись.
И в каждом последующем ты будешь писать [id_parent] = COALESCE( tn.[id], tn-1.[id],... ) ?
Сумасошел? Пойди выспись!
...
Рейтинг: 0 / 0
Рекурсия
    #39756939
*Ann*
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Руслан Дамирович,

При запуске запроса выдаётся сообщение о том, что cte не существует. Мне кажется, ругается на второй select. У меня используется postgresql, но вряд ли это влияет.
...
Рейтинг: 0 / 0
Рекурсия
    #39756950
Фотография alexeyvg
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
*Ann*При запуске запроса выдаётся сообщение о том, что cte не существует. Мне кажется, ругается на второй select. У меня используется postgresql, но вряд ли это влияет.Думаю, у вас в коде ошибка.
...
Рейтинг: 0 / 0
Рекурсия
    #39760596
*Ann*
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Подскажите, пожалуйста, postgresql не поддерживает квадратные скобки? С ними скрипт не работает. Если их убирать, то тогда как обозначить lvl =1?
...
Рейтинг: 0 / 0
Рекурсия
    #39760599
Kopelly
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: sql
1.
1 as lvl
...
Рейтинг: 0 / 0
Рекурсия
    #39760638
*Ann*
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Убрала скобки, заменила названия полей и таблиц, в коде ничего не меняла, все равно выдается сообщение о том, что cte не существует (см. файл).
...
Рейтинг: 0 / 0
Рекурсия
    #39760664
Фотография StarikNavy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
*Ann*Подскажите, пожалуйста, postgresql
тут века не по postgres , а MS SQL
...
Рейтинг: 0 / 0
Рекурсия
    #39760687
Asic
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
*Ann*,
ошибка в cte, вы ее еще не создали, а после union all ее же и используете
...
Рейтинг: 0 / 0
Рекурсия
    #39760691
Asic
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Код: sql
1.
select cte.base, cte.lvl from cte 


откуда им взяться?
...
Рейтинг: 0 / 0
Рекурсия
    #39760721
*Ann*
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Как тогда правильно написать?
...
Рейтинг: 0 / 0
Рекурсия
    #39796069
*Ann*
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добрый день.
Подскажите, пожалуйста, все-таки как скрипт нужно исправить, чтобы он заработал
...
Рейтинг: 0 / 0
Рекурсия
    #39796119
andreymx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
*Ann*,

а какая у вас СУБД - имя, версия?
...
Рейтинг: 0 / 0
Рекурсия
    #39796173
*Ann*
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пишу на Postgressql
Если указать тип поля aolevel integer, то ошибка про несовпадение типов уже не выдается.
Не уверена, что (tt.aolevel = 1)::integer правильно записано, но ошибки уже нет.
Теперь выдается ошибка: function left(character varying, integer) does not exist для строки
MAX( CASE WHEN lvl = 1 AND adr_adm_ter LIKE '[0-9][0-9][0-9][0-9][0-9][0-9],%' THEN LEFT( adr_adm_ter, 6 ) END ) as index,
И ошибка "column "aolevel" does not exist" ругается на последний итоговый select.

Сам запрос:
WITH cte AS (
SELECT
tt.id as base,
(tt.aolevel = 1)::integer as lvl,
tt.adr_adm_ter,
tt.formalname
FROM
edw_prod.edw_ods.t_000148_ent_as_addrobj tt
LEFT JOIN edw_prod.edw_ods.t_000148_ent_as_addrobj tt1 ON (
tt1.parent_id = tt.id )
WHERE
tt1.parent_id IS NULL
UNION ALL
SELECT
tt3.id as base,
(tt3.aolevel + 1)::integer as lvl,
tt.adr_adm_ter,
tt.formalname
FROM
edw_prod.edw_ods.t_000148_ent_as_addrobj tt3
INNER JOIN edw_prod.edw_ods.t_000148_ent_as_addrobj tt ON (
tt.id = tt3.parent_id )
)
SELECT
base,
adr_adm_ter = MAX( CASE WHEN lvl = 1 THEN adr_adm_ter END ),
MAX( CASE WHEN lvl = 1 AND adr_adm_ter LIKE '[0-9][0-9][0-9][0-9][0-9][0-9],%' THEN LEFT( adr_adm_ter, 6 ) END ) as index,
MAX( CASE WHEN aolevel = 1 THEN formalname END ) as formalname1,
MAX( CASE WHEN aolevel = 2 THEN formalname END ) as formalname2,
MAX( CASE WHEN aolevel = 3 THEN formalname END ) as formalname3,
MAX( CASE WHEN aolevel = 4 THEN formalname END ) as formalname4,
MAX( CASE WHEN aolevel = 5 THEN formalname END ) as formalname5,
MAX( CASE WHEN aolevel = 6 THEN formalname END ) as formalname6,
MAX( CASE WHEN aolevel = 7 THEN formalname END ) as formalname7,
MAX( CASE WHEN aolevel = 8 THEN formalname END ) as formalname8,
MAX( CASE WHEN aolevel = 90 THEN formalname END ) as formalname90,
MAX( CASE WHEN aolevel = 91 THEN formalname END ) as formalname91
FROM
cte
GROUP BY
base
limit 1000
;
...
Рейтинг: 0 / 0
Рекурсия
    #39796183
Фотография court
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
*Ann*Пишу на Postgressql
Если указать тип поля aolevel integer, то ошибка про несовпадение типов уже не выдается.
Не уверена, что (tt.aolevel = 1)::integer правильно записано, но ошибки уже нет.
Теперь выдается ошибка: function left(character varying, integer) does not exist для строки
MAX( CASE WHEN lvl = 1 AND adr_adm_ter LIKE '[0-9][0-9][0-9][0-9][0-9][0-9],%' THEN LEFT( adr_adm_ter, 6 ) END ) as index,
И ошибка "column "aolevel" does not exist" ругается на последний итоговый select.

Сам запрос:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
WITH cte AS (
  SELECT
    tt.id as base,
    (tt.aolevel = 1)::integer as lvl,
    tt.adr_adm_ter,
	tt.formalname
  FROM
    edw_prod.edw_ods.t_000148_ent_as_addrobj tt
    LEFT JOIN edw_prod.edw_ods.t_000148_ent_as_addrobj tt1 ON (
          tt1.parent_id = tt.id )
  WHERE
    tt1.parent_id IS NULL
  UNION ALL
  SELECT
    tt3.id  as base,
    (tt3.aolevel + 1)::integer as lvl,
    tt.adr_adm_ter,
	tt.formalname
  FROM
     edw_prod.edw_ods.t_000148_ent_as_addrobj tt3
    INNER JOIN edw_prod.edw_ods.t_000148_ent_as_addrobj tt ON (
          tt.id = tt3.parent_id )
)
SELECT
  base,
  adr_adm_ter = MAX( CASE WHEN lvl = 1 THEN adr_adm_ter END ),
  MAX( CASE WHEN lvl = 1 AND adr_adm_ter LIKE '[0-9][0-9][0-9][0-9][0-9][0-9],%' THEN LEFT( adr_adm_ter, 6 ) END ) as index,
  MAX( CASE WHEN aolevel = 1 THEN formalname END ) as formalname1,
  MAX( CASE WHEN aolevel = 2 THEN formalname END ) as formalname2,
  MAX( CASE WHEN aolevel = 3 THEN formalname END ) as formalname3,
  MAX( CASE WHEN aolevel = 4 THEN formalname END ) as formalname4,
  MAX( CASE WHEN aolevel = 5 THEN formalname END ) as formalname5,
  MAX( CASE WHEN aolevel = 6 THEN formalname END ) as formalname6,
  MAX( CASE WHEN aolevel = 7 THEN formalname END ) as formalname7,
  MAX( CASE WHEN aolevel = 8 THEN formalname END ) as formalname8,
  MAX( CASE WHEN aolevel = 90 THEN formalname END ) as formalname90,
  MAX( CASE WHEN aolevel = 91 THEN formalname END ) as formalname91
FROM
  cte
GROUP BY
  base
limit 1000


;так нет у тебя в cte aolevel
Есть lvl
...
Рейтинг: 0 / 0
Рекурсия
    #39796191
Фотография court
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
*Ann*
Код: sql
1.
    (tt.aolevel = 1)::integer as lvl,

это ваще бредятина какая-то ...
Это может "имелось в виду" ?
Код: sql
1.
1::integer as lvl,
...
Рейтинг: 0 / 0
Рекурсия
    #39796255
*Ann*
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Согласна, бредятина )) Исправила. Вроде запрос заработал, но выдается какая-то ерунда...
Взять для примера первую строку.
В поле base выдается id улицы (433404, Ульяновская обл, Чердаклинский р-н, Богдашкино с., Полевая ул.), а в поле adr_adm_ter выдается адрес до населенного пункта "433404, Ульяновская обл, Чердаклинский р-н, Богдашкино с."
и в итоге мне надо получить строку с такими значениями (если взять первую строку результатов из файла):
formalname7 = Полевая
formalname6 = Богдашкино
formalname3 = Чердаклинский
formalname1 = Ульяновская
adr_adm_ter = 433404, Ульяновская обл, Чердаклинский р-н, Богдашкино с., Полевая ул.
base = 24292037

Ошибка где-то в первых селектах.
...
Рейтинг: 0 / 0
25 сообщений из 25, страница 1 из 1
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Рекурсия
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]