简体   繁体   中英

UNION ALL -> ORDER BY not working as expected …?

I have the following SQL-Statement:

  (SELECT animation_id,
          animation_name,
          animation_group
   FROM animations
   WHERE animation_id = '45')
UNION ALL
  (SELECT animation_id,
          animation_name,
          animation_group
   FROM animations
   WHERE animation_id <> '45'
     AND active = TRUE
   ORDER BY animation_group,
            animation_name)

The table looks like this:

表

The sorting is not OK. I wanted that the data is being sorted by the column "animation_group", but the result is sorted after "animation_id" I think. Look at the screenshot, which shows the output:

排序

If it is relevant: It is MySQL. The Statement is beeing called through PHP.

Is the SQL-Statement correct?

Thank you!

EDIT: The base of my request or lets say the reason why I use the UNION is the following:

The result of the first select should always be the first row. The result of the second select should be ordered by. Therefore I don't want to order the whole result.

First of all there is no group by rather the issue is with order by . Second, don't see why you need a UNION here. You can change your query to be like below using a OR condition probably

SELECT animation_id, animation_name, animation_group
FROM animations
WHERE animation_id = '45'
OR (animation_id <> '45' and active = true)
ORDER BY animation_group, animation_name;

Manually add a sort ordering for the selected item:

SELECT
  animation_id, 
  animation_name, 
  animation_group,
  CASE WHEN animation_id = '45' THEN 1 ELSE 0 END AS is_selected
FROM 
  animations
WHERE 
  animation_id = '45' OR (animation_id <> '45' AND active = true)
ORDER BY 
  is_selected DESC,
  animation_group, 
  animation_name

If you still want to keep your UNION for some reason:

SELECT 
  animation_id, 
  animation_name, 
  animation_group,
  1 AS is_selected
FROM 
  animations
WHERE 
  animation_id = '45'
UNION ALL -- If animation_id is unique, I can't see why you'd need a UNION ALL here, by the way 
SELECT 
  animation_id, 
  animation_name, 
  animation_group,
  0 AS is_selected
FROM 
  animations
WHERE
  animation_id <> '45' AND 
  active = true
ORDER BY
  is_selected DESC,
  animation_group, 
  animation_name

Also, assuming animation_id is a unique integer, 1) you don't need UNION ALL, as each row selected will be unique anyway, and 2) you don't need the quotes around the values, ie just animation_id = 45 will work, rather than animation_id = '45' .

try this

SELECT * FROM (
  SELECT animation_id, animation_name, animation_group
  FROM animations
  WHERE animation_id = '45'
UNION ALL 
  SELECT animation_id, animation_name, animation_group
  FROM animations
  WHERE animation_id <> '45' and active = true
) as tmp_table
ORDER BY animation_group, animation_name;

Just don't enclose the order by inside parentheses. Doing so, performs the order by for the second query only and not the result. Moreover, UNION is free to reorder results. An order by after one or more UNION applies to the result of the unions, but only if you don't change its scope using parentheses. Your query will work if you remove parentheses.

"UNION ALL -> ORDER BY not working as expected" -- indeed, it's not working as expected, it is working as documented :

Use of ORDER BY for individual SELECT statements implies nothing about the order in which the rows appear in the final result because UNION by default produces an unordered set of rows. Therefore, the use of ORDER BY in this context is typically in conjunction with LIMIT , so that it is used to determine the subset of the selected rows to retrieve for the SELECT , even though it does not necessarily affect the order of those rows in the final UNION result. If ORDER BY appears without LIMIT in a SELECT , it is optimized away because it will have no effect anyway.

To use an ORDER BY or LIMIT clause to sort or limit the entire UNION result, parenthesize the individual SELECT statements and place the ORDER BY or LIMIT after the last one.

Following the documentation advice, your query should be:

(
    SELECT animation_id, animation_name, animation_group
    FROM animations
    WHERE animation_id = '45'
)
UNION ALL 
(
    SELECT animation_id, animation_name, animation_group
    FROM animations
    WHERE animation_id <> '45' and active = true
)
ORDER BY animation_group, animation_name

More, your query selects the record that have either animation_id = 45 OR active = true and therefore it can be written as:

SELECT animation_id, animation_name, animation_group
FROM animations
WHERE animation_id = '45' OR active = true
ORDER BY animation_group, animation_name

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