2012-12-05 15 views
7

Czy istnieje jakiś wbudowany sposób (tj. Bez potrzeby wyzwalaczy i/lub funkcji), aby inkrementować indeksy na wiele kolumn?Zwiększanie sekwencji wielokolumnowej w PostgreSQL

Więc po wykonaniu:

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') 

Moja tabela skończy się tak (uwaga na "zadanie" kolumna):

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 

Odpowiedz

12

Możesz dodać simlpe SERIAL kolumnę do tabeli (to będzie wydać zamówienie na rzeczy), a następnie użyć czegoś takiego:

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

To da ci e wyniki, które chcesz.

Jeśli nie trzeba zamówić wiersze, można spróbować:

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

Szczegóły tutaj: row_number() OVER(...)

UPD Jak to działa:

Kolumna z rodzaju SERIAL jest zasadniczo pole "auto increment". Automatycznie pobiera wartość z sekwencji. Podczas wstawiania wierszy do tabeli będą wyglądać następująco:

| 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 | 

Kluczową sprawą - każdy następny dodany wiersz ma wartość SERIAL_COLUMNwiększa niż wszystkich poprzednich wierszach.

Następny. row_number() OVER (PARTITION BY month ORDER BY serial_column) służy:

1) podsystemu wszystkie wiersze do grupy o równej month (PARTITION BY month)

2) Każda ich wartości serial_column (ORDER BY serial_column)

3) w każdej grupie przyporządkowuje się numer wiersza stosując zamawiania z etapu 2 (`row_number() OVER)

wyjście jest:

| 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 | 

Aby zmienić wyjście row_number(), należy zmienić wartości w SERIAL_COLUMN. Fro przykład, aby umieścić Second and last of second month przed First of second month się zmieni wartości SERIAL_COLUMN tak:

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'; 

To będzie zmienić wyjście zapytania:

| 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 | 

Dokładne wartości w SERIAL_COLUMN nie mają znaczenia. Ustawiają one tylko zamówienia na zadania w ciągu miesiąca.

Mój przykład SQLFiddle to here.

+0

Jest to sposób, aby to zrobić. W środowisku wielodostępnym * bardzo trudno * wymusić bezpodstawną numerację bez tworzenia ** warunków wyścigu **. Będziesz musiał mocno korzystać z blokad stołowych, co jest prawdziwym pluginem dla wydajności. Proponuję użyć 'row_number()' zamiast 'rank()'. Wynik jest taki sam przy braku rówieśników (jak w tym przypadku), ale 'row_number()' jest tańszą i bardziej odpowiednią funkcją. –

+0

@ErwinBrandstetter Dziękuję za 'row_number()'. Edytowałem zapytania. –

+0

Czy mogę później zmienić kolejność zadań w ciągu miesiąca? – Mario

4

Jeśli chcesz złamać instrukcje INSERT w jednym wierszu danych na insert, możesz użyć PostgreSQL rules. Poniższy przykład jest nieco skomplikowany, ponieważ reguły nie pozwalają ci przekierować zapisu do samej relacji. Zwykle odbywa się to za pomocą wyzwalaczy. Ale widzimy, jeśli jest to możliwe bez wyzwalaczy, tak tu idzie:

--drop table table_data cascade; 
CREATE TABLE table_data (
    month integer not null, 
    task integer not null, 
    "desc" text 
); 
ALTER TABLE table_data add primary key (month, task); 

CREATE VIEW "table" as 
select month, task, "desc" from table_data; 

CREATE OR REPLACE RULE calculate_task AS ON INSERT TO "table" 
    DO INSTEAD 
    INSERT into table_data (month, task, "desc") 
    VALUES (
    NEW.month, 
    (select coalesce(max(task),0) + 1 from table_data where month = NEW.month), 
    NEW."desc"); 

BEGIN; 
INSERT INTO "table" ("month", "desc") VALUES (1, 'One thing'); 
INSERT INTO "table" ("month", "desc") VALUES (1, 'Another thing'); 
INSERT INTO "table" ("month", "desc") VALUES (1, 'Last task of the month'); 
INSERT INTO "table" ("month", "desc") VALUES (2, 'Last task of the month'); 
INSERT INTO "table" ("month", "desc") VALUES (2, 'Last task of the month'); 
INSERT INTO "table" ("month", "desc") VALUES (3, 'First of third month'); 
COMMIT; 

select * from "table"; 

Uwagi

  • Jeśli trzeba wspierać DELETE/UPDATE na „stół”, a następnie można dodać zasady także dla każdego z tych działań.
  • Powyższy blok powyżej jest używany do pokazania, że ​​nawet w ramach tej samej transakcji ta metoda będzie działać tak długo, jak każdy wiersz zostanie podzielony na własny INSERT.
  • Używasz kilku reserved words, takich jak table i desc. Pamiętaj, aby dwukrotnie zacytować je tak, jak zrobiłeś i nie będziesz mieć żadnych problemów.

Here jest powyższy kod w sqlfiddle

Powiązane problemy