Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Рекурсия / 25 сообщений из 25, страница 1 из 1
25.12.2018, 17:02
    #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
25.12.2018, 17:10
    #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
25.12.2018, 20:21
    #39753083
Щукина Анна
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Рекурсия
*Ann*,

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

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

Если же задача всё таки стоит в отдельные столбцы, а не просто одной строкой - то тут прямая дорога к динамическому SQL. А в вашем конкретном случае - сначала вертикальный сбор дерева посредством рекурсивного СТЕ, затем его разворот в строку при помощи динамического PIVOT-а
...
Рейтинг: 0 / 0
28.12.2018, 08:24
    #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
28.12.2018, 10:13
    #39754361
*Ann*
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Рекурсия
PizzaPizza,

Уровней всего 9. Если родителя какого-то уровня нет, то ячейку надо пропустить. Уровень указан в поле level. Если нет индекса, то ячейка тоже должна остаться пустой.
...
Рейтинг: 0 / 0
28.12.2018, 10:44
    #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
28.12.2018, 21:01
    #39754713
PizzaPizza
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Рекурсия
*Ann*PizzaPizza,

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

Если у вас всегда четко 9 уровней, то сделайте на 9ти джойнах например.
Выберете все записи первого уровня и к ним присоединяйте self join Id=parent_id и следующий уровень, а к нему уже по Id=parent_id-предыдущего уровня и так 9 раз. А потом отдельно поиск индекса по каким то правилам в подстроке и формируете запись.
...
Рейтинг: 0 / 0
29.12.2018, 10:00
    #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
09.01.2019, 06:49
    #39756939
*Ann*
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Рекурсия
Руслан Дамирович,

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


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

а какая у вас СУБД - имя, версия?
...
Рейтинг: 0 / 0
04.04.2019, 10:02
    #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
04.04.2019, 10:06
    #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
04.04.2019, 10:10
    #39796191
court
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Рекурсия
*Ann*
Код: sql
1.
    (tt.aolevel = 1)::integer as lvl,

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

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


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