2016-04-04 2 views
3

Я пытаюсь перенести некоторые плохо сформированные данные в базу данных. Данные поступают из CSV и сначала загружаются в промежуточную таблицу всех столбцов varchar (поскольку на этом этапе я не могу обеспечить безопасность типов).Саморегулярный оператор CASE WHEN в SQL

Эти данные могут выглядеть

COL1  | COL2 | COL3 
Name 1 |  |  
2/11/16 | $350 | $230 
2/12/16 | $420 | $387 
2/13/16 | $435 | $727 
Name 2 |  |  
2/11/16 | $121 | $144 
2/12/16 | $243 | $658 
2/13/16 | $453 | $214 

Первый Колум представляет собой смесь названий компаний, как псевдо-заголовки, и даты, для которых Колум 2 и 3 данные релевантны. Я хотел бы начать преобразовывать данные, создав столбец «Бренд», где «StoreBrand» - это значение Col1, если Col2 равно NULL или в предыдущей строке StoreBrand. Comething как:

COL1  | COL2 | COL3 | StoreBrand 
Name 1 |  |  | Name 1 
2/11/16 | $350 | $230 | Name 1 
2/12/16 | $420 | $387 | Name 1 
2/13/16 | $435 | $727 | Name 1 
Name 2 |  |  | Name 2 
2/11/16 | $121 | $144 | Name 2 
2/12/16 | $243 | $658 | Name 2 
2/13/16 | $453 | $214 | Name 2 

Я написал:

SELECT 
    t.*, 
    CASE 
     WHEN t.COL2 IS NULL THEN COL1 
     ELSE      LAG(StoreBrand) OVER() 
    END AS StoreBrand 
FROM 
(
    SELECT 
     ROW_NUMBER() OVER() AS i, 
     * 
    FROM 
     Staging_Data 
) t; 

Но база данных (Postgres в этом случае, но мы рассматриваем альтернативы, так что самый разнообразный ответ является предпочтительным) дроссели на LAG (Storebrand) потому что это производный столбец, который я создаю. Вызов LAG (Col1) только заполнит реальные данные в первой строки:

COL1  | COL2 | COL3 | StoreBrand 
Name 1 |  |  | Name 1 
2/11/16 | $350 | $230 | Name 1 
2/12/16 | $420 | $387 | 2/11/16 
2/13/16 | $435 | $727 | 2/12/16 
Name 2 |  |  | Name 2 
2/11/16 | $121 | $144 | Name 2 
2/12/16 | $243 | $658 | 2/11/16 
2/13/16 | $453 | $214 | 2/12/16 

Моя цель будет столбец Storebrand который является первым значением COL1 для всех значений даты до следующей торговой маркой:

COL1  | COL2 | COL3 | StoreBrand 
Name 1 |  |  | Name 1 
2/11/16 | $350 | $230 | Name 1 
2/12/16 | $420 | $387 | Name 1 
2/13/16 | $435 | $727 | Name 1 
Name 2 |  |  | Name 2 
2/11/16 | $121 | $144 | Name 2 
2/12/16 | $243 | $658 | Name 2 
2/13/16 | $453 | $214 | Name 2 

Значение StoreBrand, когда Col2 и Col3 равны нулю, является несущественным - эта строка будет удалена как часть процесса преобразования. Важно связать строки данных (т. Е. С датами) с их брендом.

Есть ли способ ссылаться на предыдущее значение для столбца, который мне не хватает?

+1

Как должен выглядеть результат? –

+0

И вы импортировали данные с каким-то столбцом номера строки (например, 'serial'), который сохраняет исходный порядок. –

+0

vkp - см. Править. –

ответ

1

Edit для людей, которые находят этот вопрос с помощью поисковой системы:

Хитрость заключается в использовании WITH, что позволяет использовать временный результат в нескольких местах (link).


Я думаю, что это делает то, что вы хотите, и отбрасывает пустые строки в то же время (если вы хотите). Мы в основном выбираем все бренды перед строкой, в которой мы сейчас смотрим, и если между ней и текущей строкой нет «торговой марки», мы берем ее.

WITH t AS 
    (SELECT 
     ROW_NUMBER() OVER() AS i, 
     * 
    FROM 
     Staging_Data 
    ) 
SELECT 
    a.COL1, 
    a.COL2, 
    a.COL3, 
    (SELECT b.COL1 FROM t b WHERE b.COL2 IS NULL AND b.i <= a.i AND NOT EXISTS(
     SELECT * FROM t c WHERE c.COL2 IS NULL AND c.i <= a.i AND c.i > b.i) 
    ) StoreBrand 
FROM 
    t a 
WHERE -- I don't think you need those rows? Otherwise remove it. 
    a.COL2 IS NOT NULL 

Это может быть немного запутанным.t - это временная таблица, которую мы определили with ваш запрос. И a, b и c являются псевдонимами для t. Мы могли бы также написать FROM t AS a, чтобы сделать его более очевидным.

+0

OK first pass , это выглядит хорошо! Можете ли вы объяснить мне, где указан псевдоним «a»? Близко, поскольку я могу сказать, что он существует только в этом запросе (может быть, я просто борюсь со всеми одиночными именами символов) –

+0

@ J.Doe добавил объяснение для него. Вы также можете выбрать более длинное имя, чем 't', например Stage_Two или что-то, чтобы сделать его более читаемым. – maraca

0

Я думаю, что понимаю, чего вы хотите. Технически, вы хотите ignore nulls опцию lag(), поэтому он будет выглядеть следующим образом:

select lag(case when col1 not like '%/%/%' then col1 end ignore nulls) over (order by linenumber) as brandname 

Единственная проблема? Postgres не поддерживает ignore nulls.

Но вы можете сделать почти то же самое с подзапросом. Идея состоит в том, чтобы назначить идентификатор группировки для каждой группы. И это совокупное количество действительных брендов. Тогда простой max() агрегация работы:

select t.*, 
     max(case when col1 not like '%/%/%' then col1 end) over (partition by grp) as brand 
from (select t.*, 
      sum(case when col1 not like '%/%/%' then 1 end) over 
       (order by linenumber) as grp 
     from t 
    ); 
+1

Не знаете, как это должно быть работать - col1 является varchar, как вы собираетесь его суммировать? Postgres ответил на этот запрос с помощью «ERROR: сумма функции (изменение символа) не существует LINE 5: SUM (CASE КОГДА Col1 НЕ НРАВИТСЯ«% »ТОГО, КОГДА КОНЕЦ КОНЕЦ) ЗАВЕРШЕНО (ЗАКАЗ B ... ^^ СОВЕТ: Никакой функции соответствует указанному имени и типам аргументов. Возможно, вам придется добавить явные типы. ' –

+0

Я думаю, вы хотите использовать 'sum (... then 1 end) ...', а не 'col1'. – shawnt00

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