0

I have a Postgres table with categorys and timespans (columns valid_from and valid_to). This represents road sign that are installed for certain time span. I want to compute how many road signs there are necessary. A road sign can be used multiple times if it is not in the same timespan.

Take the following example. For the first two rows, only two signs are needed, since the signs in the first can be moved. enter image description here

For the other rows, only sign is needed.

I came up with this code, but it does not compute the correct number:

SELECT category, MAX(num_concurrent_objects) AS max_concurrent_objects
FROM (
    SELECT v1.category, COUNT(*) AS num_concurrent_objects
    FROM signs v1
    JOIN signs v2 
    ON v1.category= v2.category
    AND v1.valid_from <= v2.valid_to
    AND v1.valid_to >= v2.valid_from
    GROUP BY v1.category, v1.valid_from, v1.valid_to
) AS concurrent_objects
GROUP BY category;

How can I compute the number of concurrently used signs for any combination? Any help would be appreciated.

The road signs also have positions (column geom in PostGIS). For multiple-use signs, I would like to compute the ideal movement, meaning the same sign should be moved to its new place based on the smallest distance.

edit:

CREATE TABLE signs (
    id serial NOT NULL,
    category int4 NULL,
    valid_from timestamp NULL,
    valid_to timestamp NULL,
    geom public.geometry NULL
);


INSERT INTO signs (id, category, valid_from, valid_to, geom) VALUES(6, 8, '2024-04-29 10:32:00.000', '2024-05-31 10:32:00.000', 'SRID=2056;POINT (2602921.5000000005 1201049.4999999998)'::public.geometry);
INSERT INTO signs (id, category, valid_from, valid_to, geom) VALUES(7, 6, '2024-04-29 00:00:00.000', '2024-06-09 14:40:00.000', 'SRID=2056;POINT (2602824.1597646 1201053.8597646)'::public.geometry);
INSERT INTO signs (id, category, valid_from, valid_to, geom) VALUES(4, 3, '2024-05-01 09:35:00.000', '2024-05-30 09:35:00.000', 'SRID=2056;POINT (2602888.000000001 1201083.5)'::public.geometry);
INSERT INTO signs (id, category, valid_from, valid_to, geom) VALUES(9, 2, '2024-05-10 00:00:00.000', '2024-05-24 00:00:00.000', 'SRID=2056;POINT (2602971.500000001 1201055.2499999995)'::public.geometry);
INSERT INTO signs (id, category, valid_from, valid_to, geom) VALUES(5, 2, '2024-04-16 00:00:00.000', '2024-06-06 00:00:00.000', 'SRID=2056;POINT (2602961.500000001 1201084.9999999995)'::public.geometry);
INSERT INTO signs (id, category, valid_from, valid_to, geom) VALUES(8, 2, '2024-04-15 00:00:00.000', '2024-05-15 00:00:00.000', 'SRID=2056;POINT (2602954 1201128.5)'::public.geometry);
INSERT INTO signs (id, category, valid_from, valid_to, geom) VALUES(10, 2, '2024-06-07 00:00:00.000', '2024-07-05 00:00:00.000', 'SRID=2056;POINT (2602856.500000001 1201014.7499999995)'::public.geometry);
4
  • Please add xa minimal reproducible example with tables and insert into Commented May 13, 2024 at 11:54
  • I added an example. Commented May 13, 2024 at 12:04
  • What is desired output for this data? Commented May 13, 2024 at 12:39
  • I would like to have a query that shows the minimal number per road sign that is needed. It should be category -> number of signs. Commented May 13, 2024 at 12:54

1 Answer 1

1

A sample which demonstrates the method.

-- source data
CREATE TABLE signs (
  id serial NOT NULL,
  category int4 NULL,
  valid_from timestamp NULL,
  valid_to timestamp NULL
  );

INSERT INTO signs (id, category, valid_from, valid_to) VALUES
  (6, 8, '2024-04-29 10:32:00.000', '2024-05-31 10:32:00.000'),
  (7, 6, '2024-04-29 00:00:00.000', '2024-06-09 14:40:00.000'),
  (4, 3, '2024-05-01 09:35:00.000', '2024-05-30 09:35:00.000'),
  (9, 2, '2024-05-10 00:00:00.000', '2024-05-24 00:00:00.000'),
  (5, 2, '2024-04-16 00:00:00.000', '2024-06-06 00:00:00.000'),
  (8, 2, '2024-04-15 00:00:00.000', '2024-05-15 00:00:00.000'),
  (10, 2, '2024-06-07 00:00:00.000', '2024-07-05 00:00:00.000');

