4

画像投稿のユーザー フィードがあります。各ユーザーは単一の画像を投稿できますが、アクションを頻繁に繰り返すことができます。たとえば、1 時間以内に複数の画像をアップロードします。

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


マルチアップロードフォームを提案しないでください。そうではありません。より一般的な用語でタスクを説明しただけです:)

4

4 に答える 4

3

投稿ごとにタイムスタンプを保存し、タイムスタンプがしきい値よりも小さい各アイテムを次のアイテムから選択できますか?

別のアイデアは、タイムスタンプと「グループ番号」の両方を各投稿に保存することです。投稿を保存する前に、 a を実行して、過去数分SELECT以内に送信された投稿を探します。n見つかった場合は、新しい投稿に同じグループ番号を使用してください。そうしない場合は、新しい投稿のグループ番号を増やしてください。次に、グループ番号で選択して、必要なアイテムを見つけることができます。

于 2012-05-10T21:58:12.313 に答える
2

データモデルは次のようになります。

ここに画像の説明を入力してください

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

分析機能をサポートするDBMSでは、時間的に近い投稿をかなり簡単にグループ化できます。たとえば、互いに1時間以内にある(特定のユーザーの)投稿をグループ化する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フィドルで遊ぶことができます。

残念ながら、MySQLは分析関数をサポートしていませんが、アプリケーションレベルで基本的に同じことを行うのに問題はないはずです。ただSELECT ... ORDER BY TIMESTAMP、結果を直線的にトラバースして、現在の行と前の行の違いを確認してください。

于 2012-05-11T11:50:08.837 に答える
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`)
  );

時間的に近い投稿をまとめたいと思います。

まず、必要な粒度を宣言します: 時間的近接性に対するしきい値:

SET @granularity:=60*60;

各行は、行 ID と一致するグループ ID (タイムスタンプの場合もあります) を持つグループを形成します。

SELECT `g`.`id` AS `group`
FROM `feed` `g`;

各グループには、グループ形成者よりも前に投稿された、同じユーザーから発信された行が含まれています。

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`-@granularity 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`-@granularity AND `g`.`tm`
    )
GROUP BY `f`.`id`

最近更新されたグループが常に先頭にジャンプします ( groupDESC で並べ替えた場合)。ただし、グループを永続的にしたい場合 (たとえば、アイテムがあるグループから別のグループに移動しないようにする場合) は、MIN代わりにMAX次を使用します。

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`+@granularity
    )
GROUP BY `f`.`id`

次に、テーブルのgroup列を更新します。まず、MySQL は読み取り元と同じテーブルを更新できません。一時テーブルが必要です。group2 番目:列が 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`-@granularity 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

于 2012-05-13T19:41:55.510 に答える
1

「o_O Tync」による解決策では、アイテムが追加された場合、1 時間以内にアイテムがグループ化されません (例: 1:00、1:40、2:30)。最後の 2 つだけがグループ化されます。

これは、(同じテーブルの) 一時テーブルと結合を使用しない超高速の Mysql ソリューションです。

CREATE TABLE `フィード`(
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `tm` INT UNSIGNED NOT NULL COMMENT 'timestamp',
  `user_id` INT UNSIGNED NOT NULL COMMENT '作成者ID',
  `image` VARCHAR(255) NOT NULL COMMENT '投稿された画像ファイル名',
  `group` INT UNSIGNED NULL DEFAULT NULL COMMENT 'post group',
  PRIMARY KEY(`id`),
  INDEX(`user_id`),
  INDEX(`tm`,`グループ`)
  );


SET @粒度:=60*60;
UPDATE フィード f CROSS JOIN (
  選択する
    g.id、
    @id:=COALESCE( IF( ISNULL(@prev_date) OR (user_id!=@prev_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, フィード g
  ORDER BY user_id DESC、tm DESC
) z USING (id)
SET f.group = z.group_id;

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

于 2012-08-24T13:20:35.203 に答える