Home > database >  ORDER BY field from a comma separated subquery
ORDER BY field from a comma separated subquery

Time:05-14

I'm trying to order a query based on the comma separated result of another query

SELECT t.field1, t.field2 /*, ...*/
FROM table t
/* ... */
ORDER BY 
    CASE WHEN NOT EXISTS (SELECT 1 FROM table1 t1 WHERE t1.field1sub = t.field1) 
        THEN FIELD(t.field2, (SELECT field_order FROM table1 t1 WHERE t1.field1sub = t.field1))
        ELSE FIELD(t.field2, (SELECT field_order FROM table1 t1 WHERE t1.field1sub = 0))
    END

The important part of the query is this

FIELD(t.field2, (SELECT field_order FROM table1 t1 WHERE t1.field1sub = t.field1))

So basically, the subquery returns a comma separated string 1,2,3,7,9,4,10, and I want to order by those fields first.

I know we can do for example ORDER BY FIELD(t.field,2,1,2,3), but how can we use a subquery that returns a string, to fill the order field?

CodePudding user response:

There is no neeed for dynamic sql.

you can doit with FIND_IN_SET

But still this is slower as when you had a normalized Structure.

and fyi you should read Is storing a delimited list in a database column really that bad?

A simplifieed version of your query to demonstarte that it works

CREATE TABLE t1 (Sortder varchar(100), id int)
INSERT INTO t1 VALUES ('1,2,3,4',0),('4,3,2,1',1)
CREATE TABLe t2 (f1 int, val varchar(10))
INSERT INTO t2 VALUES (1,'a'),(2,'b'),(3,'c'),(4,'d')
SELECT * FROM t2
ORDER BY
CASE WHEN  4 = 5
THEN  find_in_set( f1, (SELECT Sortder FROM t1 WHERE id = 0) )
ELSE find_in_set( f1, (SELECT Sortder FROM t1 WHERE id = 1) )
END
f1 | val
-: | :--
 4 | d  
 3 | c  
 2 | b  
 1 | a  
SELECT * FROM t2
ORDER BY
CASE WHEN  5 = 5
THEN  find_in_set( f1, (SELECT Sortder FROM t1 WHERE id = 0) )
ELSE find_in_set( f1, (SELECT Sortder FROM t1 WHERE id = 1) )
END
f1 | val
-: | :--
 1 | a  
 2 | b  
 3 | c  
 4 | d  

db<>fiddle here

CodePudding user response:

The function that you can use to locate a value in a comma separated list of values is FIND_IN_SET().

You don't need the CASE expression and EXISTS:

ORDER BY FIND_IN_SET(
           t.field2,
           COALESCE(
             (SELECT field_order FROM table1 t1 WHERE t1.field1sub = t.field1),
             (SELECT field_order FROM table1 t1 WHERE t1.field1sub = 0)
           )
         ) 

When there is no row in table1 satisfying the condition of the 1st subquery:

SELECT field_order FROM table1 t1 WHERE t1.field1sub = t.field1

then its result is null in which case COALESCE() will return the result of the 2nd subquery:

SELECT field_order FROM table1 t1 WHERE t1.field1sub = 0

Another way to write the ORDER BY clause would be:

ORDER BY FIND_IN_SET(
           t.field2,
           (
             SELECT field_order 
             FROM table1 t1 
             WHERE t1.field1sub IN (t.field1, 0) 
             ORDER BY t1.field1sub = 0 LIMIT 1
           )
         ) 

In this case, the subquery:

SELECT field_order 
FROM table1 t1 
WHERE t1.field1sub IN (t.field1, 0) 
ORDER BY t1.field1sub = 0 LIMIT 1 

returns the row with t1.field1sub = t.field1 if it exists, but if it does not exist it returns the row with t1.field1sub = 0.

  • Related