2012-05-10 17 views
4

私は画像投稿のユーザーフィードを持っています。各ユーザは単一の画像を投稿することができるが、頻繁に行動を繰り返すことができる。 1時間以内に複数の画像をアップロードしてください。ユーザーフィードのシリアル投稿をグループ化する

ユーザが1時間以内に複数の画像を(1つずつ)投稿したときに効果的にデータベーステーブルを設計するにはどうすればよいですか?の投稿をINSERTまたはSELECTで簡単にグループ化できますか?


複数アップロードフォームを提案しないでください。それは事実ではありません:私はちょうどより一般的な言葉でタスクを説明しました:)

答えて

2

遊び場アウトです:

CREATE TABLE `feed`(
    `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, 
    `tm` INT UNSIGNED NOT NULL COMMENT 'timestamp', 
    `user_id` INT UNSIGNED NOT NULL COMMENT 'author id', 
    `image` VARCHAR(255) NOT NULL COMMENT 'posted image filename', 
    `group` INT UNSIGNED NULL DEFAULT NULL COMMENT 'post group', 
    PRIMARY KEY(`id`), 
    INDEX(`user_id`), 
    INDEX(`tm`,`group`) 
); 

私たちは一緒のグループに一時的に接近している投稿をしたいと思います。

まず、所望の粒度宣言:

SELECT `g`.`id` AS `group` 
FROM `feed` `g`; 
:各行は行IDと一致するグループIDを有するグループを(それはまた、タイムスタンプであってもよい)を形成

SET @granularity:=60*60; 

:時間的近接度に閾値を

各グループは、同じユーザから発信行が含まれている以前のグループ、前者よりも掲示された:

SELECT `g`.`id` AS `group`, `f`.* 
FROM `feed` `g` 
    CROSS JOIN `feed` `f` 
    ON (`f`.`user_id` = `g`.`user_id` 
     AND `f`.`tm` BETWEEN `g`.`tm`[email protected] AND `g`.`tm` 
    ) 

各行は複数のグループに属します。各行について、我々は最も「幅広い」グループを選択:それは最大のROWID

SELECT MAX(`g`.`id`) AS `group`, `f`.* 
FROM `feed` `g` 
    CROSS JOIN `feed` `f` 
    ON (`f`.`user_id` = `g`.`user_id` 
     AND `f`.`tm` BETWEEN `g`.`tm`[email protected] AND `g`.`tm` 
    ) 
GROUP BY `f`.`id` 

は最近更新されたグループは、常にトップ(ソートgroup DESCたか)にジャンプしています。今

SELECT MIN(`g`.`id`) AS `group`, `f`.* 
FROM `feed` `g` 
    CROSS JOIN `feed` `f` 
    ON (`f`.`user_id` = `g`.`user_id` 
     AND `f`.`tm` BETWEEN `g`.`tm` AND `g`.`tm`[email protected] 
    ) 
GROUP BY `f`.`id` 

、私たちはテーブルのを更新するつもりだ:あなたはグループが永続的でたい場合 しかし、MIN代わりMAXの使用、(たとえば、アイテムを別のグループから移動しないようにします) groupカラム。 最初に、MySQLはあなたが読んでいる同じテーブルを更新できません。一時的なテーブルが必要です。 第二:我々は唯一のgroup列NULLである、または行以降のUNIX_TIMESTAMP()-2*@thresholdよりも掲示行更新:

CREATE TEMPORARY TABLE `_feedg` 
SELECT MAX(`g`.`id`) AS `group`, `f`.`id` 
FROM `feed` `g` 
    CROSS JOIN `feed` `f` 
    ON (`f`.`user_id` = `g`.`user_id` 
     AND `f`.`tm` BETWEEN `g`.`tm`[email protected] AND `g`.`tm` 
    ) 
WHERE `f`.`group` IS NULL 
    OR `f`.`tm` >= (UNIX_TIMESTAMP()-2*@granularity) 
GROUP BY `f`.`id`; 

をそしてgroupコラム更新:

UPDATE `feed` `f` CROSS JOIN `_feedg` `g` USING(`id`) 
SET `f`.`group` = `g`.`group`; 

ここSQLFiddleです:http://sqlfiddle.com/#!2/be9ce/15

3

タイムスタンプを各ポストに保存し、タイムスタンプが次のスレッシュホールドより小さい各アイテムを選択できますか?

もう1つのアイデアは、タイムスタンプと「グループ番号」の両方を各ポストに格納することです。投稿を保存する前に、n分以内に投稿された投稿を探すにはSELECTを実行してください。見つけた場合は、新しい投稿に同じグループ番号を使用してください。そうでない場合は、新しい投稿のグループ番号を増やします。グループ番号で選択して、必要な項目を見つけることができます。

+0

任意のカラムは、タイムスタンプを含むことが可能です。しかし、その後、どのように選択して、「近くに」行を張り合わせるのですか? – kolypto

+0

更新されました。このような問題を解決しようとすると、コンピュータとデータベースについて忘れてしまいます。論理的に考えてみましょう。複数のアイテムをどのように「関連付ける」ことができますか? –

+0

しかし、私はあなたの提案を使用する場合は、[私はこの技術を使用する必要があります(http://stackoverflow.com/questions/10542647/grouping-serial-posts) -in-a-user-feed)を使用してフィードを表示する – kolypto

2

は、私は、データモデルは、同様になりますと仮定し、この:

enter image description here

ただ、ポスト間の時間差がTIMESTAMPの解像度よりも大きいことを確認するように注意してください(または正常PK違反を処理するために準備すること)。

分析機能をサポートするDBMSでは、時間的に近いポストを簡単にグループ化することができます。例えば、お互いの時間内に収まる(特定のユーザーのための)グループのポストへのOracleクエリは、次のようになります。

SELECT T.*, SUM(DIFF) OVER (ORDER BY TIMESTAMP) GROUPING 
FROM (
    SELECT 
     IMAGE.*, 
     CASE 
      WHEN TIMESTAMP <= LAG(TIMESTAMP) OVER (ORDER BY TIMESTAMP) 
       + INTERVAL '1' HOUR 
      THEN 0 
      ELSE 1 
      END DIFF 
    FROM IMAGE 
    WHERE USER_ID = :user_id 
) T; 

たGROUPINGフィールドはTIMESTAMP「近い行の個々のグループを識別します十分な"。このクエリも非常に効率的です。PKインデックスの範囲スキャンです。あなたはSQL Fiddleでそれを遊ぶことができます。

残念ながら、MySQLは分析関数をサポートしていませんが、アプリケーションレベルで本質的に同じことをすることは問題ありません。ちょうどSELECT ... ORDER BY TIMESTAMP、結果を直線的に横断して、現在の行と前の行の違いを確認します。

+0

行間の時間差を使用するのは、各ユーザーの公表記事が他のユーザーの投稿によって「中断されていない」ときだけです。 – kolypto

+0

@o_OTyncわかりません。このスキーマを使用すると、他のユーザーが同時に画像を投稿している場合でも、ユーザーごとのグループ化を効率的に行うことができます( 'WHERE USER_ID =:user_id')。その前提に同意しないのでしょうか、それとも別のことをする必要がありますか? –

+0

申し訳ありませんが、私はあなたのクエリを誤解しました:)残念ながら、私はMySQLが必要とこれは助けになりません:( – kolypto

1

を"o_O Tync"による解決策は、例えば1時、1時40分、2時30分に追加された場合、1時間以内に項目をグループ化しません。最後の2つだけがグループ化されます。

ここには、(同じテーブルの)一時テーブルを持たない超高速のMysqlソリューションがあります。

 
CREATE TABLE `feed`(
    `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, 
    `tm` INT UNSIGNED NOT NULL COMMENT 'timestamp', 
    `user_id` INT UNSIGNED NOT NULL COMMENT 'author id', 
    `image` VARCHAR(255) NOT NULL COMMENT 'posted image filename', 
    `group` INT UNSIGNED NULL DEFAULT NULL COMMENT 'post group', 
    PRIMARY KEY(`id`), 
    INDEX(`user_id`), 
    INDEX(`tm`,`group`) 
); 


SET @granularity:=60*60; 
UPDATE feed f CROSS JOIN (
    SELECT 
    g.id, 
    @id:=COALESCE(IF(ISNULL(@prev_date) OR ([email protected]_user_id) OR NOT(@prev_date-tm BETWEEN 0 AND @granularity), g.id, NULL), @id) 
    +least(0, @prev_date:=tm) 
    +least(0, @prev_user_id:=user_id) as group_id  
    FROM (SELECT @prev_date:=null, @id:=null, @user_id:=null) r, feed g 
    ORDER BY user_id DESC, tm DESC 
) z USING (id) 
SET f.group = z.group_id; 

http://sqlfiddle.com/#!2/02a98/1/0

関連する問題