簡體   English   中英

MySQL:如何對每個WHERE過濾器進行限制查詢

[英]MySQL: How to do a query with a limit for each WHERE filter

我需要執行按ID過濾的查詢,但是我需要限制每個ID的結果。 我不知道如何在一個查詢中執行此操作,我只能用PHP循環每個ID,對每個迭代進行查詢,然后將結果合並在一起:

$allResults = [];
$ids = [1, 2, 3, 4, 5];

foreach ($ids as $id) {

    $db->query("
        SELECT *
        FROM example_table
        WHERE example_id = $id
        ORDER BY rating DESC
        LIMIT 2
    ");
    $results = $db->getResults();

    $allResults = array_merge($allResults, $results);
}

上面的代碼滿足了我的需要,但是我將如何在一個查詢中而不是5個查詢循環中執行此操作?

我正在尋找這樣的東西:

    SELECT *
    FROM example_table
    WHERE 
        example_id = 1 LIMIT 2 OR
        example_id = 2 LIMIT 2 OR
        example_id = 3 LIMIT 2 OR
        example_id = 4 LIMIT 2 OR
        example_id = 5 LIMIT 2
    ORDER BY rating DESC

顯然,where子句中的限制會引發錯誤,但是您可以看到我正在嘗試實現的目標。 任何幫助表示贊賞。

正如@Strawberry提到的那樣,如果沒有可驗證的示例和數據,很難給出正確的答案,但這就是查詢的樣子,因為mysql還不支持組排名功能。

SELECT * FROM
(SELECT *, 
    @f_rank := IF(@f_rating = rating AND @f_id = id, @f_rank + 1, 1) AS rank, 
    @f_rating := rating,
    @f_id := id
FROM   example_table 
WHERE id IN (1, 2, 3, 4, 5)
ORDER  BY id, rating DESC) tmp_table
WHERE tmp_table.rank <= 2

您需要在此處對每個ID將所有評級從1排到n。 然后選擇前兩個ID。

您可以使用以下UNION查詢:

(SELECT * FROM example_table WHERE id = 1 ORDER BY rating DESC LIMIT 2)
UNION ALL
(SELECT * FROM example_table WHERE id = 2 ORDER BY rating DESC LIMIT 2)
UNION ALL
(SELECT * FROM example_table WHERE id = 3 ORDER BY rating DESC LIMIT 2)
UNION ALL
(SELECT * FROM example_table WHERE id = 4 ORDER BY rating DESC LIMIT 2)
UNION ALL
(SELECT * FROM example_table WHERE id = 5 ORDER BY rating DESC LIMIT 2)

這是在PHP中創建它的一種方法:

$ids = [1, 2, 3, 4, 5];

$sqlArray = array_map(function($id){
    $id = (int)$id;
    return "(SELECT * FROM example_table WHERE id = $id ORDER BY rating DESC LIMIT 2)";
}, $ids);

$sql = implode("\nUNION ALL\n", $sqlArray);

演示: http//rextester.com/GNDR46232

您還應該考慮使用占位符准備查詢並綁定ID。 由於我不知道$db的類,這里是PDO方法:

$ids = [1, 2, 3, 4, 5];

$sqlArray = array_map(function($id){
    return "(SELECT * FROM example_table WHERE id = ? ORDER BY rating DESC LIMIT 2)";
}, $ids);

$sql = implode("\nUNION ALL\n", $sqlArray);

$stmt = $db->prepare($sql);
$stmt->execute($ids);
$allResults = $stmt->fetchAll(PDO::FETCH_ASSOC);

它比我想象的要復雜一些,但是您應該能夠相當容易地在PHP中以編程方式創建查詢。 您甚至不能對UNION進行直接限制,但可以有效地對具有最低級別限制的選擇進行選擇。 這是否比執行5個單獨的查詢更有效,尚不得而知。

SELECT *
      FROM (SELECT *
            FROM example_table
            WHERE id = 1
            LIMIT 2) AS id1
      UNION
      SELECT *
      FROM (SELECT *
            FROM example_table
            WHERE id = 2
            LIMIT 2) AS id2
      UNION
      SELECT *
      FROM (SELECT *
            FROM example_table
            WHERE id = 3
            LIMIT 2) AS id3
      .... more UNIONS ....;

(您有一個不唯一的名為“ id”的屬性,這有點令人困惑)

基於聯合的方法都意味着對查詢中的example_id值進行硬編碼,並且無法很好地擴展到大量的example_id。

OTOH當僅查看值的子集時,將進行相對有效的查詢。

這是另一種方法...

SELECT *
FROM
(
SELECT id, max(rating) as scndrt
FROM example_table scnd
INNER JOIN (
   SELECT id, max(rating) maxrt
   FROM example_table
   GROUP BY id) AS first
 ON scnd.id=first.id
 WHERE scnd.rating<first.maxrt
 ) AS rts
 INNER JOIN example_table thrd
 ON rts.id=thrd.id
 AND thrd.rating IN (rts.maxrt, rts.scndrt)

這將不會返回僅包含一條記錄的ID,如果(例如)對於某個ID最高評價出現兩次,則將返回多於2條記錄。 它還需要多次傳遞數據。

我認為也可能使用查詢中的變量來解決原始問題,例如...。

 SELECT @rank:=1+IF(id=@previd, @rank, 0) as ranking,
 @previd=id AS currentid,
 FROM (
   SELECT *
   FROM example_table
   ORDER BY id, rating DESC
 ) AS src
 HAVING ranking<3

我沒有使用過這樣的變量進行測試,因此可能必須使用游標將上述選擇展開到過程中,並將匹配的行寫入臨時表中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM