2012-03-07 6 views
0

내 친구 중 한 명이이 Q를 나에게 가지고 있고 너무 혼란 스럽습니다.식별자를 기반으로 두 간격 사이에 데이터 분리

그의 팀이 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 

규칙은 다음과 같습니다 : 예를 들어

발 9, 전 부하 시작 때마다; val이 8 일 때마다 로드가 중지됩니다. (또는 다음 val이 8 일 때마다 최대 부하가 멈 춥니 다).

이때

전체 부하, I 만이 기록을 수집해야


ID 이름 브로

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 기록 파트너로서 더 팔 없습니다
- 아래 결과 집합은 이러한 조건을 만족>

+0

을 미시간도 수집해야합니까? 그리고 애틀란타는 더블 스타트 이후 어떻게 처리되어야할까요? –

+0

3 개의 시작점과 2 개의 끝점이 있습니다. 어떻게 처리할까요? – JNK

+0

@justin ... 이제 규칙은 우리가 8을 가지고 9를 만들어 시작/멈춤 쌍을 형성해야한다는 것입니다. 이제 atlanta (9) 기록에는 즉각적인 동반 파트너 즉 6이 포함되지 않았으므로 포함 시키면 안됩니다. 더 많은 꼬마 도깨비. 여기서 필요한 것은 9 개의 레코드를 포함하거나 포함하지 않고 이러한 모든 쌍 9-8의 레코드를 캡처하는 것입니다.이 레코드는 필요한 경우 쉽게 val = 9 인 필터 절에만 포함될 수 있습니다. – Ram

답변

1

에 대한 몇 가지 상반된 확인하고 의견을 넣어 시도 완전히 도움이 될 수 있기를 바랍니다.

  1. 모든 행의 순위를 매기고 경계 (val IN (8, 9))를 따로 따로 지정합니다.

  2. val = 8은 하위 집합과 결합합니다. 여기서, val = 9은 바인딩 된 순위가 후자보다 정확하게 1 배가되어야합니다.

  3. 은 조건의 2 단계의 결과 집합에 비 (8, 9) 행의 서브 세트에 참여하는 제 (일반) 상기 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

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 시까 지 끝내고 루프 afte를 종료 할 때까지 모든 레코드를 제외하고 진정으로 재귀 CTE는 실제 짐승 일 수 있습니다. – Ram

+0

@justin .... 미시간 레코드가 잘못 선택되어 브루클린이 잘못 되었기 때문에 미안합니다 ... – Ram

+0

업데이트 된 요구 사항을 확인한 후 방금 업데이트했습니다. 위. 이 작업을 수행하는 전체 코드를 제공했습니다 (초기로드 포함). 나는 단지 멈추기부터 시작해서 완벽하게 일치하는 쌍만 원하기 때문에 시작으로 돌아 가야했습니다. 나는 재귀 적 CTE가 그 안에있는 몇 가지 제약으로 인해 작동 할 것이라고 생각하지 않는다. CTE는 종종 코드를 더 읽기 쉽게 만드는 방법이기도합니다.그리고,이 경우 CTE (가능한 경우)가 임시 테이블 방법보다 더 혼란 스럽다고 생각합니다. –