2012-07-23 121 views
0

選擇列考慮以下表中,其與資產的調度涉及的應用程序:從第一和最後一個記錄匹配的標準

date  group_id free_spots 
2011-01-01 1   0 
2011-01-01 2   0 
2011-01-08 1   1 
2011-01-08 2   0 
2011-01-15 1   1 
2011-01-15 2   1 
2011-01-22 1   2 
2011-01-22 2   2 
2011-01-29 1   1 
2011-01-29 2   0 
2011-02-05 1   0 
2011-02-05 2   1 
2011-02-12 1   0 
2011-02-12 2   1 
2011-02-19 1   0 
2011-02-19 2   0 

此信息放在一起使用利用不同的表一個相當昂貴的查詢(〜100ms的) 。結果可以放在臨時表中,也可以直接內聯使用。

我想要的是找到提供點的第一個日期(free_spots> 0)。然後在同一張唱片中,我想要最後一個拍攝點的日期。所有這些都由group_id分組。

爲了說明,在給定的示例表,我希望下面的輸出:

group_id start_date end_date 
1   2011-01-08 2011-01-29 
2   2011-01-15 2011-01-22 
2   2011-02-05 2011-02-12 

現在,我已經想出了一個粗略的解決方案。使用給定的表我想:

  • 抓住這些之前*由NULL或與free_spots記錄< = 0(開始日期)
  • 對於所有這些記錄的所有記錄,搶到第一接班人*是成功由一行爲空或有free_spots < = 0
  • 以某種方式在這裏混合group_id分組。

但是,這似乎是不可能的,因爲我不能再次使用相同的子查詢來查找後繼或前面的記錄。與臨時表格相同。這些我無法打開並重復使用一次以上。

(*在之前或基於該日期成功了。對於每個組,該日期是相等的,連續的和均勻的(但任意地)間隔開。一般7或14天)

+0

請參閱我的更新,有一個MySQL的翻譯爲我工作的SQL Server的答案的開始。 – ErikE 2012-07-24 00:32:16

回答

1

它可能不是超高效的,但它適用於您的數據。 (請注意,我添加了一個WHERE約束的日期範圍,如果你想要一個):

SELECT group_id,MIN(`date`) AS start_date, 
     (SELECT `date` FROM Slots s3 
     WHERE s3.group_id=t.group_id 
     AND s3.`date`<t.next_stop_date 
     AND s3.free_spots > 0 
     ORDER BY s3.`date`DESC 
     LIMIT 1) as end_date 
FROM 
    (SELECT s1.*, MIN(s2.`date`) AS next_stop_date 
    FROM 
    Slots s1 LEFT JOIN Slots s2 
     ON s2.`date` > s1.date AND s1.group_id=s2.group_ID AND s2.free_spots = 0 
    WHERE s1.free_spots > 0 
    GROUP BY s1.group_id, s1.`date` 
    ORDER BY s1.group_id ASC, s1.`date` ASC 
) AS t 
GROUP BY group_id, next_stop_date 
+0

不幸的是,這似乎給我與我的嘗試遇到同樣的錯誤:無法重新打開表's3'。另外,這些日期在查詢中被硬編碼的原因是什麼?我想我可以忽略這些以處理整個表格? – ChrisDekker 2012-07-24 00:07:36

+0

我不確定你的意思是「無法重新打開表格s3'」。您可以刪除日期而不影響任何內容。 (我只是編輯了答案,以便他們不再在那裏) – 2012-07-24 00:13:59

+0

您可以查看數據並使用http://sqlfiddle.com/#!2/0f4b5/12/0 – 2012-07-24 00:24:24

-1

予想不到的超級簡單的方法來做到這一點。這裏有一個方法的草圖,我可以(使用多個查詢)

create temporary table temp1 select group_id,min(date) as start_date from table1 where free_spots>0 group by group_id

alter table temp1 add column end_date datetime default null

create temporary table TEMP2在while循環select * from table1 where free_spots>0

然後(使用某種編程語言),想起來了,我會做下列事情,直到temp2爲空。你應該在迴路中的每一個步驟(稱之爲$ CURDATE)增加日期到第二天:

update temp2,temp1 set temp1.end_date=temp2.date where temp1.group_id=temp2.group_id and temp2.date='$curDate' and temp2.free_spots>0

你可以,如果有0行更新,每次查詢後檢查。如果他們是,你就完成了,你可以打破while循環。

+0

這讓我覺得解決方案更好地放在應用程序端(Rails 3.x)而不是MySQL。 – ChrisDekker 2012-07-24 00:09:35

+0

此外,只選擇free_spots> 0到第二個臨時表中的記錄將合併該組的所有開始/結束範圍。例如,自由點批次[1,2,2,1,0,0,1,2,1]將作爲[1,2,2,1,1,2,1]插入,從而丟失來自中心...或者我一定完全誤會了你...... – ChrisDekker 2012-07-24 00:19:59

-1