SELECT id, category, valid_from, valid_to
FROM signs
ORDER BY 3;
id category valid_from valid_to
8 2 2024-04-15 00:00:00 2024-05-15 00:00:00
5 2 2024-04-16 00:00:00 2024-06-06 00:00:00
7 6 2024-04-29 00:00:00 2024-06-09 14:40:00
6 8 2024-04-29 10:32:00 2024-05-31 10:32:00
4 3 2024-05-01 09:35:00 2024-05-30 09:35:00
9 2 2024-05-10 00:00:00 2024-05-24 00:00:00
10 2 2024-06-07 00:00:00 2024-07-05 00:00:00
-- minimal timeranges with signs amount, category ignored
WITH cte (timepoint, weight) AS (
  SELECT valid_from, 1 FROM signs
  UNION ALL
  SELECT valid_to, -1 FROM signs
)
SELECT DISTINCT
       timepoint valid_from,
       LEAD(timepoint) OVER (ORDER BY timepoint) valid_to,
       SUM(weight) OVER (ORDER BY timepoint) amount
FROM cte
ORDER BY 1;
valid_from valid_to amount
2024-04-15 00:00:00 2024-04-16 00:00:00 1
2024-04-16 00:00:00 2024-04-29 00:00:00 2
2024-04-29 00:00:00 2024-04-29 10:32:00 3
2024-04-29 10:32:00 2024-05-01 09:35:00 4
2024-05-01 09:35:00 2024-05-10 00:00:00 5
2024-05-10 00:00:00 2024-05-15 00:00:00 6
2024-05-15 00:00:00 2024-05-24 00:00:00 5
2024-05-24 00:00:00 2024-05-30 09:35:00 4
2024-05-30 09:35:00 2024-05-31 10:32:00 3
2024-05-31 10:32:00 2024-06-06 00:00:00 2
2024-06-06 00:00:00 2024-06-07 00:00:00 1
2024-06-07 00:00:00 2024-06-09 14:40:00 2
2024-06-09 14:40:00 2024-07-05 00:00:00 1
2024-07-05 00:00:00 null 0
-- minimal timeranges with signs amount and list, category ignored
WITH cte (timepoint, weight) AS (
  SELECT valid_from, 1 FROM signs
  UNION ALL
  SELECT valid_to, -1 FROM signs
  ),
cte2 AS (
  SELECT DISTINCT
         timepoint valid_from,
         LEAD(timepoint) OVER (ORDER BY timepoint) valid_to,
         SUM(weight) OVER (ORDER BY timepoint) amount
  FROM cte
  )
SELECT cte2.*, string_agg(signs.id :: TEXT, ',' :: TEXT) ids
FROM cte2
JOIN signs ON cte2.valid_from < signs.valid_to 
          AND signs.valid_from < cte2.valid_to 
GROUP BY 1,2,3
ORDER BY 1
valid_from valid_to amount ids
2024-04-15 00:00:00 2024-04-16 00:00:00 1 8
2024-04-16 00:00:00 2024-04-29 00:00:00 2 5,8
2024-04-29 00:00:00 2024-04-29 10:32:00 3 7,5,8
2024-04-29 10:32:00 2024-05-01 09:35:00 4 6,7,5,8
2024-05-01 09:35:00 2024-05-10 00:00:00 5 6,7,4,5,8
2024-05-10 00:00:00 2024-05-15 00:00:00 6 6,7,4,9,5,8
2024-05-15 00:00:00 2024-05-24 00:00:00 5 6,7,4,9,5
2024-05-24 00:00:00 2024-05-30 09:35:00 4 6,7,4,5
2024-05-30 09:35:00 2024-05-31 10:32:00 3 6,7,5
2024-05-31 10:32:00 2024-06-06 00:00:00 2 7,5
2024-06-06 00:00:00 2024-06-07 00:00:00 1 7
2024-06-07 00:00:00 2024-06-09 14:40:00 2 7,10
2024-06-09 14:40:00 2024-07-05 00:00:00 1 10

fiddle

Investigate it. Then adjust to your need.

I would like to have a query that shows the minimal number per road sign that is needed. It should be category -> number of signs.

Adjust 1st query (calculate for each category separately) then get maximal amount value for each category.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.