2009-10-28 1 views
0

В настоящее время я пишу SQL-запрос, который должен отображать древовидное представление областей внутри здания с областями, подзонами и т. Д. К сожалению, мне не удалось имитировать порядок, используемый некоторыми нашими программными инструментами. Я ограничен MS SQL 2000, поэтому вопрос о заказе становится намного сложнее, и я сейчас нахожусь над моей головой.Более продвинутая логика для упорядочивания результатов запроса в T-SQL?

Логика заказа заключается в том, что столбец «Дети» и «Родительский столбец» взаимосвязаны. Если значение столбца «Дети» 1-й строки соответствует столбцу «Родитель» второй строки, вторая строка идет после первого.

--How it currently returns data 
Child Level  Parent 
562 Campus  0 
86 Area  1 
87 Area  1 
88 Area  1 
90 Sub-Area 86 
91 Sub-Area 86 
92 Sub-Area 87 
93 Sub-Area 87 
94 Sub-Area 88 
95 Sub-Area 88 
3 Unit  90 
16 Unit  90 
4 Unit  91 
6 Unit  91 
etc, so on and therefore 

--How I want it to return the data 
Child Level   Parent 
562 Campus   0 
1 Building  562 
86 Area   1 
90 Sub-Area  86 
91 Sub-Area  86 
87 Area   1 
95 Sub-Area  87 
95 Sub-Area  87 

Для этого логика для корректной работы ему необходимо будет сделать что-то вроде

  1. Вернуться строительные строки с их родителем и ребенком кодов
  2. Match Area коды Родитель к кодам здания ребенка, то вставьте строки строк под соответствующей строкой Строка.
  3. Подходящие подгруппы Подходящие коды родительских кодов для детей, а затем вставьте строки подрайона под соответствующей областью.
  4. Match Unit Родительские коды к подзоне кодов ребенка, а затем вставьте строки Unit в соответствующей подобласти

Если это действительно возможно с SQL?

Я хотел бы знать, если это так, поскольку я не решаюсь вкладывать в это больше времени, если не знаю, что это на самом деле возможность. Я понимаю, что я могу написать оператор CASE с настраиваемым сопоставлением для инструкции ORDER BY, но это не сработает ни для какого другого кампуса (родительские/дочерние коды разные), и я хотел бы иметь возможность повторно использовать этот код в будущем с минимальной настройкой.

Спасибо!

EDIT: Добавление запроса в соответствии с просьбой

DECLARE 
@BuildingType int, 
@CampusType int 

SET @BuildingType= 4 
SET @CampusType= 1 

select 

b.fkabc_building_child, 
(select isnull(c.collectionname, 'none') 
from abc_collections c 
where c.pkabc_collections = b.fkabc_building_child) as 'Child Collection', 
l.floorname, 
isnull(b.fkabc_collections_parent,0) as fkabc_collections_parent, 
b.fkabc_floorbreakdowns 

from abc_breakdowns r 
left join abc_floorbreakdowns fr 
on fr.pkabc_floorbreakdowns = b.fkabc_floorbreakdowns 
inner join abc_buildingtypescampustypes btct 
on btct.pkabc_buildingtypescampustypes = fr.fkabc_buildingtypescampustypes 
inner join abc_buildingtypes bt 
on btct.fkabc_buildingtypes = bt.pkabc_buildingtypes 
inner join abc_collectiontypes ct 
on btct.fkabc_collectiontypes = ct.pkabc_collectiontypes 
inner join abc_collections c 
on b.fkabc_building_child = c.pkabc_collections 
inner join abc_floors l 
on l.pkabc_floors = c.fkabc_floors 

where bt.pkabc_buildingtypes = @BuildingType 
and ct.pkabc_collectiontypes = @CampusType 
+0

Можете ли вы опубликовать запрос, который вы используете сейчас. Это может помочь нам, если мы понимаем схему, с которой вы работаете. –

+0

Ооо, это сложно. Это займет промежуточную таблицу, итерацию и некоторую фидшую логику. Я бы сработал для вас, но сейчас не время. Удачи! –

+0

Является ли иерархия произвольно глубокой или ограничена 5 уровнями? Если он ограничен 5 уровнями, то это может быть сделано в режиме «в линию», «нет» или «промежуточная таблица». –

ответ

2

Что-то вроде этого:

-- prepare some test data 
declare @table table (Child int, [Level] varchar(30), Parent int) 

