ORDER BY 更改了具有相同名称的列

ORDER BY changed column with same name

我有一个 table artist,其中包含多个列,例如 nametype ...
我希望返回 name 列并按字母顺序排列,但应调整列名。如果艺术家的名字以 'the ' 开头(不区分大小写),那么它应该被删除并放在大写的名字后面,例如 'the Beatles' -> 'Beatles, THE'

这是我的代码(不起作用):

SELECT name,
  CASE
    WHEN UPPER(name) LIKE 'THE %' THEN CONCAT(RIGHT(name, length(name)-4),', THE')
  END AS name 
FROM artist
ORDER BY name

问题 1:name 没有被替换,它创建了一个新列。

问题 2:新列具有相同的名称 => 列 nameorder by name 中不明确。

如何轻松解决这些问题?

CASE中使用ELSE子句:

SELECT
  CASE
    WHEN UPPER(name) LIKE 'THE %' THEN CONCAT(RIGHT(name, length(name)-4),', THE')
    ELSE name
  END AS name 
FROM artist
ORDER BY name

如果你只想要这个用于订购,那么你可以将逻辑只放在 order by 子句中:

order by (case when name ilike 'the%'
               then substring(name from 5) || ', The'
               else name
          end)

如果您还想在 select 子句中使用此列,请使用 name 以外的别名。这已经用于您的第一列:

select name,
       (case when name ilike 'the%'
             then substring(name from 5) || ', The'
             else name
        end) as munged_name
. . .
order by munged_name

注意:ilike 是不区分大小写的 Postgres 扩展 like

查询

SELECT CASE WHEN name ILIKE 'THE %'  -- simpler
            THEN right(name, -4) || ', THE'  -- simpler, faster
            ELSE name END AS name  -- but better use a distinct alias
      , *
FROM   artist
ORDER  BY 1;  -- positional reference to 1st output column

理由

  • 得到字符串减去 n个前导字符最简单最快的表达式是:right(string, -n).

  • ILIKElower() / upper() LIKE ....

  • 更简单
  • 你不需要 concat(), the plain concatenation operator || 做同样的事情更快,因为表达式 right(name, -4) 保证NOT NULL 在这种情况下。

  • 当与列名的 SQL 可见性规则混淆时,或者当有 重复的输出列名时 (这在 Postgres 中是完全合法的)您还可以在 GROUP BYORDER BYDISTINCT ON (...) 子句中使用 位置引用

    • When can we use an identifier number instead of its name in PostgreSQL?
    • How to re-use result for SELECT, WHERE and ORDER BY clauses?

    但是,一开始就重复输出列名称既不明智也不有用。而是使用不同的名称。

  • 如果你 运行 这个查询很多,我建议在同一个表达式上使用 functional index 以获得更快的结果:

    CREATE INDEX artist_name_sort_idx ON artist 
     ((CASE WHEN name ILIKE 'THE %' THEN right(name, -4) || ', THE' ELSE name END));
    

查询中的表达式必须相同才能使用该索引。

正确的测试用例

WITH artist(artist_id, name) AS (
   VALUES
      (1, 'The Beatles')   
    , (2, 'tHe bEatles')
    , (3, 'The The')
    , (4, 'Then')
    , (5, 'The X')
    , (6, 'Theodor')
    , (7, 'Abba')
    , (8, 'ZZ TOP')
    , (9, 'The ')  -- unlikely corner case, I don't think it would pay to test for it
    , (10, '')      -- empty string
    , (11, NULL)   -- NULL
   )
SELECT CASE WHEN name ILIKE 'THE %' THEN right(name, -4) || ', THE' ELSE name END AS name
      , *
FROM   artist
ORDER  BY 1;

结果:

     name     | artist_id |    name
--------------+-----------+-------------
              |        10 |
 , THE        |         9 | The           -- remaining corner case
 Abba         |         7 | Abba
 bEatles, THE |         2 | tHe bEatles
 Beatles, THE |         1 | The Beatles
 The, THE     |         3 | The The
 Then         |         4 | Then
 Theodor      |         6 | Theodor
 X, THE       |         5 | The X
 ZZ TOP       |         8 | ZZ TOP
 <NULL>       |        11 | <NULL>