Incrementing multi-column sequence in PostgreSQL

multiple-columns, postgresql, sequences

Solution

You can add simlpe `SERIAL` column to your table (it will give you the order for things) and then use something like:

SELECT *, row_number() OVER (PARTITION BY month ORDER BY serial_column)
FROM table

This will give you the results you want.

If you do not need to order the rows, you can try:

SELECT *, row_number() OVER (PARTITION BY month)
FROM table

Details here : `row_number() OVER(...)`

UPD How it works:

A column with type `SERIAL` is essentially an "auto increment" field. It automatically get a value from a sequence. When you insert rows to the table they will look like this:

| MONTH | SERIAL_COLUMN |                     DESCRIPTION |
-----------------------------------------------------------
|     1 |             1 |                       One thing |
|     1 |             2 |                   Another thing |
|     1 |             3 |          Last task of the month |
|     2 |             4 |           First of second month |
|     2 |             5 | Second and last of second month |
|     3 |             6 |            First of third month |

The key thing - every next added row has value of `SERIAL_COLUMN` greater than all previous rows.

Next. The `row_number() OVER (PARTITION BY month ORDER BY serial_column)` does:

1) Partition all the rows into groups with equal `month` (`PARTITION BY month`)

2) Orders them by value of `serial_column` (`ORDER BY serial_column`)

3) In every group assigns a row number using the ordering from step 2 (``row_number() OVER`)

The output is:

| MONTH | SERIAL_COLUMN |                     DESCRIPTION | ROW_NUMBER |
------------------------------------------------------------------------
|     1 |             1 |                       One thing |          1 |
|     1 |             2 |                   Another thing |          2 |
|     1 |             3 |          Last task of the month |          3 |
|     2 |             4 |           First of second month |          1 |
|     2 |             5 | Second and last of second month |          2 |
|     3 |             6 |            First of third month |          1 |

To change the output of the `row_number()` you need to change the values in `SERIAL_COLUMN`. Fro example, to place `Second and last of second month` before `First of second month` a will change the values of `SERIAL_COLUMN` like that:

UPDATE Table1
SET serial_column = 5
WHERE description = 'First of second month';

UPDATE Table1
SET serial_column = 4
WHERE description = 'Second and last of second month';

It will change the output of the query:

| MONTH | SERIAL_COLUMN |                     DESCRIPTION | ROW_NUMBER |
------------------------------------------------------------------------
|     1 |             1 |                       One thing |          1 |
|     1 |             2 |                   Another thing |          2 |
|     1 |             3 |          Last task of the month |          3 |
|     2 |             4 | Second and last of second month |          1 |
|     2 |             5 |           First of second month |          2 |
|     3 |             6 |            First of third month |          1 |

The exact values in `SERIAL_COLUMN` do not matter. They only set an order on the tasks in a month.

My SQLFiddle example is here.

Problem

Is there any built-in way (I mean, without need of triggers and/or functions) to have incrementing indexes per multiple columns? So after performing: ``` INSERT INTO "table" ("month", "desc") VALUES (1, 'One thing') , (1, 'Another thing') , (1, 'Last task of the month') , (2, 'Last task of the month') , (2, 'Last task of the month') , (3, 'First of third month') ``` My table would end up like this (note the "task" column): ``` month task desc 1 1 One thing 1 2 Another thing 1 3 Last task of the month 2 1 First of second month 2 2 Second and last of second month 3 1 First of third month ```

Original source