insert @table values (562 , 'Campus ', 0 ) 
insert @table values (1 , 'Building', 562) 
insert @table values (86 , 'Area ', 1 ) 
insert @table values (87 , 'Area ', 1 ) 
insert @table values (88 , 'Area ', 1 ) 
insert @table values (90 , 'Sub-Area', 86) 
insert @table values (91 , 'Sub-Area', 86) 
insert @table values (92 , 'Sub-Area', 87) 
insert @table values (93 , 'Sub-Area', 87) 
insert @table values (94 , 'Sub-Area', 88) 
insert @table values (95 , 'Sub-Area', 88) 
insert @table values (3 , 'Unit ', 90) 
insert @table values (16 , 'Unit ', 90) 
insert @table values (4 , 'Unit ', 91) 
insert @table values (6 , 'Unit ', 91) 

select 
    a.Child, a.[Level], a.Parent 
, Campus = 
    case a.[Level] 
     when 'Unit'  then e.Child 
     when 'Sub-Area' then d.Child 
     when 'Area'  then c.Child 
     when 'Building' then b.Child 
     when 'Campus' then a.Child 
    end 
, Building = 
    case a.[Level] 
     when 'Unit'  then d.Child 
     when 'Sub-Area' then c.Child 
     when 'Area'  then b.Child 
     when 'Building' then a.Child 
    end 
, Area = 
    case a.[Level] 
     when 'Unit'  then c.Child 
     when 'Sub-Area' then b.Child 
     when 'Area'  then a.Child 
    end 
, Sub_Area = 
    case a.[Level] 
     when 'Unit'  then b.Child 
     when 'Sub-Area' then a.Child 
    end 
, Unit = 
    case a.[Level] 
     when 'Unit'  then a.Child 
    end 

from @table a 

left join @table b on a.Parent = b.Child 
    and ((a.[Level] = 'Unit'  and b.[Level] = 'Sub-Area') 
    or (a.[Level] = 'Sub-Area' and b.[Level] = 'Area' ) 
    or (a.[Level] = 'Area'  and b.[Level] = 'Building') 
    or (a.[Level] = 'Building' and b.[Level] = 'Campus' )) 

left join @table c on b.Parent = c.Child 
    and ((b.[Level] = 'Sub-Area' and c.[Level] = 'Area' ) 
    or (b.[Level] = 'Area'  and c.[Level] = 'Building') 
    or (b.[Level] = 'Building' and c.[Level] = 'Campus' )) 

left join @table d on c.Parent = d.Child 
    and ((c.[Level] = 'Area'  and d.[Level] = 'Building') 
    or (c.[Level] = 'Building' and d.[Level] = 'Campus' )) 

left join @table e on d.Parent = e.Child 
    and ((d.[Level] = 'Building' and e.[Level] = 'Campus' )) 

order by 
    4, 5, 6, 7, 8 

Там, наверное, поумнее способ сделать это ж/меньше повторений, но намекает мне на данный момент.

Теперь этот код предназначен только для демонстрации, чтобы проиллюстрировать, как работает запрос. Вам не нужно иметь 5 полей сортировки в SELECT, вы можете переместить их в ORDER BY. И вы не должны использовать порядковые позиции в ORDER BY.

Но вам нужно 4 соединения и логику условного соединения, чтобы вытащить родительские уровни для каждого ребенка. И вам нужны операторы CASE, чтобы вывести ключ сортировки для каждого уровня.

Возможно, вы можете обернуть инструкцию SELECT в производную таблицу и перенести ORDER BY во внешний запрос. например:

SELECT Child, [Level], Parent 
FROM (
    SELECT .... 
) a 
ORDER BY Campus, Building, Area, Sub_Area, Unit 
+0

Это работает довольно хорошо. Однако, как я упоминал в своем посте, мне не нравятся смешение данных и метаданных (вызов столбца - имя уровня). И я согласен с тем, что, вероятно, есть способ очистить часть повторяемости, но на 5 уровнях это, вероятно, не является серьезной проблемой. Однако, когда они завтра добавят столы в подразделения, код нуждается в капитальном ремонте. Во всяком случае, вы получили мой голос, так как даже с этими ограничениями он намного более изящный, чем мой курсор. –

+0

Возможно, существует некоторый способ избежать значений жестко заданного уровня с помощью таблицы управления, но вам по-прежнему потребуется одно соединение и один столбец сортировки для каждого уровня. Да, курсор может быть более надежным. –

+0

Ничего себе! Спасибо за отличную информацию и идеи. Сегодня я получу больше этого во второй половине дня. Я отправлю свое окончательное решение и отметю, какое решение лучше всего подходит для этой ситуации. (Но, несмотря на это, ваши решения чрезвычайно полезны) – bluehiro

0

