简体   繁体   中英

Preserve order of SQL WHERE IN() clause with nested SELECT

I need a list of item names ordered by count of items. Item names and corresponing id's are stored in tabletwo while tableone refers to items by id's:

      tableone              tabletwo
+--------+-----------+    +----+------+
| itemid | condition |    | id | name |
+--------+-----------+    +----+------+
| 2      | satisfied |    | 1  | foo  |
+--------+-----------+    +----+------+
| 1      | satisfied |    | 2  | bar  |
+--------+-----------+    +----+------+
| 3      | satisfied |    | 3  | hurr |
+--------+-----------+    +----+------+
| 3      | satisfied |    | 4  | durr |
+--------+-----------+    +----+------+
| 3      | satisfied |
+--------+-----------+
| 4      | satisfied |
+--------+-----------+
| 4      | satisfied |
+--------+-----------+
| 3      | nope      |
+--------+-----------+
| 1      | satisfied |
+--------+-----------+

SQL code:

SELECT `itemname` FROM `tabletwo` WHERE `id` IN (
    SELECT `itemid` FROM (
        SELECT count(`itemid`), `itemid`
        FROM `tableone`
        WHERE `some_codition`="satisfied"
        GROUP BY `itemid`
        ORDER BY count(`itemid`) DESC
    ) alias
)

The nested SELECT returns a list of item id's in descendant order: 3, 4, 1, 2 . This list is then used as an argument of an IN() clause. The expected result of the whole query is: hurr, durr, foo, bar (in this exact order). But the order is not preserved. I know it can be done like this: ORDER BY FIELD(id, 3, 4, 1, 2) but I don't know how to do this trick when the ordered list is fetched dynamically like in my case. Do I need to SELECT it again? Or temporary table maybe? Or is it better to build another query outside SQL?

Use JOIN instead of IN :

SELECT
    t2.name
FROM tabletwo t2
LEFT JOIN tableone t1
    ON t1.itemid = t2.id
    AND t1.`condition` = 'satisfied'
GROUP BY
    t2.id, t2.name
ORDER BY COUNT(*) DESC

If you want to exclude rows from tabletwo that do not have a match on tableone , use INNER JOIN instead of LEFT JOIN .

ONLINE DEMO

Try using JOIN instead:

SELECT t2.`itemname` 
FROM `tabletwo` AS t2
JOIN (    
   SELECT count(`itemid`) AS cnt, `itemid`
   FROM `tableone`
   WHERE `some_codition`="satisfied"
   GROUP BY `itemid`
) AS t1 ON t1.`itemid` = t2.`id`
ORDER BY t1.cnt DESC

You can create a derived table using the subquery of the IN operator and perform a JOIN to this table, so that you are able to use the COUNT in the ORDER BY clause of the main query.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM