2013-03-02 81 views
2

我有2個表如下得到記錄:SAS PROC SQL與最近的日期特定日期

表1,用戶清單表:

Year Month Id Type 
2010 3  1 A 
2010 5  2 B 
2010 10 1 A 
2010 12 1 A 

表2描述了用戶推廣歷史:

Promote Date Id 
2/20/2010 1 
5/20/2010 1  (4/2010 the user got demoted, and after 1 month he got promote again) 

從這2個表,我需要產生喜歡錶1的結果表,但增加了該分類A型用戶已在過去3個月被提拔或在3個月以上的列具體日期。例如,結果將是:

Year Month Id | Duration 
2010 3  1 | A < 3 months 
2010 10 1 | A > 3 months 
2010 12 1 | A > 3 months  

總體思路是:

  • 我需要一個月列,年列轉換從表1中的 日期格式像3/2010
  • 將日期(2/2010)中距離最近的促銷日期的新轉換值減去 ,以獲得用戶使用 促銷的天數
  • 與將其分類爲90天的天數推廣時間

有兩個問題,我目前堅持。

我不知道將月份列和年份列轉換爲月份/年份日期格式的最佳方法。

假設我已經從table1轉換了month/year列,我使用Max函數從table2中獲取最近的日期。據我所知,max函數對性能不好,所以有沒有其他的解決方案,而不是使用max?在mysql中,使用限制1很容易解決,但SAS proc-sql不支持限制。是否有任何等價於proc-sql的限制?以下是我目前正在考慮的代碼。

PROC SQL; 
Create table Result as SELECT table1.Year, table1.Month, table1.Code, 
(Case When table1.Type = "B" then "B" 
When table1.Type = "A" AND (table1.Date - (Select MAX(table2.Date) From table2 Where table2.Date <= table1.Date AND table2.Id = table1.Id) < 90) THEN "A < 3 months" 
When table1.Type = "A" AND (table1.Date - (Select MAX(table2.Date) From table2 Where table2.Date <= table1.Date AND table2.Id = table1.Id) >= 90) THEN "A > 3 months" 
When table1.Type = "C" then "C" 
end) as NewType 
From table1 
LEFT JOIN 
// .... 
; 
QUIT; 

正如你所看到的,我要留下參加與其他表的表1,所以我使用子查詢,這也是一個糟糕的表現也是如此,但我不知道是否有任何其他方式。幫助和建議表示讚賞。

回答

4

您可以從它的使用mdy()功能的日期值,就像這樣:

data have; 
input Year Month Id Type $; 
datalines; 
2010 3  1 A 
2010 5  2 B 
2010 10 1 A 
2010 12 1 A 
; 
run; 

data have; 
set have; 
format date date9.; 
date = mdy(Month, 1, Year); 
run; 

你不必每天價值,所以我只是用1(創建的每個日期是當月的第一天)。

現在,你可以通過ID連接兩個表,並計算在第一表和促銷日期從日期的差別在第二個表:

proc sql; 
    create table want as 
    select * 
      ,abs(date - promote) as diff 
    from have as a 
      left join 
     prom as b 
      on a.id = b.id; 
quit; 

之後,你的ID進行排序的結果表,日期和DIFF:

proc sort data=want; 
by id date diff; 
run; 

排序數據集後看起來是這樣的:

Year Month Id Type date  Promote diff 
--------------------------------------------------- 
2010 3  1 A  01MAR2010 20FEB2010 9 
2010 3  1 A  01MAR2010 20MAY2010 80 
2010 5  2 B  01MAY2010 .   . 
2010 10  1 A  01OCT2010 20MAY2010 134 
2010 10  1 A  01OCT2010 20FEB2010 223 
2010 12  1 A  01DEC2010 20MAY2010 195 
2010 12  1 A  01DEC2010 20FEB2010 284 

最後一步,遍歷數據集並檢查每個ID和日期值的第一個diff值是否低於或大於3個月(我剛剛檢查了90天,還可以使用intck函數)。因爲我們通過id,date和diff對數據集進行了排序,所以第一行應該是最接近日期的,所以您只有output只有第一行。

data want2(keep = year month id type duration); 
set want; 
by date; 

if first.date and Type = 'A' then do; 


if diff lt 90 then do; 
    duration = 'A < 3 months'; 
    output want2; 
end; 
if diff gt 90 then do; 
    duration = 'A > 3 months'; 
    output want2; 

    end; 
end; 
else if first.date then do; 
    duration = type; 
    output want2; 
end; 

run; 

output語句的使用,因爲我們只想保留一些行(第一個針對通過組)。最後的output就是這樣,所以具有不同於A的類型值的行也保留在最終結果中。

這是最後的結果是:

Year Month Id Type duration 
-------------------------------------------- 
2010 3  1  A  A < 3 months 
2010 5  2  B  B 
2010 10  1  A  A > 3 months 
2010 12  1  A  A > 3 months 
+0

非常感謝您爲您詳細的解答。關於日值,有沒有辦法讓日值成爲該月的最後一天?最後一個代碼塊,你能指出我的迭代命令的位置嗎?如果我們使用輸出命令,它會創建所需的結果表嗎? – 2013-03-02 11:43:58

+0

您可以使用'intnx'函數將日期值移動到月底,如下所示:'intnx('month',mdy(Month,1,Year),0,'end')'。不需要'迭代'命令,當你在'set'語句中使用它時,會在數據集的行上存在隱式循環。我要編輯答案,以更好地說明這裏發生了什麼。 – Tartaglia 2013-03-02 14:22:38

+0

對於遲到的回覆感到抱歉。我已經試過你的答案,它完美的作品。爲了適應我的需要,我已經將它扭曲了一些。非常感謝您的詳細解答。 – 2013-03-04 14:21:55