2013-03-08 35 views
3

我正在嘗試從Oracle RDBMS將視圖轉換爲SQL Server。該視圖的樣子:如何將包含START WITH ... CONNECT BY子查詢的視圖轉換爲SQL Server?

create or replace view user_part_v 
as 
    select part_region.part_id, users.id as users_id 
    from part_region, users 
    where part_region.region_id in(select  region_id 
             from region_relation 
            start with region_id = users.region_id 
            connect by parent_region_id = prior region_id) 

已經閱讀recursive CTE's,也對他們的use in sub-queries,在轉換到上述SQL Server語法我最好的猜測是:

create view user_part_v 
as 
    with region_structure(region_id, parent_region_id) as (
    select region_id 
     , parent_region_id 
     from region_relation 
    where parent_region_id = users.region_id 
    union all 
    select r.region_id 
     , r.parent_region_id 
     from region_relation r 
     join region_structure rs on rs.parent_region_id = r.region_id 
) 
    select part_region.part_id, users.id as users_id 
    from part_region, users 
    where part_region.region_id in(select region_id from region_structure) 

顯然,這給了我關於一個錯誤參考CTE定義中的users.region_id。

如何在SQL Server中實現與Oracle視圖相同的結果?

背景

我對系統的轉換工作從上的Oracle 11g關係數據庫管理系統運行到SQL Server 2008這個系統是一個比較大的基於Java EE的系統,使用JPA(休眠)查詢來自數據庫。

許多查詢使用上述視圖來限制返回適合當前用戶的結果。如果我無法直接轉換視圖,那麼轉換將變得更加困難,因爲我需要更改查詢數據庫的所有位置以獲得相同的結果。

由這種觀點所引用的表有類似的結構:

USERS 
    ID 
    REGION_ID 

REGION 
    ID 
    NAME 

REGION_RELATIONSHIP 
    PARENT_REGION_ID 
    REGION_ID 

PART 
    ID 
    PARTNO 
    DESCRIPTION 

PART_REGION 
    PART_ID 
    REGION_ID 

所以,我們的區域,安排到一個層次。用戶可能被分配到一個地區。一部分可能被分配到許多地區。用戶只能看到分配給其區域的零件。該地區參考不同的地理區域:

World 
    Europe 
    Germany 
    France 
    ... 
    North America 
    Canada 
    USA 
     New York 
     ... 

如果零件,#123,被分配到區域美國,用戶被分配到區域紐約,那麼用戶應該能夠看到的部分。

更新:我能夠通過創建一個單獨的視圖,包含必要的數據,然後讓我的主視圖加入此視圖解決錯誤。這有系統的工作,但我還沒有做徹底的正確性或性能測試。我仍然樂於提供更好的解決方案。

+0

。或類似於http://stackoverflow.com/questions/12869048/hierarchy-query-sql-server-2008 – xQbert 2013-03-08 03:22:11

+1

xQbert,感謝您的評論。我知道如何使用CTE在SQL Server中創建分層查詢。我非常希望按原樣轉換視圖,以避免在查詢數據庫時不得不修改大部分系統。我編輯了這個問題,希望能夠讓這個問題更清楚。 – 2013-03-08 03:34:36

+0

我想你需要在cte中構建整棵樹,然後將結果限制在'IN'子句的子選擇中。順便說一句,在CTE遞歸部分的連接是錯誤的:它應該是'join region_structure'而不是'join region_relation' – 2013-03-08 08:19:10

回答

2

我重新格式化了您的原始查詢,使我更容易閱讀。

create or replace view user_part_v 
as 
select part_region.part_id, users.id as users_id 
from part_region, users 
where part_region.region_id in(
    select region_id 
    from region_relation 
    start with region_id = users.region_id 
    connect by parent_region_id = prior region_id 
); 

讓我們來看看這個查詢中發生了什麼。

select part_region.part_id, users.id as users_id 
from part_region, users 

這是一箇舊式聯接,其中表是笛卡爾聯接,然後結果被後續的where子句減少。

where part_region.region_id in(
    select region_id 
    from region_relation 
    start with region_id = users.region_id 
    connect by parent_region_id = prior region_id 
); 

這是一個使用CONNECT BY語句中的子查詢使用從用戶表中的region_id在外部查詢定義爲遞歸的起點。 然後in子句檢查在遞歸查詢的結果中是否找到part_regionregion_id。 該遞歸遵循region_relation表中給出的父子關聯。

