Разделить данные между 2 интервалами на основе идентификатора - PullRequest
0 голосов
/ 07 марта 2012

У одного из моих друзей был этот вопрос для меня, и я слишком озадачен.

Его команда загружает DW, и данные продолжают поступать в режиме приращения и полной загрузки на основе adhoc basic. Теперь есть идентификатор флага, который говорит относительно того, когда полная загрузка началась или остановилась. Теперь нам нужно собрать и отделить всю полную нагрузку.

Например:

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, полный нагрузка останавливается; (или когда каждый следующий параметр равен 8, полная загрузка прекращается).

В этом случае для полной загрузки я должен собирать только следующие записи:


имя-идентификатор val

3 Хьюстон 1
4 Лос-Анджелес 4
10 philly 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 образуют небольшие фрагменты полной загрузки
-> Сама девятая запись не считается, так как у нее нет 8-го партнера
-> Показанный ниже набор результатов удовлетворяет этим условиям

Ответы [ 2 ]

1 голос
/ 08 марта 2012

Я не уверен, что мое знание английского позволит мне полностью объяснить мой подход, но я постараюсь, на всякий случай, если это поможет.

  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 голосов
/ 07 марта 2012

Я не верю, что есть способ сделать это без цикла 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

Я заметил, что данные выглядят немного шаткими, поэтому я попытался поставить некоторые проверки и комментарии к ним

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...