я бы тратить больше времени, глядя на него, чтобы выяснить подробности ... но, если вы используете SQL Server 2005 (или 2008), я хотел бы предложить глядя с использованием Общей таблицы Выражение (CTE). Это позволяет построить запрос рекурсивно; поэтому вы можете получить здание, а затем забрать все его дочерние элементы, чтобы добавить их в список. Вы можете придумать схему нумерации или подобное, чтобы получить записи в правильном порядке, используя CTE.

+0

Спасибо RMorrisey, у меня есть несколько систем с 2005 года, и я обязательно попробую Common Table Expressions. Однако для этого конкретного вопроса я ограничен MS SQL 2000. – bluehiro

1

Вот один из подходов; очень процедурный. К сожалению, на SQL Server 2000 я не думаю, что вы сможете уйти от курсоров, если не используете такое решение, как Peter, которое ограничено 5 уровнями и жестко кодирует типы уровней в самом запросе (смешивание данных и метаданных). Вам придется взвесить эти ограничения с любой наблюдаемой разницей в производительности.

Обратите внимание, что я не добавлял никакой обработки для циркулярных ссылок, поэтому, надеюсь, вы предотвратите это от других способов.

SET NOCOUNT ON; 
GO 

DECLARE @foo TABLE 
(
AreaID INT PRIMARY KEY, 
[Level] SYSNAME, 
ParentAreaID INT 
); 

INSERT @foo 
SELECT   562, 'Campus', 0 
UNION ALL SELECT 86, 'Area',  1 
UNION ALL SELECT 87, 'Area',  1 
UNION ALL SELECT 88, 'Area',  1 
UNION ALL SELECT 90, 'Sub-Area', 86 
UNION ALL SELECT 91, 'Sub-Area', 86 
UNION ALL SELECT 92, 'Sub-Area', 87 
UNION ALL SELECT 93, 'Sub-Area', 87 
UNION ALL SELECT 94, 'Sub-Area', 88 
UNION ALL SELECT 95, 'Sub-Area', 88 
UNION ALL SELECT 3, 'Unit',  90 
UNION ALL SELECT 16, 'Unit',  90 
UNION ALL SELECT 4, 'Unit',  91 
UNION ALL SELECT 6, 'Unit',  91 
UNION ALL SELECT 1, 'Building', 562; 

DECLARE @nest TABLE 
(
NestID INT IDENTITY(1,1) PRIMARY KEY, 
AreaID INT, 
[Level] INT, 
ParentNestID INT, 
AreaIDPath VARCHAR(4000) 
); 

DECLARE @rc INT, @l INT; 

SET @l = 0; 

INSERT @nest(AreaID, [Level], AreaIDPath) 
SELECT AreaID, 0, CONVERT(VARCHAR(12), AreaID) 
FROM @foo 
WHERE ParentAreaID = 0; 

SELECT @rc = @@ROWCOUNT; 

WHILE @rc >= 1 
BEGIN 
SELECT @l = @l + 1; 

INSERT @nest(AreaID, [Level], ParentNestID) 
    SELECT f.AreaID, @l, n.NestID 
    FROM @foo AS f 
    INNER JOIN @nest AS n 
    ON f.ParentAreaID = n.AreaID 
    AND n.[Level] = @l - 1; 

SET @rc = @@ROWCOUNT; 

UPDATE n 
    SET n.AreaIDPath = COALESCE(n2.AreaIDPath, '') 
    + '\' + CONVERT(VARCHAR(12), n.AreaID) + '\' 
    FROM @nest AS n 
    INNER JOIN @nest AS n2 
    ON n.ParentNestID = n2.NestID 
    WHERE n.[Level] = @l 
    AND n2.AreaIDPath NOT LIKE '%\' + CONVERT(VARCHAR(12), n.AreaID) + '\%'; 
END 

SELECT 
structure = REPLICATE(' - ', n.[Level]) + RTRIM(f.AreaID), 
f.AreaID, f.[Level], f.ParentAreaID 
FROM @nest AS n 
INNER JOIN @foo AS f 
ON n.AreaID = f.AreaID 
ORDER BY n.AreaIDPath; 

Это действительно то, для чего были предназначены рекурсивные CTE в SQL Server 2005. (Это по-прежнему по существу курсор, но синтаксис намного чище, чем описанный выше беспорядок.) Пока вы не сможете перейти на SQL Server 2005, вам может быть повезло, просто используя уровень представления, чтобы зациклиться на наборе результатов и упорядочить вещи соответствующим образом, если это слишком сложно, чтобы представить ваши операции с запросами.

+0

Если важна функция SELECT, он все равно может обернуть ее в многозначную табличную функцию. –

Смежные вопросы