因此,將in子句與引用父類和舊類型聯接的子查詢結合起來,意味着您必須考慮查詢的目的是完成並從該方向接近它(而不僅僅是對舊查詢進行了調整),以便能夠將其轉換爲單個遞歸CTE。

如果零件被分配到區域heirarchy的同一分支上的多個區域,該查詢也會返回多行。例如如果部分被分配給兩個北美美國分配給紐約將獲得兩行的用戶返回他們users_id具有相同part_id數量。


鑑於甲骨文視圖,你給什麼樣的看法是應該做的背景下,我想你要尋找的是更多的東西是這樣的:

create view user_part_v 
as 
with user_regions(users_id, region_id, parent_region_id) as (
    select u.users_id, u.region_id, rr.parent_region_id 
    from users u 
    left join region_relation rr on u.region_id = rr.region_id 
    union all 
    select ur.users_id, rr.region_id, rr.parent_region_id 
    from user_regions ur 
    inner join region_relation rr on ur.parent_region_id = rr.region_id 
) 
select pr.part_id, ur.users_id 
from part_region pr 
inner join user_regions ur on pr.region_id = ur.region_id; 

注意,我我已經將users_id添加到遞歸CTE的輸出中,然後完成part_region表和CTE結果的簡單內部連接。

讓我分解你的查詢。

select u.users_id, u.region_id, rr.parent_region_id 
from users u 
left join region_relation rr on u.region_id = rr.region_id 

這是我們的遞歸的出發集。我們將region_relation表與users表加入,以便爲每個用戶獲取遞歸的起點。該起點是用戶分配給該地區的parent_region_id地區。 A left join在此處完成,並且在用戶被分配到最頂部區域(這意味着該區域的region_relation表中不存在該條目的表中的條目)的情況下,從user表中拉出region_id。

select ur.users_id, rr.region_id, rr.parent_region_id 
from user_regions ur 
inner join region_relation rr on ur.parent_region_id = rr.region_id 

這是CTE的遞歸部分。我們爲每個用戶獲取現有結果,然後爲每個用戶添加現有集合的父區域的行。這種遞歸發生,直到我們用完父母。 (即我們打的是必須在region_relationship表爲他們region_id沒有條目行。)

select pr.part_id, ur.users_id 
from part_region pr 
inner join user_regions ur on pr.region_id = ur.region_id; 

這是我們抓住我們的最終結果集的一部分。假設(就像我從你的描述中得出的那樣)每個區域只有一個父代(這意味着每個region_id只有一行region_relationship),一個簡單的連接將返回所有應該能夠查看該部分的用戶,該部分的region_id。這是因爲每個用戶只有一行返回給用戶的指定區域,每個用戶對於每個父區域都有一行直到層次根。

注:

無論是原來的查詢,這個確實有,我要確保你知道的限制。如果該部分被分配到比用戶更低的區域(即,作爲用戶區域的後代的區域,如被分配給紐約的用戶和用戶到USA而不是其他方式),用戶將看不到該部分。該部分必須分配給用戶分配的區域,或者分配給區域分層中較高的一個。

另一件事是,這個查詢仍然展示我上面提到的關於原始查詢的情況,其中如果一部分被分配到多個區域沿多個區域的多個區域,多個行將被返回相同的組合users_idpart_id。我這樣做是因爲我不確定你是否想要改變這種行爲。

如果這確實是一個問題,你想消除重複,那麼你可以用這個代替CTE下面的查詢:

select p.part_id, u.users_id 
from part p 
cross join users u 
where exists (
    select 1 
    from part_region pr 
    inner join user_regions ur on pr.region_id = ur.region_id; 
    where pr.part_id = p.part_id 
    and ur.users_id = u.users_id 
); 

這確實笛卡爾part表和users之間的連接表,然後僅返回行的組合,這兩個行的組合在子查詢的結果中至少有一行,這是我們試圖去重複的結果。用於XML ..語法的

+0

感謝您的回覆。我在設法找到解決方法後不久取消了該項目。儘管我無法對其進行測試,但我認爲你的答案是正確的,因爲它看起來非常徹底,而且是唯一的答案。再次感謝。 – 2017-07-12 14:48:05

相關問題