2012-12-07 11 views
7

mam następującej tabeli PostgreSQL 9,2 zawierający znaczniki czasowe:Aktualizacja kolumnę z wynikami zapytania w PostgreSQL

GID [PK] (bigserial) timestamp_mes (znacznik czasu bez strefy czasowej) time_diff (interwał)
1, 2012-01-23 11:03:40, pusty
2, 2012-01-23 11:03:42, pusty
3, 2012-01-23 11:03:44, pusty

Dodałem kolumnę interwałową (time_diff) i chciałbym ją wypełnić wartościami różnicy czasu wynikającymi z t Jego zapytania:

SELECT timestamp_mes - lag(timestamp_mes, 1) 
over (order by timestamp_mes) as diff 
from gc_entretien.trace order by timestamp_mes 

Próbowałem następującą kwerendę, aby zaktualizować kolumny time_diff, bez powodzenia:

UPDATE gc_entretien.trace set time_diff = 
(SELECT trace.timestamp_mes - lag(trace.timestamp_mes, 1) 
over (order by trace.timestamp_mes) 
from gc_entretien.trace order by timestamp_mes); 

Wynika to z błędu:

ERROR: more than one row returned by a subquery used as an expression

Jak należy postępować zaktualizować kolumnę time_diff wartościami wynikającymi z zapytania o różnicę czasu?

+1

Nie jestem pewien co do logiki Twojej aplikacji, ale opcja zwraca prawdopodobnie więcej wierszy, co powoduje błąd, gdy przypisywanie do s ingle kolumna (jak robisz w 'UPDATE') ... jeśli wynik jest w pierwszym wierszu zwrócone w' WYBIERZ', użyj 'LIMIT 1', aby przydzielić zadanie możliwe. W każdym razie wybór nie wydaje się być poprawnie zaprojektowany. –

+0

@ KamilŠrot - Jest z tym problem; ponieważ podzapytanie jest (obecnie) nieskorelowane, 'LIMIT 1' po prostu zwróci górny wiersz, kropkę, a nie taki, który ma cokolwiek wspólnego z bieżącym wierszem. –

+0

@ Clockwork-Muse, właśnie dlatego mówię, że nie jest poprawnie zaprojektowane zapytanie.Najpierw @jatobat musi utworzyć zapytanie zwracające dokładnie jedną wartość (tę, której potrzebuje zgodnie z logiką aplikacji), a następnie umieścić ją jako podzapytanie w zapytaniu "UPDATE" ... zwykłym scenariuszem jest użycie niektórych identyfikatorów/wartości z tabeli (lub wiersza), które są aktualizowane w podzapytaniu jako warunek (-y) w klauzuli podzapytania "WHERE". Ale szczerze powiedziawszy: nie rozumiem logiki aplikacji, a nawet nie chcę jej zrozumieć :-) –

Odpowiedz

22

Coś li ke to:

with new_values as (
    SELECT gid, 
      timestamp_mes - lag(timestamp_mes, 1) over (order by timestamp_mes) as diff 
    from gc_entretien.trace 
) 
update gc_entretien.trace as tr 
    set time_diff = nv.diff 
from new_values nv 
where nv.gid = tr.gid; 
+0

+1. To wygląda na mnie i pokonaj mnie 4 minuty. Nigdy nie wiedziałem o istnieniu 'Z ... AS ...' aż do teraz. Chłodny. :) –

+0

@MarkAmery: to się nazywa "wspólne wyrażenie tabelowe", a od 9.1 może być również używane w instrukcjach DML (wcześniej można go było używać tylko w przypadku normalnych instrukcji SELECT - w tym zapytań rekursywnych) –

+0

Jeśli robisz to w T-SQL wydaje się, że nie można alias tabeli aktualizacji. Jeśli użyjesz nazwy tabeli jawnie (zamiast tr w tym przypadku) to by działało. –

1

Właściwie otrzymujesz ten błąd, ponieważ podzapytanie zwraca wiele wyników,

nie jestem w stanie zrozumieć zapytanie tak,

dam wam przykład rozwiązywać je,

update table t1 set time_diff= select *your_operation* from table t2 where t1.id=t2.id 

Tutaj: -your_operation oznacza logikę znalezienie różnicy czasu,

+0

Tak, to jest ogólna forma tego, co musi zrobić ... może wypełnić resztę pytanie? –

+0

Ta odpowiedź zadziałała, ponieważ miałem problem z PostgreSQL 9.0.1. Dzięki. – Ambran

3

Nie można bezpośrednio użyć funkcji okna w aktualizacji, więc zamiast tego trzeba użyć go w sub-SELECT - co masz zrobić. Jednak sposób, w jaki próbowałeś użyć tego podelektronu w AKTUALIZACJI jest nieprawidłową składnią. Trzeba umieścić sub-select w klauzula FROM aktualizacją, jak wyjaśnił docs PostgreSQL tutaj:

http://www.postgresql.org/docs/9.2/static/sql-update.html

Poprawna składnia, co chcesz zrobić, to:

UPDATE gc_entretien.trace t 
SET time_diff = subquery.diff 
FROM (SELECT {{SomeUniqueId}}, 
      timestamp_mes - lag(timestamp_mes, 1) over (order by timestamp_mes) as diff 
     FROM gc_entretien.trace order by timestamp_mes) AS subquery 
WHERE t.{{SomeUniqueId}} = subquery.{{SomeUniqueId}} 

Oczywiście musisz zastąpić w kolumnie nazwę jakiegoś unikalnego identyfikatora, którego wiersze mają tam, gdzie napisałem: {{SomeUniqueId}}