2016-04-04 15 views
3

不適切な形式のデータをデータベースに移行しようとしています。データはCSVから取得され、最初にすべてのvarcharカラムのステージングテーブルにロードされます(この段階でタイプの安全性を強制することはできません)。SQLの自己参照CASE WHEN句

データは、最初のcolumは、会社の擬似ヘッダなどの名前、およびcolum 2と3のデータが関連しているため、日付の混合物である

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 

のように見えるかもしれません。私は 'Brand'列を作成してデータを変換したいと思います。StoreBrandはCol2がNULLの場合はCol1の値、そうでない場合は前の行の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を、ほとんどの多様な答えが好ましいので、我々は代替案を検討している)(StoreBrand LAGにチョーク)これは私が作成している派生列だからです。 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 

私の目標は、次のブランド名の前にすべての日付値のCOL1の最初の値であるStoreBrand列のようになります。

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がnullの場合の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,bおよびctの別名です。さらに明らかにするためにFROM t AS aと書くこともできます。

+0

OK first passエイリアスがどこに定義されているのかを説明してもらえますか?私はそれがちょうどこのクエリーに存在することを伝えることができます(ただ私はすべての単一の文字名で苦労しているかもしれません)。 –

+0

@ J.Doeは説明を追加しました't'よりも長い名前を選ぶこともできます(例えばStage_Twoなど)。 – maraca

0

私はあなたが欲しいものを理解していると思います。

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

唯一の問題:それは次のようになりますので、技術的には、あなたは、lag()ignore nullsオプションをしたいですか? 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:function sum(character varying)が存在しません。 LINE 5:SUM(Col1が'% 'を超えている場合のケース1)OVER(ORDER B ... ^ ヒント:機能がありません指定された名前と引数の型と一致します。明示的な型キャストを追加する必要があります。 ' –

+0

'col1'ではなく' sum(... then end)...'を使用したいと考えています。 – shawnt00