2013-04-25 133 views
3

我有以下表格:SQL從n個組獲得均勻分佈 - 獲取隨機物品

TABLE product 
id int(11) 
title varchar(400) 

TABLE tag 
id int(11) 
text varchar(100) 

TABLE product_tag_map 
product_id int(11) 
tag_id int(11) 

PRODUCT_TAG_MAP地圖標籤產品。系統中標籤的分佈是不正常的,即一些標籤比其他標籤有更多的產品。

我想寫一個SQL,將獲取25個隨機產品:每個標籤5個產品,5個標籤(所以這是5x5 = 25)。

在這裏找到答案:How can I get an even distribution using WHERE id IN(1,2,3,4)

但這並不產生隨機的產品 - 它總是讀取每個標籤相同的產品。

這裏是SQL我:

SET @last_tag = 0; 
SET @count_tag = 0; 

SELECT DISTINCT id FROM (
SELECT 
    product.*, 
    @count_tag := IF(@last_tag = product_tag_map.tag_id, @count_tag, 0) + 1 AS tag_row_number, 
    @last_tag := product_tag_map.tag_id 
FROM product 
    LEFT JOIN product_tag_map ON (product_tag_map.product_id=product.id) 
WHERE 
    product_tag_map.tag_id IN (245,255,259,281,296) 
) AS subquery WHERE tag_row_number <= 5; 

如何讓每個標籤返回隨機的產品?

任何幫助將不勝感激!謝謝。

回答

2

有在此查詢了不少招數:

  1. 添加子查詢嵌套使用LIMIT的水平:mySQL subquery limit
  2. 添加ROW_NUMBER功能爲MySQL:How to select the first/least/max row per group in SQL

最終的結果是很多子查詢:

SELECT tag.Name, t0.Id as MapId 
FROM 
(
    SELECT * 
     , @num := if(@type = tag_id, @num + 1, 1) as row_number 
     , @type := tag_id as dummy 
    FROM (
     SELECT * 
     FROM map m 
     WHERE tag_id in 
     (
      SELECT * 
      FROM 
      (
       SELECT id 
       FROM tag 
       ORDER BY RAND() LIMIT 5 
      ) t 
     ) 
    ORDER BY tag_id, RAND() 
) mainTable 
    , (SELECT @num:=0) foo 
    , (SELECT @type:=0) foo2 
) t0 
    INNER JOIN tag 
     ON t0.tag_id = tag.id 
WHERE row_number <= 5 

SQL Fiddle

這個想法是選擇前5個隨機標籤。這並不難,只是一個簡單的ORDER BY RAND() LIMIT 5

然後棘手的部分太模擬ROW_NUMBER() OVER(PARTITION BY tag_id, RAND()),因爲隨機排列每個項目,但標記分區正是你需要的。所以你聲明變量並按查詢顯示。

最後,過濾row_number,你有你的25個隨機項目!

+0

謝謝!那就是訣竅。我添加了一個INNER JOIN到content_item,這樣我就可以返回content_item.id字段(這就是我以後的)。完善! – 2013-04-25 15:56:07

0

我也想提供「蠻力」的方法。這將適用於大多數數據庫(儘管rand()函數可能被命名爲其他)。

select content_item_id from content_item where tag_id = 245 order by RAND() limit 5 
union all 
select content_item_id from content_item where tag_id = 255 order by RAND() limit 5 
union all 
select content_item_id from content_item where tag_id = 259 order by RAND() limit 5 
union all 
select content_item_id from content_item where tag_id = 281 order by RAND() limit 5 
union all 
select content_item_id from content_item where tag_id = 206 order by RAND() limit 5 

這個性能可能是好的,如果你在content_item(tag_id)有一個索引。

+0

謝謝。我原來的解決方案包括工會,但這是非常重要的性能。 – 2013-04-25 15:57:04

+0

@MotiRadomski。 。 。如果您擁有正確的索引,性能可能會好。否則,您需要爲每個子查詢執行一次單獨的表掃描,以消除性能。 – 2013-04-25 16:03:49

+0

我明白了。好,謝謝! – 2013-04-25 16:12:22