PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Uporządkowana liczba kolejnych powtórzeń/duplikatów

Przypadek testowy

Po pierwsze, bardziej użyteczny sposób prezentowania danych – lub jeszcze lepiej, w sqlfiddle , gotowy do gry z:

CREATE TEMP TABLE data(
   system_measured int
 , time_of_measurement int
 , measurement int
);

INSERT INTO data VALUES
 (1, 1, 5)
,(1, 2, 150)
,(1, 3, 5)
,(1, 4, 5)
,(2, 1, 5)
,(2, 2, 5)
,(2, 3, 5)
,(2, 4, 5)
,(2, 5, 150)
,(2, 6, 5)
,(2, 7, 5)
,(2, 8, 5);

Uproszczone zapytanie

Ponieważ pozostaje to niejasne, zakładam, że podano tylko powyższe.
Następnie uprościłem Twoje zapytanie, aby dotrzeć do:

WITH x AS (
   SELECT *, CASE WHEN lag(measurement) OVER (PARTITION BY system_measured
                               ORDER BY time_of_measurement) = measurement
                  THEN 0 ELSE 1 END AS step
   FROM   data
   )
   , y AS (
   SELECT *, sum(step) OVER(PARTITION BY system_measured
                            ORDER BY time_of_measurement) AS grp
   FROM   x
   )
SELECT * ,row_number() OVER (PARTITION BY system_measured, grp
                             ORDER BY time_of_measurement) - 1 AS repeat_ct
FROM   y
ORDER  BY system_measured, time_of_measurement;

Teraz, chociaż korzystanie z czystego SQL jest ładne i błyszczące, będzie to dużo szybciej z funkcją plpgsql, ponieważ może to zrobić w pojedynczym skanowaniu tabeli, gdzie to zapytanie wymaga co najmniej trzech skanów.

Szybciej dzięki funkcji plpgsql:

CREATE OR REPLACE FUNCTION x.f_repeat_ct()
  RETURNS TABLE (
    system_measured int
  , time_of_measurement int
  , measurement int, repeat_ct int
  )  LANGUAGE plpgsql AS
$func$
DECLARE
   r    data;     -- table name serves as record type
   r0   data;
BEGIN

-- SET LOCAL work_mem = '1000 MB';  -- uncomment an adapt if needed, see below!

repeat_ct := 0;   -- init

FOR r IN
   SELECT * FROM data d ORDER BY d.system_measured, d.time_of_measurement
LOOP
   IF  r.system_measured = r0.system_measured
       AND r.measurement = r0.measurement THEN
      repeat_ct := repeat_ct + 1;   -- start new array
   ELSE
      repeat_ct := 0;               -- start new count
   END IF;

   RETURN QUERY SELECT r.*, repeat_ct;

   r0 := r;                         -- remember last row
END LOOP;

END
$func$;

Zadzwoń:

SELECT * FROM x.f_repeat_ct();

Pamiętaj, aby przez cały czas zakwalifikować nazwy kolumn do tabeli w tego rodzaju funkcji plpgsql, ponieważ używamy tych samych nazw jako parametrów wyjściowych, które mają pierwszeństwo, jeśli nie są kwalifikowane.

Miliardy wierszy

Jeśli masz miliardy wierszy , możesz podzielić tę operację. Cytuję instrukcję tutaj:

Uwaga:aktualna implementacja RETURN NEXT i RETURN QUERY przechowuje cały zestaw wyników przed powrotem z funkcji, jak omówiono powyżej. Oznacza to, że jeśli funkcja PL/pgSQL generuje bardzo duży zestaw wyników, wydajność może być słaba:dane zostaną zapisane na dysku, aby uniknąć wyczerpania pamięci, ale sama funkcja nie powróci, dopóki nie zostanie wygenerowany cały zestaw wyników. Przyszła wersja PL/pgSQL może umożliwiać użytkownikom definiowanie funkcji zwracania zestawu, które nie mają tego ograniczenia. Obecnie punkt, w którym rozpoczyna się zapis danych na dysku, jest kontrolowany przez zmienną work_memconfiguration. Administratorzy, którzy mają wystarczającą ilość pamięci, aby przechowywać w pamięci większe zestawy wyników, powinni rozważyć zwiększenie tego parametru.

Rozważ obliczanie wierszy dla jednego systemu na raz lub ustaw wystarczająco wysoką wartość dla work_mem radzić sobie z obciążeniem. Kliknij link podany w cytacie, aby dowiedzieć się więcej o work_mem.

Jednym ze sposobów byłoby ustawienie bardzo wysokiej wartości dla work_mem z SET LOCAL w swojej funkcji, która obowiązuje tylko dla bieżącej transakcji. Dodałem skomentowaną linię w funkcji. Nie ustaw go bardzo wysoko globalnie, ponieważ może to zniszczyć twój serwer. Przeczytaj instrukcję.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak zaktualizować wybrane wiersze wartościami z pliku CSV w Postgresie?

  2. Para klucz-wartość w PostgreSQL

  3. Zmień typ pola varchar na integer:nie można automatycznie rzutować na typ integer

  4. Różnica między typem danych DECIMAL i NUMERIC w PSQL

  5. Jak najlepiej wykorzystać indeksy PostgreSQL