2016-04-04 59 views
3

我試圖將一些結構不良的數據遷移到數據庫中。數據來自CSV,並首先被加載到所有varchar列的登臺表中(因爲我無法在此階段強制執行類型安全)。SQL中的自引用CASE WHEN子句

的數據可能看起來像

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的數據是相關的。我想通過創建一個'Brand'列來開始轉換數據 - 其中'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 

當Col2和Col3爲空時,StoreBrand的值無關緊要 - 該行將作爲轉換過程的一部分被刪除。重要的是將數據行(即具有日期的數據行)與其品牌相關聯。

有沒有辦法引用我錯過的列的前一個值?

+1

結果應該如何? –

+0

你是否用某種行號列(例如'serial')導入數據,保留了原來的順序。 –

+0

vkp - 請參閱編輯。 –

回答

1

編輯對誰通過搜索引擎找到這個問題的人:

訣竅是使用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,bct的別名。我們也可以寫FROM t AS a使其更加明顯。

+0

OK first pass ,這看起來不錯!你可以向我解釋別名'a'的定義嗎?我可以告訴它它只存在於這個查詢中(可能僅僅是我與所有單個字符名稱一起掙扎) –

+0

@ J.Doe增加了解釋你也可以選擇一個比't'更長的名字,比如Stage_Two或者其他的東西來使它更具可讀性。 – maraca

0

我想我明白你想要什麼。從技術上說,你想在lag()ignore nulls選項,所以它應該是這樣的:

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迴應此查詢與'錯誤:函數總和(字符變化)不存在 線5:總和(案件當Col1不喜歡'%'然後Col1結束)OVER(ORDER B ... ^ 提示:無功能匹配給定的名稱和參數類型您可能需要添加明確的類型轉換 ' –

+0

我想您想使用'sum(...然後1結束)...'而不是'col1'。 – shawnt00