這在SQL服務器上工作(如果我正確理解你的問題)。它應在MySQL工作,以及:

選擇a.group_id,a.min_date,從b.max_date( 選擇s1.group_id,分鐘(s1.date)MIN_DATE 從點S1 組由s1.group_id) a 內部連接
(從group_id中選擇group_id,max(date)max_date group_id)a.group_id = b上的 。group_id

+0

這會給每組最多1個開始/結束組合。考慮一批空閒點[1,2,2,1,0,0,1,2,1]。對於同一組,顯然應該是[1,2,2,1]和[1,2,1]。 2條記錄。 – ChrisDekker 2012-07-24 00:13:44

1

我可以在SQL Server寫這篇文章,並知道這是翻譯到MySQL。首先我會給你SQL Server版本,然後在下面的翻譯給你一個提升。我會跳過這個問題,但最初並沒有意識到這是針對MySQL的。

這容忍日期之間的任何長度的可變間隙。

WITH IDs AS (
    SELECT *, Row_Number() OVER (PARTITION BY GroupID ORDER BY AvailableDate) ID 
    FROM Availability 
), Data AS (
    SELECT 
     GroupID, 
     AvailableDate, 
     ID - Dense_Rank() OVER (PARTITION BY GroupID ORDER BY ID) G 
    FROM IDs 
    WHERE FreeSpots > 0 
) 
SELECT 
    GroupID, 
    Min(AvailableDate) FromDate, 
    Max(AvailableDate) ToDate 
FROM Data 
GROUP BY GroupID, G; 

這裏的安裝腳本:

CREATE TABLE Availability (
    AvailableDate datetime, 
    GroupID tinyint, 
    FreeSpots tinyint 
) 
INSERT Availability 
SELECT '20110101', 1, 0 
UNION ALL SELECT '20110101', 2, 0 
UNION ALL SELECT '20110108', 1, 1 
UNION ALL SELECT '20110108', 2, 0 
UNION ALL SELECT '20110115', 1, 1 
UNION ALL SELECT '20110115', 2, 1 
UNION ALL SELECT '20110122', 1, 2 
UNION ALL SELECT '20110122', 2, 2 
UNION ALL SELECT '20110129', 1, 1 
UNION ALL SELECT '20110129', 2, 0 
UNION ALL SELECT '20110205', 1, 0 
UNION ALL SELECT '20110205', 2, 1 
UNION ALL SELECT '20110212', 1, 0 
UNION ALL SELECT '20110212', 2, 1 
UNION ALL SELECT '20110219', 1, 0 
UNION ALL SELECT '20110219', 2, 0 

MySQL的翻譯

下應相當於我的第一CTE(公共表表達式),模擬ROW_NUMBER()函數。稍微調整一下,你可以用這個作爲派生表來做第二個CTE來模擬Dense_Rank(),並且你有一個工作查詢!

SELECT 
    GroupID, 
    AvailableDate, 
    FreeSpots, 
    @rownum=CASE WHEN @grpset <> GroupID THEN 0 ELSE @rownum + 1 END AS rownum, 
    @grpset=GroupID AS grpset 
FROM 
    (SELECT @grpset= -1) g, 
    (SELECT @rownum:= -1) r, 
    (SELECT * 
    FROM Availability 
    ORDER BY GroupID, AvailableDate 
    ) a 

我知道MySQL不是一個小小的東西,所以從網上的一個例子來說,這應該工作,但我可以讓語法錯誤在那裏滑。如果此查詢有效,並且您需要更多幫助,請告訴我,我將嘗試將其應用到MySQL的完整查詢中。雖然如果@Quassnoi出現,你很快就會擁有它!

+0

感謝您的輸入。很抱歉,我沒有更清楚使用MySQL。我很感謝你的回答,但我將與Holger Brandt的解決方案一起進行,因爲它直接對數據進行操作,對我來說更符合邏輯。如果我遇到麻煩,我會嘗試你的解決方案。 – ChrisDekker 2012-07-24 00:40:36

+0

你很清楚,但它被編輯出標題。我絕不會建議任何人使用MySQL,因爲你剛纔指出的原因......非常傷心。 – ErikE 2012-07-24 00:44:38

0

查詢是不是很好,但似乎工作:

SELECT * 
FROM (

SELECT a.group_id, a.`date` AS 
start_date , max(b.`date`) AS 
end_date 
FROM test AS a 
LEFT JOIN test AS b ON a.group_id = b.group_id 
AND b.free_spots >0 
AND a.date < b.date 
WHERE a.free_spots >0 
AND (

SELECT count(*) 
FROM test AS c 
WHERE c.group_id = a.group_id 
AND c.date > a.date 
AND c.date < b.date 
AND free_spots =0 
) =0 
GROUP BY group_id, 
start_date 
) AS d 
WHERE end_date IS NOT NULL 
GROUP BY d.end_date 
ORDER BY `d`.`group_id` ASC 
相關問題