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

この情報は、さまざまなテーブルを使用したかなり高価なクエリ (最大 100 ミリ秒) を使用してまとめられます。結果は、一時テーブルに入れるか、インラインで直接使用できます。

私が望むのは、スポットを提供する最初の日付を見つけることです (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 (開始日) のレコードのいずれかが先行*するすべてのレコードを取得します
  • これらすべてのレコードについて、行が NULL であるか、または free_spots <= 0 であることが継承された最初のサクセサ * を取得します。
  • どういうわけか、ここで group_id によるグループ化を混合します。

ただし、同じサブクエリを再度使用して後続レコードまたは先行レコードを見つけることはできないため、これは不可能に思えます。一時テーブルと同じ扱い。これらを複数回開いて再利用することはできません。

( * 先行または後続は日付に基づいています。各グループについて、日付は等しく、連続しており、均等に (ただし任意に) 間隔が空けられています。通常は 7 日または 14 日です)

4

5 に答える 5

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 翻訳

以下は、Row_Number() 関数をシミュレートする最初の CTE (Common Table Expression) と同等である必要があります。少し調整するだけで、これを派生テーブルとして使用して、2 番目の 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 が現れれば、すぐに手に入れることができます。

于 2012-07-24T00:03:36.847 に答える
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
于 2012-07-24T00:01:40.543 に答える
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 
于 2012-07-24T00:24:04.933 に答える
-1

これを行うための非常に簡単な方法は考えられません。これが私がそれを考えることができる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 tabletemp2select * from table1 where free_spots>0

次に、whileループ(プログラミング言語を使用)で、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ループを破ることができます。

于 2012-07-23T23:24:56.047 に答える
-1

これはSQLサーバーで機能します(あなたの質問を正しく理解していれば)。mysql でも動作するはずです。

a.group_id、a.min_date、b.max_date を選択 ( s1.group_id によるスポット s1 グループから s1.group_id、min(s1.date) min_date を
選択) 内部結合 (スポット グループから group_id、max(date) max_date を選択) by group_id) b on a.group_id = b.group_id

于 2012-07-23T23:46:40.673 に答える