最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

postgresql - Create an index with concatenation of text fields, taking into account their lengths - Stack Overflow

programmeradmin1浏览0评论

I know that I can create an index that uses concatenation as follows:

CREATE INDEX idx_table1_field1_field2 ON table1((field1 || field2));

But this is not quite what I need. I will try to explain exactly what kind of index I need. Let's assume there is a table (let's call it table1) that contains two fields (field1 and field2) of the character data type, each with a length of 15.

+----------------+----------------+
| field1         | field2         |
| character (15) | character (15) |
+----------------+----------------+
| James          | Black          |
+----------------+----------------+
| Michael        | White          |
+----------------+----------------+
| David          | Silver         |
+----------------+----------------+

Now if I execute the following SQL query:

SELECT field1 || field2, CONCAT(field1, field2) FROM table1

I get the following result:

+------------------+---------------------------------+
|  ?column?        | concat                          |
|  text            | text                            |
+------------------+---------------------------------+
| JamesBlack       | James          Black            |
+------------------+---------------------------------+
| MichaelWhite     | Michael        White            |
+------------------+---------------------------------+
| DavidSilver      | David          Silver           |
+------------------+---------------------------------+

If I use two vertical bars (||), the server does not check that the name James has 5 characters and does not add 10 spaces to the field length (15). If I use the CONCAT function, I see that the field length is not ignored and the server added 10 spaces to the name James.

I desire to create an index that will take the field length into account (how CONCAT does this).

To create the desired index, I tried to execute the following SQL query:

CREATE INDEX idx_table1_field1_field2 ON table1(CONCAT(field1, field2));

But I got the following error: ERROR: functions in index expression must be marked IMMUTABLE

Is there another way to create the such index?

I know that I can create an index that uses concatenation as follows:

CREATE INDEX idx_table1_field1_field2 ON table1((field1 || field2));

But this is not quite what I need. I will try to explain exactly what kind of index I need. Let's assume there is a table (let's call it table1) that contains two fields (field1 and field2) of the character data type, each with a length of 15.

+----------------+----------------+
| field1         | field2         |
| character (15) | character (15) |
+----------------+----------------+
| James          | Black          |
+----------------+----------------+
| Michael        | White          |
+----------------+----------------+
| David          | Silver         |
+----------------+----------------+

Now if I execute the following SQL query:

SELECT field1 || field2, CONCAT(field1, field2) FROM table1

I get the following result:

+------------------+---------------------------------+
|  ?column?        | concat                          |
|  text            | text                            |
+------------------+---------------------------------+
| JamesBlack       | James          Black            |
+------------------+---------------------------------+
| MichaelWhite     | Michael        White            |
+------------------+---------------------------------+
| DavidSilver      | David          Silver           |
+------------------+---------------------------------+

If I use two vertical bars (||), the server does not check that the name James has 5 characters and does not add 10 spaces to the field length (15). If I use the CONCAT function, I see that the field length is not ignored and the server added 10 spaces to the name James.

I desire to create an index that will take the field length into account (how CONCAT does this).

To create the desired index, I tried to execute the following SQL query:

CREATE INDEX idx_table1_field1_field2 ON table1(CONCAT(field1, field2));

But I got the following error: ERROR: functions in index expression must be marked IMMUTABLE

Is there another way to create the such index?

Share Improve this question edited Feb 6 at 6:15 Ivan Yuzafatau asked Feb 5 at 22:11 Ivan YuzafatauIvan Yuzafatau 6085 silver badges13 bronze badges 5
  • If you don't want trailing spaces why are you using character(n) in the first place? 15 is the field length if you choose character. If that's not what you want, use varchar like everyone else. – Richard Huxton Commented Feb 5 at 23:05
  • I second Richard's comment, character is a red flag. You almost always want text. See postgresql.org/docs/current/datatype-character.html – Schwern Commented Feb 5 at 23:25
  • 1 This question is similar to: Why am I getting a an error when creating a generated column in PostgreSQL?. Specifically he mentions making an immutable version of concat. Or don't use char. If you really need the padding, pad it yourself. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. – Schwern Commented Feb 5 at 23:34
  • @Schwern But if I use pad, I have to specify the length. I would like to find a solution without explicitly specifying the length. What other data type should I try? – Ivan Yuzafatau Commented Feb 6 at 6:09
  • @Schwern And thank you for the link. Now I'm examining the essence of that question. There is probably a similar problem. – Ivan Yuzafatau Commented Feb 6 at 6:34
Add a comment  | 

2 Answers 2

Reset to default 3

You can use RPAD() in this, to fill it up with whitespace (the default):

CREATE TABLE bar(field1 char(15), field2 char(15));

CREATE INDEX ON bar ((rpad(field1,15) || rpad(field2, 15)));

INSERT INTO bar(field1, field2) VALUES ('a', 'b');

EXPLAIN(ANALYSE , VERBOSE , BUFFERS , SETTINGS )
SELECT * FROM bar WHERE (rpad(field1,15) || rpad(field2, 15)) = 'a              b              ';

It's important that you write the WHERE condition just like the index definition, otherwise the index can't be used.

Unless you definitely want fixed length fields, and your example uses names so you probably don't, don't use the character type. Use text, or if you have a specific reason to limit your data length, varchar.

From the PostgreSQL docs...

There is no performance difference among these three types, apart from increased storage space when using the blank-padded type [character], and a few extra CPU cycles to check the length when storing into a length-constrained column. While character(n) has performance advantages in some other database systems, there is no such advantage in PostgreSQL; in fact character(n) is usually the slowest of the three because of its additional storage costs. In most situations text or character varying [varchar] should be used instead.

The padding isn't a property of the data, it's formatting. If you really want that padding, use text and add it yourself with rpad.

create index idx_format on table1((rpad(field1, 15) || rpad(field2, 15)));

Or try to make your own immutable character concat.


To explain || vs concat we reference 9.4 String Functions And Operators.

Except where noted, these functions and operators are declared to accept and return type text. They will interchangeably accept character varying arguments. Values of type character will be converted to text before the function or operator is applied, resulting in stripping any trailing spaces in the character value.

|| is defined to take text and returned text, text || text → text. It will cast your char to text losing the trailing whitespace. concat (which is non-standard) is defined to take any type and return text, concat ( val1 "any" [, val2 "any" [, ...] ] ) → text.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论