Apache Derby: funkcja użytkownika MEDIAN
Zadanie programistyczne
Stworzyć funkcję kolumnową umożliwiającą obliczanie mediany dla kolumny wyników
Mediana to środkowa wartość z kolumny wyników. W przypadku gdy liczba rekordów jest nieparzysta jest to wartość środkowa. W przypadku, gdy liczba rekordów jest nieparzysta jest to suma wyrazów środkowych podzielona przez 2.
Przykład w dokumentacji Derby oblicza błędną wartość mediany.
Rozwiązanie
Napisać klasę Median
(Median.java
). Pamiętaj o wyeksportowaniu pakietu, w którym znajduje się ta klasa.
Opis klasy
Klasa implementuje interfejs Aggregator
, a więc i cztery metody tego interfejsu.
Typ podany jako środkowy (Double
) w linii 9 musi odpowiadać typowi zwracanemu przez metodę terminate() w linii 31
Typ podany jako trzeci (Median<p>) w linii 9 musi odpowiadać typowi podanemu jako typ argumentu 'otherData’ w metodzie merge
. Nie musi to być typ tej klasy. Może być inny typ agregacyjny, ale z parametrem T.
Zadeklarować funkcję w bazie Derby (Plik r091.sql)
Napisać klasę testującą (R091_MEDIAN.java
)
Klasa Median.java
package aderby.functions.custom; import java.util.*; import org.apache.derby.agg.*; public class Median<T extends Comparable<T>> implements Aggregator<T, Double, Median<T>> { private static final long serialVersionUID = 6089118633594167438L; private ArrayList<T> data; public Median() { } @Override public void init() { data = new ArrayList<>(); } @Override public void accumulate(T value) { data.add(value); } @Override public void merge(Median<T> otherData) { data.addAll(otherData.data); } @Override public Double terminate() { Collections.sort(data); int n = data.size(); if (n == 0) { return null; } if (n % 2 != 0) { return (Double) data.get((n + 1) / 2); } else { return (Double) data.get(n / 2) / (Double) data.get((n / 2) + 1); } } }
Klasa R091_MEDIAN.java
package aderby.functions.custom; import aderby.DerbyUtil; import java.io.File; import java.sql.*; public class R091_MEDIAN { private static final String baza = "C:/Przyklady/r091_median"; private static final String skrypt = "aderby/src/resources/sqls/r091.sql"; private static final String sqlQuery = "SELECT czujnik, MEDIAN(val) FROM temperatura GROUP BY czujnik"; public static void main(String[] args) { DerbyUtil.startDerbyEngine("EmbeddedDriver"); Connection con = DerbyUtil.connectEmbeddedDB(baza, ";create=true"); DerbyUtil.jdbcRunScript(skrypt, con); Statement stat = null; ResultSet rs = null; try { stat = con.createStatement(); rs = stat.executeQuery(sqlQuery); while (rs.next()) { System.out.println(rs.getInt(1) + " " + rs.getDouble(2)); } } catch (SQLException e) { e.printStackTrace(); } DerbyUtil.close(rs); DerbyUtil.close(stat); DerbyUtil.close(con); DerbyUtil.shutdownDerbyEngine(); DerbyUtil.dropDatabase(new File(baza)); } }
Plik r091.sql
CREATE TABLE temperatura( id INTEGER NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY(START WITH 1, INCREMENT BY 1), czujnik INTEGER, val DOUBLE ); CREATE INDEX i_czujnik ON temperatura (czujnik ASC); CREATE INDEX i_val ON temperatura (val ASC); CREATE DERBY AGGREGATE median FOR DOUBLE RETURNS DOUBLE EXTERNAL NAME 'aderby.functions.custom.Median'; INSERT INTO temperatura (czujnik, val) VALUES (2, 37.8); INSERT INTO temperatura (czujnik, val) VALUES (1, 39.7); INSERT INTO temperatura (czujnik, val) VALUES (3, 36.6); INSERT INTO temperatura (czujnik, val) VALUES (3, 33.2); INSERT INTO temperatura (czujnik, val) VALUES (2, 39.2); INSERT INTO temperatura (czujnik, val) VALUES (2, 40.6); INSERT INTO temperatura (czujnik, val) VALUES (2, 41.2); INSERT INTO temperatura (czujnik, val) VALUES (1, 40.1); INSERT INTO temperatura (czujnik, val) VALUES (1, 39.8); INSERT INTO temperatura (czujnik, val) VALUES (2, 38.9); INSERT INTO temperatura (czujnik, val) VALUES (1, 38.3); INSERT INTO temperatura (czujnik, val) VALUES (2, 37.9); INSERT INTO temperatura (czujnik, val) VALUES (2, 37.4); INSERT INTO temperatura (czujnik, val) VALUES (3, 37.1); INSERT INTO temperatura (czujnik, val) VALUES (3, 36.8); INSERT INTO temperatura (czujnik, val) VALUES (3, 36.7); INSERT INTO temperatura (czujnik, val) VALUES (1, 36.6);
Przetestować przykład
1 39.8 2 39.2 3 36.8
Pliki do ściągnięcia
Aktualny (tworzony narastająco) plik module-info.java
Aktualny (tworzony narastająco) plik DerbyUtil.java
Pliki tworzone narastająco zastępują poprzednie pliki o tej samej nazwie i działają dla wszystkich wcześniej opublikowanych przykładów we wszystkich wpisach w projekcie. W przypadku pliku module-info.java
może być potrzebne skreślenie niepotrzebnych wpisów.