Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Намиране на едновременни събития в база данни между времена

Отказ от отговорност:Пиша отговора си въз основа на (отличен) следния пост:

https://www.itprotoday.com/sql-server/calculating-concurrent-sessions-part-3 (Част 1 и 2 също се препоръчват)

Първото нещо, което трябва да разберете тук с този проблем, е, че повечето от настоящите решения, открити в интернет, могат да имат основно два проблема

  • Резултатът не е правилният отговор (например ако диапазон A се припокрива с B и C, но B не се припокрива с C, те се считат за 3 припокриващи се диапазона).
  • Начинът за изчисляването му е много неефективен (защото е O(n^2) и/или те се въртят за всяка секунда от периода)

Често срещаният проблем с производителността в решения като предложеното от Unreasons е куадратично решение, за всяко повикване трябва да проверявате всички други повиквания, ако се припокриват.

има алгоритмично линейно общо решение, което изброява всички „събития“ (начално повикване и крайно обаждане), подредени по дата, и добавя 1 за начало и изважда 1 за прекъсване и запомня макс. Това може да се приложи лесно с курсор (решението, предложено от Hafhor изглежда е по този начин), но курсорите не са най-ефективните начини за решаване на проблеми.

Посочената статия има отлични примери, различни решения, сравнение на производителността им. Предложеното решение е:

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

Обяснение

да предположим, че този набор от данни

+-------------------------+-------------------------+
|        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 |
+-------------------------+-------------------------+

Това е начин да приложите със заявка същата идея, като добавяте 1 за всяко начало на повикване и изваждате 1 за всяко завършване.

  SELECT starttime AS ts, +1 AS TYPE,
    ROW_NUMBER() OVER(ORDER BY starttime) AS start_ordinal
  FROM Calls

тази част от C1 CTE ще приеме всяко начало на всяко повикване и ще го номерира

+-------------------------+------+---------------+
|           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 |
+-------------------------+------+---------------+

Сега този код

  SELECT endtime, -1, NULL
  FROM Calls

Ще генерира всички "крайни времена" без номериране на редове

+-------------------------+----+------+
|         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 |
+-------------------------+----+------+

Сега правите UNION да има пълната C1 CTE дефиниция, ще имате смесени и двете таблици

+-------------------------+------+---------------+
|           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 се изчислява, като сортира и номерира C1 с нова колона

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 |
+-------------------------+------+-------+--------------+

И там се случва магията, по всяко време резултатът от #start - #ends е количеството съпътстващи повиквания в този момент.

за всеки Type =1 (начално събитие) имаме #start стойността в 3-та колона. и имаме също #начало + #край (в 4-та колона)

#start_or_end = #start + #end

#end = (#start_or_end - #start)

#start - #end = #start - (#start_or_end - #start)

#start - #end = 2 * #start - #start_or_end

така че в SQL:

SELECT MAX(2 * start_ordinal - start_or_end_ordinal) AS mx
FROM C2
WHERE TYPE = 1

В този случай с предложения набор от повиквания резултатът е 2.

В предложената статия има малко подобрение, за да имате групиран резултат например чрез услуга или "телефонна компания" или "телефонна централа" и тази идея може да се използва и за групиране например по времеви интервал и да има максимален едновременност час по час в даден ден.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Регистърът на транзакциите за базата данни е пълен

  2. Как да инсталирате SQL Server на Linux

  3. как да свържете sql сървър с помощта на JTDS драйвер в Android

  4. nvarchar конкатенация / индекс / nvarchar(max) необяснимо поведение

  5. Как да актуализирате голяма таблица с милиони редове в SQL Server?