Utilisez la fonction intégrée souvent négligée width_bucket()
en combinaison avec votre agrégation :
Si vos coordonnées vont, disons, de 0 à 2000 et que vous souhaitez tout regrouper dans des carrés de 5 à des points uniques, je tracerais une grille de 10 (5*2) comme ceci :
SELECT device_id
, width_bucket(pos_x, 0, 2000, 2000/10) * 10 AS pos_x
, width_bucket(pos_y, 0, 2000, 2000/10) * 10 AS pos_y
, count(*) AS ct -- or any other aggregate
FROM tbl
GROUP BY 1,2,3
ORDER BY 1,2,3;
Pour minimiser l'erreur vous pouvez GROUP BY
la grille comme illustré, mais enregistrez les coordonnées moyennes réelles :
SELECT device_id
, avg(pos_x)::int AS pos_x -- save actual averages to minimize error
, avg(pos_y)::int AS pos_y -- cast if you need to
, count(*) AS ct -- or any other aggregate
FROM tbl
GROUP BY
device_id
, width_bucket(pos_x, 0, 2000, 2000/10) * 10 -- aggregate by grid
, width_bucket(pos_y, 0, 2000, 2000/10) * 10
ORDER BY 1,2,3;
sqlfiddle démontrant les deux à côté.
Eh bien, ce cas particulier pourrait être plus simple :
...
GROUP BY
device_id
, (pos_x / 10) * 10 -- truncates last digit of an integer
, (pos_y / 10) * 10
...
Mais c'est simplement parce que la taille de la grille de démonstration de 10
correspond commodément au système décimal. Essayez la même chose avec une taille de grille de 17
ou quelque chose ...
Étendre aux horodatages
Vous pouvez étendre cette approche pour couvrir date
et timestamp
valeurs en les convertissant en époque unix (nombre de secondes depuis '1970-1-1') avec extract().
SELECT extract(epoch FROM '2012-10-01 21:06:38+02'::timestamptz);
Lorsque vous avez terminé, reconvertissez le résultat en timestamp with time zone
:
SELECT timestamptz 'epoch' + 1349118398 * interval '1s';
Ou simplement to_timestamp()
:
SELECT to_timestamp(1349118398);