ORDER BY 更改了具有相同名称的列
ORDER BY changed column with same name
我有一个 table artist
,其中包含多个列,例如 name
、type
...
我希望返回 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:新列具有相同的名称 => 列 name
在 order 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)
.
ILIKE
比 lower()
/ upper() LIKE ...
.
更简单
你不需要 concat()
, the plain concatenation operator ||
做同样的事情更快,因为表达式 right(name, -4)
保证 是 NOT NULL
在这种情况下。
当与列名的 SQL 可见性规则混淆时,或者当有 重复的输出列名时 (这在 Postgres 中是完全合法的)您还可以在 GROUP BY
、ORDER BY
或 DISTINCT 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>
我有一个 table artist
,其中包含多个列,例如 name
、type
...
我希望返回 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:新列具有相同的名称 => 列 name
在 order 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)
.ILIKE
比lower()
/upper() LIKE ...
. 更简单
你不需要
concat()
, the plain concatenation operator||
做同样的事情更快,因为表达式right(name, -4)
保证 是NOT NULL
在这种情况下。当与列名的 SQL 可见性规则混淆时,或者当有 重复的输出列名时 (这在 Postgres 中是完全合法的)您还可以在
GROUP BY
、ORDER BY
或DISTINCT 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>