2012-03-07 93 views
0

我的一位朋友對我有這個問題,我感到太困惑了。基於標識符在2個區間之間隔離數據

他的團隊正在加載DW,並且數據在adhoc基礎上以遞增和滿載的方式進行。現在有一個標識符標誌 ,表示滿載何時開始或停止。現在我們需要收集並分離所有滿負荷。

對於離:

create table #tmp (
    id int identity(1,1) not null, 
    name varchar(30) null, 
    val int null 
) 

insert into #tmp (name, val) select 'detroit', 3 
insert into #tmp (name, val) select 'california', 9 
insert into #tmp (name, val) select 'houston', 1 
insert into #tmp (name, val) select 'los angeles', 4 
insert into #tmp (name, val) select 'newyork', 8 
insert into #tmp (name, val) select 'chicago', 1 
insert into #tmp (name, val) select 'seattle', 9 
insert into #tmp (name, val) select 'michigan', 6 
insert into #tmp (name, val) select 'atlanta', 9 
insert into #tmp (name, val) select 'philly', 6 
insert into #tmp (name, val) select 'brooklyn', 8 

drop table #tmp 

規則是:

每當val爲9,全負荷啓動;每當val爲8時,全部的 加載停止; (或者當下一個val是8時,滿載停止)。

在這種情況下,對於全負載,我應該只收集這些記錄:


ID名稱VAL

3休斯敦1
4洛杉磯4
10費城6

我的方法到目前爲止:

;with mycte as (
    select id, name, val, row_number() over (order by id) as rnkst 
    from #tmp 
    where val in (8,9)) 
SELECT * 
FROM mycte y 
WHERE val = 9 
    AND Exists (
     SELECT * 
     FROM mycte x 
     WHERE x.id = 
         ----> this gives start 9 record but not stop record of 8 
         (SELECT MIN(id)  
         FROM mycte z 
         WHERE z.id > y.id) 
      AND val = 8) 

我不想冒險進入光標方式的光標,但用CTE,請指教!

UPDATE:
正如應答者,我重申了規則的人提到。
- >滿負荷記錄開始後即將9(第9條記錄不包括在內)
- >滿負荷持續到它看到眼前8.
- >所以,有效地9至8形式的所有記錄滿載的小塊
- >的個人第9個記錄本身沒有得到考慮,因爲它沒有8夥伴
- >下面顯示的結果集滿足這些條件

+0

不應該密歇根也收集?亞特蘭大應該如何處理,因爲這是一個雙重的開始? – 2012-03-07 18:52:39

+0

你有3個開始,但2個結束。你想如何處理? – JNK 2012-03-07 19:13:59

+0

@justin ......現在的規則是,我們應該有一個8伴隨着9,形成一個起止點對。現在因爲亞特蘭大(9)記錄沒有其直接伴隨的伴侶,即6,所以不應該包括它。越多進出口。這裏的事情是捕獲所有這些對9-8中的記錄,而不是包括或不包括9條記錄,因爲如果需要的話,這些記錄可以容易地包含在只有val = 9的過濾器子句中。 – Ram 2012-03-07 19:16:35

回答

1

我不知道如果我的英語命令將允許我解釋一下我的方法完全,但我會嘗試,以防萬一它可以幫助。

  1. 排列所有行並分別排列界限(val IN (8, 9))。

  2. 加入子集,其中val = 8與子集val = 9的條件是前者的綁定排名應該完全比後者的1(一)大。

  3. 加入非(8, 9)行的子集到結果集步驟2的條件是下述(通用)排名應該是val = 9子集的排名和該val = 8之一之間。

這裏的查詢來說明我的口頭描述的嘗試:

WITH ranked AS (
    SELECT 
    *, 
    rnk  = ROW_NUMBER() OVER (ORDER BY id), 
    bound_rnk = ROW_NUMBER() OVER (
     PARTITION BY CASE WHEN val IN (8, 9) THEN 1 ELSE 2 END 
     ORDER BY id 
    ) 
    FROM #tmp 
) 
SELECT 
    load.id, 
    load.name, 
    load.val 
FROM  ranked AS eight 
INNER JOIN ranked AS nine ON eight.bound_rnk = nine.bound_rnk + 1 
INNER JOIN ranked AS load ON load.rnk BETWEEN nine.rnk AND eight.rnk 
WHERE eight.val = 8 
    AND nine .val = 9 
    AND load .val NOT IN (8, 9) 
