refactor(scud): изоляция тяжелых SQL-шаблонов Орион в sql_queries и разгрузка scud_export
This commit is contained in:
@@ -1,40 +1,46 @@
|
||||
"""
|
||||
===============================================================================
|
||||
FILE: services/scud_etl/sql_queries.py
|
||||
PROJECT: SCUD Orion AI (Unified Architecture)
|
||||
MODULE: services / scud_etl
|
||||
ROLE: Хранилище сырых SQL-шаблонов для выгрузки из MS SQL Server (СКУД Орион Pro).
|
||||
|
||||
AI-CONTEXT-ANCHORS:
|
||||
- ANCHOR[SQL_SCUD_EXPORT_TEMPLATE]: T-SQL запрос с расчетом первой активности и длительности.
|
||||
ROLE: Изолированные SQL-шаблоны для MS SQL Server (СКУД Орион Pro).
|
||||
===============================================================================
|
||||
"""
|
||||
|
||||
# ANCHOR[SQL_SCUD_EXPORT_TEMPLATE]
|
||||
SCUD_EXPORT_QUERY_TEMPLATE = r"""
|
||||
# 1. Основной срез СКУД с учетом двухконтурного правого турникета
|
||||
SQL_QUERY_TEMPLATE = r"""
|
||||
DECLARE @InputDate DATE = '{target_date}';
|
||||
DECLARE @TargetDate DATE = @InputDate;
|
||||
|
||||
DECLARE @StartDate DATETIME = CAST(@TargetDate AS DATETIME);
|
||||
DECLARE @EndDate DATETIME = DATEADD(SECOND, -1, DATEADD(DAY, 1, @StartDate));
|
||||
DECLARE @EndDate DATETIME = {end_datetime_sql};
|
||||
|
||||
WITH DailyLogs AS (
|
||||
WITH PercoPassages AS (
|
||||
-- Физические факты прохода (Event = 32)
|
||||
SELECT
|
||||
log.HozOrgan AS EmployeeID,
|
||||
log.TimeVal,
|
||||
log.Event,
|
||||
log.Mode,
|
||||
CASE
|
||||
WHEN log.Mode = 2 OR log.Event IN (29, 27, 33) THEN 'OUT'
|
||||
WHEN log.Mode = 1 OR log.Event IN (28, 26, 32) THEN 'IN'
|
||||
WHEN log.Mode = 1 THEN 'IN'
|
||||
WHEN log.Mode = 2 THEN 'OUT'
|
||||
ELSE 'OTHER'
|
||||
END AS Direction,
|
||||
ROW_NUMBER() OVER (PARTITION BY log.HozOrgan ORDER BY log.TimeVal DESC) AS RowNumDesc
|
||||
END AS Direction
|
||||
FROM pLogData log WITH (NOLOCK)
|
||||
INNER JOIN pList p WITH (NOLOCK) ON log.HozOrgan = p.ID
|
||||
LEFT JOIN PDivision div WITH (NOLOCK) ON p.Section = div.ID
|
||||
WHERE log.TimeVal BETWEEN @StartDate AND @EndDate
|
||||
AND log.HozOrgan IS NOT NULL
|
||||
AND log.HozOrgan > 0
|
||||
AND log.Event IN (26, 27, 28, 29, 32, 33, 54, 55, 64, 65)
|
||||
AND log.Event = 32
|
||||
AND log.Mode IN (1, 2)
|
||||
AND (
|
||||
-- Контур 1: Левый турникет открыт для всех
|
||||
log.DoorIndex = 1
|
||||
OR
|
||||
-- Контур 2: Правый турникет разрешен только для реестра двора
|
||||
(
|
||||
log.DoorIndex = 2
|
||||
AND ({turnstile_filter_sql})
|
||||
)
|
||||
)
|
||||
),
|
||||
Passages AS (
|
||||
SELECT
|
||||
@@ -42,10 +48,21 @@ Passages AS (
|
||||
MIN(TimeVal) AS FirstRawEvent,
|
||||
MAX(TimeVal) AS LastRawEvent,
|
||||
MIN(CASE WHEN Direction = 'IN' THEN TimeVal END) AS FirstIn,
|
||||
MAX(CASE WHEN Direction = 'OUT' THEN TimeVal END) AS LastOut,
|
||||
MAX(CASE WHEN RowNumDesc = 1 THEN Direction END) AS LastEventType
|
||||
FROM DailyLogs
|
||||
MAX(CASE WHEN Direction = 'OUT' THEN TimeVal END) AS FinalOut
|
||||
FROM PercoPassages
|
||||
GROUP BY EmployeeID
|
||||
),
|
||||
EvaluatedPassages AS (
|
||||
SELECT
|
||||
p.*,
|
||||
CASE
|
||||
WHEN p.FinalOut IS NOT NULL
|
||||
AND p.FirstIn IS NOT NULL
|
||||
AND p.FinalOut > DATEADD(MINUTE, 5, p.FirstIn)
|
||||
THEN p.FinalOut
|
||||
ELSE NULL
|
||||
END AS FilteredLastOut
|
||||
FROM Passages p
|
||||
)
|
||||
SELECT
|
||||
N'ЛЕНМОРНИИПРОЕКТ' AS [Фирма],
|
||||
@@ -67,12 +84,8 @@ SELECT
|
||||
ELSE N'—'
|
||||
END AS [Первая_активность],
|
||||
CASE
|
||||
WHEN @TargetDate = CAST(GETDATE() AS DATE) AND (pass.LastEventType = 'IN' OR pass.LastOut IS NULL OR pass.LastOut <= pass.FirstIn)
|
||||
THEN N'Нет выхода'
|
||||
WHEN pass.LastOut IS NOT NULL AND pass.LastOut > pass.FirstIn
|
||||
THEN CAST(CONVERT(VARCHAR(8), pass.LastOut, 108) AS NVARCHAR(20))
|
||||
WHEN @TargetDate < CAST(GETDATE() AS DATE) AND pass.LastRawEvent IS NOT NULL AND pass.LastRawEvent > ISNULL(pass.FirstIn, pass.FirstRawEvent)
|
||||
THEN CAST(CONVERT(VARCHAR(8), pass.LastRawEvent, 108) AS NVARCHAR(20))
|
||||
WHEN pass.FilteredLastOut IS NOT NULL
|
||||
THEN CAST(CONVERT(VARCHAR(8), pass.FilteredLastOut, 108) AS NVARCHAR(20))
|
||||
ELSE N'Нет выхода'
|
||||
END AS [Конец_дня],
|
||||
CASE
|
||||
@@ -80,14 +93,14 @@ SELECT
|
||||
RIGHT('0' + CAST(DATEDIFF(MINUTE,
|
||||
ISNULL(pass.FirstIn, pass.FirstRawEvent),
|
||||
CASE
|
||||
WHEN @TargetDate = CAST(GETDATE() AS DATE) AND (pass.LastEventType = 'IN' OR pass.LastOut IS NULL OR pass.LastOut <= pass.FirstIn) THEN GETDATE()
|
||||
ELSE ISNULL(pass.LastOut, pass.LastRawEvent)
|
||||
WHEN pass.FilteredLastOut IS NOT NULL THEN pass.FilteredLastOut
|
||||
ELSE @EndDate
|
||||
END) / 60 AS VARCHAR), 2) + ':' +
|
||||
RIGHT('0' + CAST(DATEDIFF(MINUTE,
|
||||
ISNULL(pass.FirstIn, pass.FirstRawEvent),
|
||||
CASE
|
||||
WHEN @TargetDate = CAST(GETDATE() AS DATE) AND (pass.LastEventType = 'IN' OR pass.LastOut IS NULL OR pass.LastOut <= pass.FirstIn) THEN GETDATE()
|
||||
ELSE ISNULL(pass.LastOut, pass.LastRawEvent)
|
||||
WHEN pass.FilteredLastOut IS NOT NULL THEN pass.FilteredLastOut
|
||||
ELSE @EndDate
|
||||
END) % 60 AS VARCHAR), 2)
|
||||
ELSE N'00:00'
|
||||
END AS [Находился_в_здании],
|
||||
@@ -98,7 +111,7 @@ SELECT
|
||||
FROM pList p WITH (NOLOCK)
|
||||
LEFT JOIN PDivision div WITH (NOLOCK) ON p.Section = div.ID
|
||||
LEFT JOIN PPost post WITH (NOLOCK) ON p.Post = post.ID
|
||||
LEFT JOIN Passages pass ON p.ID = pass.EmployeeID
|
||||
LEFT JOIN EvaluatedPassages pass ON p.ID = pass.EmployeeID
|
||||
WHERE
|
||||
ISNULL(p.StatusRecord, 0) = 0
|
||||
AND p.DateTimeInArchive IS NULL
|
||||
@@ -113,4 +126,42 @@ WHERE
|
||||
AND ISNULL(CAST(div.Name AS NVARCHAR(255)), N'') NOT LIKE N'тест%'
|
||||
AND ISNULL(CAST(post.Name AS NVARCHAR(255)), N'') NOT LIKE N'Практикант%'
|
||||
ORDER BY p.Name ASC;
|
||||
"""
|
||||
|
||||
# 2. Сырые события физических проходов турникетов
|
||||
SQL_RAW_EVENTS_QUERY = r"""
|
||||
DECLARE @InputDate DATE = '{target_date}';
|
||||
DECLARE @StartDate DATETIME = CAST(@InputDate AS DATETIME);
|
||||
DECLARE @EndDate DATETIME = {end_datetime_sql};
|
||||
|
||||
SELECT
|
||||
log.TimeVal,
|
||||
log.HozOrgan,
|
||||
LTRIM(RTRIM(
|
||||
ISNULL(CAST(p.Name AS NVARCHAR(255)), N'') +
|
||||
CASE WHEN p.FirstName IS NOT NULL AND CAST(p.FirstName AS NVARCHAR(255)) <> ''
|
||||
THEN N' ' + CAST(p.FirstName AS NVARCHAR(255)) ELSE N'' END +
|
||||
CASE WHEN p.MidName IS NOT NULL AND CAST(p.MidName AS NVARCHAR(255)) <> ''
|
||||
THEN N' ' + CAST(p.MidName AS NVARCHAR(255)) ELSE N'' END
|
||||
)) AS [Сотрудник],
|
||||
ISNULL(CAST(div.Name AS NVARCHAR(255)), N'Без подразделения') AS [Подразделение],
|
||||
log.Event,
|
||||
log.Mode,
|
||||
log.DoorIndex,
|
||||
CASE
|
||||
WHEN log.Mode = 2 THEN 'OUT'
|
||||
WHEN log.Mode = 1 THEN 'IN'
|
||||
WHEN log.Event IN (2, 27, 29, 33, 55, 65) THEN 'OUT'
|
||||
WHEN log.Event IN (1, 21, 26, 54, 64) THEN 'IN'
|
||||
ELSE 'OTHER'
|
||||
END AS Direction
|
||||
FROM pLogData log WITH (NOLOCK)
|
||||
INNER JOIN pList p WITH (NOLOCK) ON log.HozOrgan = p.ID
|
||||
LEFT JOIN PDivision div WITH (NOLOCK) ON p.Section = div.ID
|
||||
WHERE log.TimeVal BETWEEN @StartDate AND @EndDate
|
||||
AND log.HozOrgan IS NOT NULL
|
||||
AND log.HozOrgan > 0
|
||||
AND log.Event IN (28, 32)
|
||||
AND ISNULL(p.StatusRecord, 0) = 0
|
||||
ORDER BY log.TimeVal ASC;
|
||||
"""
|
||||
Reference in New Issue
Block a user