Как я могу получить первый экземпляр события в день с несколькими столбцами, включая datetime, и вернуть эти столбцы плюс полное значение datetime? - PullRequest
0 голосов
/ 17 апреля 2019

Мне нужно сгенерировать сценарий SQL, который будет извлекать отдельные записи, используя ряд столбцов, один из которых является столбцом даты и времени. Меня интересует только первое вхождение дня для каждого события, и запрос должен охватывать несколько дней. Запрос будет выполняться к очень большой базе данных и потенциально может возвращать сотни тысяч результатов, если не миллионов. Поэтому мне нужно, чтобы этот скрипт был максимально эффективным. В конечном итоге это будет скрипт, работающий в SSRS для извлечения транзакций доступа.

Я пытался использовать GROUP BY, DISTINCT, подзапросы, FIRST и тому подобное, но безуспешно. Во всех примерах, которые я могу найти в Интернете, нет операторов JOIN или вычисляемых столбцов, например, только сбор даты из поля datetime.

Я упростил приведенный ниже сценарий, чтобы он потянул только один день и одну дверь, но результат будет несколько дней и дверей. Этот код возвращает данные, которые мне нужны, меня не волнует COUNT, но мне также нужно каким-то образом получить поле (DateAdd(minute,-(ServerLocaleOffset),ServerUTC)) в моем наборе результатов. Проблема в том, что если он идет ко второму, он делает все записи ОТЛИЧНЫМИ.

DECLARE @Begin datetime2 = '4/10/2019',
        @End datetime2 = '4/11/2019',
        @Door varchar(max) = 'Front Entrance'

SELECT
        CONVERT(VARCHAR(10), (DateAdd(minute,-(ServerLocaleOffset),ServerUTC)),101) AS 'Date'
        ,AJ.PrimaryObjectIdentity
        ,AJ.SecondaryObjectIdentity
        ,AJ.MessageType
        ,AJ.PrimaryObjectName
        ,AJ.SecondaryObjectName
        ,AP.Text13
        ,COUNT(*) AS 'Count'
FROM Access.JournalLogView AJ
LEFT OUTER JOIN Access.Personnel as AP on AP.GUID = AJ.PrimaryObjectIdentity
WHERE (MessageType like 'CardAdmitted' OR MessageType like 'CardRejected')
AND (DateAdd(minute,-(ServerLocaleOffset),ServerUTC)) BETWEEN @Begin AND @End
AND (SecondaryObjectName IN (@Door))

GROUP BY CONVERT(VARCHAR(10), (DateAdd(minute,-(ServerLocaleOffset),ServerUTC)),101)
        ,PrimaryObjectIdentity
        ,SecondaryObjectIdentity
        ,MessageType
        ,PrimaryObjectName
        ,SecondaryObjectName
        ,Text13
ORDER BY AJ.PrimaryObjectName

Я хочу, чтобы столбцы были вызваны в операторе SELECT плюс дата и время, включая второе. Опять же, я также хочу наиболее эффективный способ получения этих данных. Большое спасибо.

1 Ответ

1 голос
/ 17 апреля 2019

Предполагая, что PrimaryObjectIdentity является первичным ключом для поиска персонала в JournalLogview и ServerLocaleOffset в качестве столбца даты и времени в этой таблице, я записал это:

DECLARE @Begin datetime2 = '4/10/2019',
        @End datetime2 = '4/11/2019',
        @Door varchar(max) = 'Front Entrance'

WITH cte 
AS(
SELECT 
ROW_NUMBER() OVER 
(PARTITION BY PrimaryObjectIdentity,CONVERT(VARCHAR(10), (DateAdd(minute,-(ServerLocaleOffset),ServerUTC)),101) ORDER BY ServerLocaleOffset) AS row_num,
--whatever the columns you want here
*
FROM
Access.JournalLogView)

SELECT
         DateAdd(minute,-(ServerLocaleOffset),ServerUTC)) AS 'DateTime'
        ,AJ.PrimaryObjectIdentity
        ,AJ.SecondaryObjectIdentity
        ,AJ.MessageType
        ,AJ.PrimaryObjectName
        ,AJ.SecondaryObjectName
        ,AP.Text13
        --I guess count(*) won't be of use a we are selecting only the first row
        ,COUNT(*) AS 'Count'
FROM cte AJ
LEFT OUTER JOIN 
Access.Personnel as AP 
on 
AP.GUID = AJ.PrimaryObjectIdentity
WHERE 
AJ.row_num = 1
AND (MessageType like 'CardAdmitted' OR MessageType like 'CardRejected')
AND (DateAdd(minute,-(ServerLocaleOffset),ServerUTC)) BETWEEN @Begin AND @End
AND (SecondaryObjectName IN (@Door))

GROUP BY (DateAdd(minute,-(ServerLocaleOffset),ServerUTC))
        ,PrimaryObjectIdentity
        ,SecondaryObjectIdentity
        ,MessageType
        ,PrimaryObjectName
        ,SecondaryObjectName
        ,Text13
ORDER BY AJ.PrimaryObjectName

В этом запросе я использовал PARTITION для разделения всей таблицы по каждому пользователю, дате, а затем назначил row_number () для каждой строки, начиная с первой записи каждого пользователя в эту конкретную дату. Таким образом, любая строка с row_num () = 1 даст вам первую запись этого пользователя в эту дату (это то же самое условие, которое я использовал в предложении where). Надеюсь, это поможет:)

...