Disclaimer:ik schrijf mijn antwoord op basis van de (uitstekende) volgende post:
https://www.itprotoday.com/sql-server/calculating-concurrent-sessions-part-3 (Deel 1 en 2 worden ook aanbevolen)
Het eerste dat u hier met dat probleem moet begrijpen, is dat de meeste van de huidige oplossingen die op internet worden gevonden in feite twee problemen kunnen hebben
- Het resultaat is niet het juiste antwoord (als bereik A bijvoorbeeld overlapt met B en C maar B niet met C, tellen ze als 3 overlappende bereiken).
- De manier om het te berekenen is erg inefficiënt (omdat O(n^2) is en/of ze circuleren voor elke seconde in de periode)
Het veelvoorkomende prestatieprobleem in oplossingen zoals de door Unreasons voorgestelde oplossing is een gelijktijdige oplossing, voor elke oproep moet u alle andere oproepen controleren als ze elkaar overlappen.
er is een algoritmische lineaire gemeenschappelijke oplossing die alle "gebeurtenissen" (startgesprek en eindgesprek) op datum vermeldt, en 1 optelt voor een start en 1 aftrekt voor een ophanging, en onthoud de max. Dat kan eenvoudig worden geïmplementeerd met een cursor (oplossing voorgesteld door Hafhor lijkt op die manier te zijn), maar cursors zijn niet de meest efficiënte manieren om problemen op te lossen.
Het artikel waarnaar wordt verwezen heeft uitstekende voorbeelden, verschillende oplossingen, prestatievergelijking ervan. De voorgestelde oplossing is:
WITH C1 AS
(
SELECT starttime AS ts, +1 AS TYPE,
ROW_NUMBER() OVER(ORDER BY starttime) AS start_ordinal
FROM Calls
UNION ALL
SELECT endtime, -1, NULL
FROM Calls
),
C2 AS
(
SELECT *,
ROW_NUMBER() OVER( ORDER BY ts, TYPE) AS start_or_end_ordinal
FROM C1
)
SELECT MAX(2 * start_ordinal - start_or_end_ordinal) AS mx
FROM C2
WHERE TYPE = 1
Uitleg
stel dat deze set gegevens
+-------------------------+-------------------------+
| starttime | endtime |
+-------------------------+-------------------------+
| 2009-01-01 00:02:10.000 | 2009-01-01 00:05:24.000 |
| 2009-01-01 00:02:19.000 | 2009-01-01 00:02:35.000 |
| 2009-01-01 00:02:57.000 | 2009-01-01 00:04:04.000 |
| 2009-01-01 00:04:12.000 | 2009-01-01 00:04:52.000 |
+-------------------------+-------------------------+
Dit is een manier om met een zoekopdracht hetzelfde idee te implementeren, door 1 toe te voegen voor elk begin van een gesprek en 1 af te trekken voor elk einde.
SELECT starttime AS ts, +1 AS TYPE,
ROW_NUMBER() OVER(ORDER BY starttime) AS start_ordinal
FROM Calls
dit deel van de C1 CTE neemt elke starttijd van elke oproep en nummert deze
+-------------------------+------+---------------+
| ts | TYPE | start_ordinal |
+-------------------------+------+---------------+
| 2009-01-01 00:02:10.000 | 1 | 1 |
| 2009-01-01 00:02:19.000 | 1 | 2 |
| 2009-01-01 00:02:57.000 | 1 | 3 |
| 2009-01-01 00:04:12.000 | 1 | 4 |
+-------------------------+------+---------------+
Nu deze code
SELECT endtime, -1, NULL
FROM Calls
Genereert alle "eindtijden" zonder rijnummering
+-------------------------+----+------+
| endtime | | |
+-------------------------+----+------+
| 2009-01-01 00:02:35.000 | -1 | NULL |
| 2009-01-01 00:04:04.000 | -1 | NULL |
| 2009-01-01 00:04:52.000 | -1 | NULL |
| 2009-01-01 00:05:24.000 | -1 | NULL |
+-------------------------+----+------+
Nu de UNION de volledige C1 CTE-definitie heeft, heb je beide tabellen gemengd
+-------------------------+------+---------------+
| ts | TYPE | start_ordinal |
+-------------------------+------+---------------+
| 2009-01-01 00:02:10.000 | 1 | 1 |
| 2009-01-01 00:02:19.000 | 1 | 2 |
| 2009-01-01 00:02:57.000 | 1 | 3 |
| 2009-01-01 00:04:12.000 | 1 | 4 |
| 2009-01-01 00:02:35.000 | -1 | NULL |
| 2009-01-01 00:04:04.000 | -1 | NULL |
| 2009-01-01 00:04:52.000 | -1 | NULL |
| 2009-01-01 00:05:24.000 | -1 | NULL |
+-------------------------+------+---------------+
C2 is berekend sorteren en nummeren C1 met een nieuwe kolom
C2 AS
(
SELECT *,
ROW_NUMBER() OVER( ORDER BY ts, TYPE) AS start_or_end_ordinal
FROM C1
)
+-------------------------+------+-------+--------------+
| ts | TYPE | start | start_or_end |
+-------------------------+------+-------+--------------+
| 2009-01-01 00:02:10.000 | 1 | 1 | 1 |
| 2009-01-01 00:02:19.000 | 1 | 2 | 2 |
| 2009-01-01 00:02:35.000 | -1 | NULL | 3 |
| 2009-01-01 00:02:57.000 | 1 | 3 | 4 |
| 2009-01-01 00:04:04.000 | -1 | NULL | 5 |
| 2009-01-01 00:04:12.000 | 1 | 4 | 6 |
| 2009-01-01 00:04:52.000 | -1 | NULL | 7 |
| 2009-01-01 00:05:24.000 | -1 | NULL | 8 |
+-------------------------+------+-------+--------------+
En daar is waar de magie plaatsvindt, op elk moment is het resultaat van #start - #ends het aantal gelijktijdige oproepen op dit moment.
voor elk Type =1 (startgebeurtenis) hebben we de #start-waarde in de 3e kolom. en we hebben ook de #start + #end (in de 4e kolom)
#start_or_end = #start + #end
#end = (#start_or_end - #start)
#start - #end = #start - (#start_or_end - #start)
#start - #end = 2 * #start - #start_or_end
dus in SQL:
SELECT MAX(2 * start_ordinal - start_or_end_ordinal) AS mx
FROM C2
WHERE TYPE = 1
In dit geval is het resultaat met de voorgestelde reeks oproepen 2.
In het voorgestelde artikel is er een kleine verbetering om een gegroepeerd resultaat te hebben op bijvoorbeeld een dienst of een "telefoonmaatschappij" of "telefooncentrale" en dit idee kan ook worden gebruikt om bijvoorbeeld op tijdslot te groeperen en de maximale gelijktijdigheid te hebben uur na uur op een bepaalde dag.