; 

,你可能不相信我,但是當我測試了它,它沒有返回以下:

id name  val 
-- ----------- --- 
3 houston  1 
4 los angeles 4 
10 philly  6 
+0

@Andrily ...非常好的解釋和結果是一個正確的匹配!...與案例分區是新的學習我....感謝證明它可以做到,也很容易! – Ram 2012-03-11 07:51:46

0

我不相信有是一種不需要一個while循環就可以做到這一點的方法,也可能是一個複雜的遞歸cte。所以,我的問題是,如果這完全有可能在代碼中完成? SQL不像程序語言那樣強大,所以代碼會更好地處理這個問題。如果這不是一個選項,那麼我會去一個while循環(比遊標好得多)。我將很快創建SQL。

/* 
drop table #tmp 
drop table #finalTmp 
drop table #startStop 
*/ 

    create table #tmp (
    id int identity(1,1) not null, 
    name varchar(30) null, 
    val int null 
) 

insert into #tmp (name, val) select 'detroit', 3 
insert into #tmp (name, val) select 'california', 9 
insert into #tmp (name, val) select 'houston', 1 
insert into #tmp (name, val) select 'los angeles', 4 
insert into #tmp (name, val) select 'newyork', 8 
insert into #tmp (name, val) select 'chicago', 1 
insert into #tmp (name, val) select 'seattle', 9 
insert into #tmp (name, val) select 'michigan', 6 
insert into #tmp (name, val) select 'atlanta', 9 
insert into #tmp (name, val) select 'philly', 6 
insert into #tmp (name, val) select 'brooklyn', 8 

CREATE TABLE #Finaltmp 
    (
     id INT, 
     name VARCHAR(30), 
     val INT 
    ) 

    SELECT id, val, 0 AS Checked 
    INTO #StartStop 
    FROM #tmp 
    WHERE val IN (8,9) 

    DECLARE @StartId INT, @StopId INT 
    WHILE EXISTS (SELECT 1 FROM #StartStop WHERE Checked = 0) 
    BEGIN 
     SELECT TOP 1 @StopId = id 
     FROM #StartStop 
     WHERE EXISTS 
      --This makes sure we grab a stop that has a start before it 
      (
       SELECT 1 
       FROM #StartStop AS TestCheck 
       WHERE TestCheck.id < #StartStop.id AND val = 9 
      ) 
     AND Checked = 0 AND val = 8 
     ORDER BY id 

     --If no more starts, then the rest are stops 
     IF @StopId IS NULL 
      BREAK 

     SELECT TOP 1 @StartId = id 
     FROM #StartStop 
     WHERE Checked = 0 AND val = 9 
      --Make sure we only pick up the 9 that matches 
      AND Id < @StopId 
     ORDER BY Id DESC 

     IF @StartId IS NULL 
      BREAK 

     INSERT INTO #Finaltmp 
     SELECT * 
     FROM #tmp 
     WHERE id BETWEEN @StartId AND @StopId 
      AND val NOT IN (8,9) 

     --Make sure to "check" any values that fell in the middle (double 9's) 
     --If not, then you would start picking up overlap data 
     UPDATE #StartStop 
     SET Checked = 1 
     WHERE id <= @StopId 
    END 

    SELECT * FROM #Finaltmp 

我注意到,數據看上去有點靠不住的,所以我試圖把一些邊緣情況的檢查和評論對他們

+0

謝謝Justin ....是的循環在一個循環內將毫無疑問地工作,因爲我們可以繼續將記錄附加到從9開始直到我們達到8並且退出循環後的虛擬表格r所有記錄,但一個真正的遞歸CTE可能是真正的野獸...... – Ram 2012-03-07 19:30:49

+0

@justin ....對不起,它給出了不正確的結果,因爲密歇根記錄錯誤地被拾起,所以是布魯克林... – Ram 2012-03-07 19:36:41

+0

我看到你的更新要求後才更新它以上。我給出了完成這個工作的所有代碼(包括初始加載)。我只需要先停下來,然後回去開始,因爲你只需要完美匹配的對。我不相信遞歸的CTE會因爲它內部的一些約束而工作。以及,CTE通常是使代碼更具可讀性的一種方式。而且,在這種情況下,我認爲CTE(如果可能的話)會比臨時表的方式更混亂 – 2012-03-07 20:03:49