Помогите мне найти блоки данных - PullRequest
2 голосов
/ 23 февраля 2010

У меня есть столбцы таблицы Id и EmployeeID. Данные таблицы имеют следующую особенность: в некоторых частях (где Id является последовательным), иногда можно найти один и тот же EmployeeID, например

Id | EmployeeID
---------------
1  |     1
2  |     1
3  |     2
4  |     5
5  |     1
6  |     1

Я хочу создать запрос для поиска блоков данных, содержащих один и тот же EmployeeID, где идентификатор является последовательным (с минимальным значением x записей). До сих пор я придумал:

SELECT EmployeeID, MIN(Id), MAX(Id), COUNT(*)
FROM recs
GROUP BY EmployeeID
HAVING COUNT(*) > 5 AND
       MAX(Id) - MIN(Id) + 1 = COUNT(*)

Этот запрос принесет мне некоторые (но не все) блоки данных, если один и тот же сотрудник может быть найден только в одном блоке. Может кто-нибудь придумать решение, которое предоставит все различные блоки данных для каждого сотрудника?

Ответы [ 4 ]

2 голосов
/ 23 февраля 2010

Присоединиться к той же таблице, где table1.Id = table2.Id + 1 и table1.employeeid = table2.employeeid

1 голос
/ 23 февраля 2010

Не лучшее решение, но оно должно работать (например, 3 последовательных идентификатора):

SELECT Id, EmployeeID FROM
(
SELECT r.Id, r.EmployeeID, 
(SELECT COUNT(1) FROM recs r1 WHERE (r1.EmployeeID = r.EmployeeID AND r1.id = r.Id-1) AS c1,
(SELECT COUNT(1) FROM recs r2 WHERE (r2.EmployeeID = r.EmployeeID AND r2.id = r.Id-2) AS c2,
(SELECT COUNT(1) FROM recs r3 WHERE (r3.EmployeeID = r.EmployeeID AND r3.id = r.Id-3) AS c3
FROM recs r1) tab1
WHERE (tab1.c1+tab1.c2+tab1.c3 =3);

Я предположил, что Id - это первичный (или уникальный) ключ. Если это не так, вы должны немного изменить каждый из подзапросов на что-то вроде SELECT IF (COUNT (1)> 0,1,0) .....

0 голосов
/ 23 февраля 2010

Ух ты, это был настоящий дразнилка мозга. Я уверен, что здесь есть все виды дыр, но вот возможное решение. Сначала наши данные испытаний:

If Exists(Select 1 From INFORMATION_SCHEMA.TABLES Where TABLE_NAME = 'recs')
    DROP TABLE recs
GO
Create Table recs
(
    Id int not null
    , EmployeeId int not null
)
Insert recs(Id, EmployeeId) 
Values (1,1) ,(2,1) ,(3,1) ,(4,2) ,(5,5) ,(6,1) ,(7,1) ,(8,1) ,(10,1)   
    ,(11,1) ,(12,1) ,(13,2) ,(14,2) ,(15,2) ,(16,2)

Далее вам потребуется таблица Tally or Numbers, содержащая последовательность чисел. Я поместил в него только 500 элементов, но, учитывая размер данных, вы можете захотеть больше. Наибольшее число в таблице Tally должно быть больше, чем наибольшее значение Id в таблице recs.

Create Table dbo.Tally(Num int not null)
GO
;With Numbers As
    (
    Select ROW_NUMBER() OVER ( ORDER BY s1.object_id) As Num
    From sys.columns as s1
    )
Insert dbo.Tally(Num)
Select Num
From Numbers
Where Num < 500

Теперь о фактическом решении. По сути, я использовал серию CTE для определения начальной и конечной точек последовательных последовательностей.

; With 
    Employees As 
    (
    Select Distinct EmployeeId 
    From dbo.Recs
    )
    , SequenceGaps As
    (
    Select E.EmployeeId, T.Num, R1.Id 
    From dbo.Tally As T
        Cross Join Employees As E
        Left Join dbo.recs As R1
            On R1.EmployeeId = E.EmployeeId
                And R1.Id = T.Num
    Where T.Num <= (    
        Select Max(R3.Id) 
        From dbo.Recs As R3
            Where R3.EmployeeId = E.EmployeeId
            )
    )
    , EndIds As
    (
    Select S.EmployeeId
        , Case When S1.Id Is Null Then S.Id End As [End]
    From SequenceGaps As S
        Join SequenceGaps As S1
            On S1.EmployeeId = S.EmployeeId
                And S1.Num = (S.Num + 1) 
    Where S.Id Is Not Null
        And S1.Id Is Null
    Union All
    Select S.EmployeeId, Max( Id )
    From SequenceGaps As S
    Where S.Id Is Not Null
    Group By S.EmployeeId
    )
    , SequencedEndIds As
    (
    Select EmployeeId, [End]
        , ROW_NUMBER() OVER (PARTITION BY EmployeeId ORDER BY [End]) As SequenceNum
    From EndIds
    )
    , StartIds As
    (
    Select S.EmployeeId
        , Case When S1.Id Is Null Then S.Id End As [Start]
    From SequenceGaps As S
        Join SequenceGaps As S1
            On S1.EmployeeId = S.EmployeeId
                And S1.Num = (S.Num - 1)
    Where S.Id Is Not Null
        And S1.Id Is Null
    Union All
    Select S.EmployeeId, 1 
    From SequenceGaps As S
    Where S.Id = 1
    )
    , SequencedStartIds As
    (
    Select EmployeeId, [Start]
        , ROW_NUMBER() OVER (PARTITION BY EmployeeId ORDER BY [Start]) As SequenceNum
    From StartIds
    )
    , SequenceRanges As
    (
    Select S1.EmployeeId, Start, [End]
    From SequencedStartIds As S1
        Join SequencedEndIds As S2
            On S2.EmployeeId = S1.EmployeeId
                And S2.SequenceNum = S1.SequenceNum
    )
Select *
From SequenceGaps As SG
Where Exists(
        Select 1
        From SequenceRanges As SR
        Where SR.EmployeeId = SG.EmployeeId
            And SG.Id Between SR.Start And SR.[End]
            And ( SR.[End] - SR.[Start] + 1 ) >= @SequenceSize
        )

Используя последний оператор в предложении WHERE и @SequenceSize, вы можете контролировать, какие последовательности возвращаются.

0 голосов
/ 23 февраля 2010

Используйте временную таблицу для этого.Используйте это решение:

SELECT EmployeeID, MIN(Id) AS Min, MAX(Id) AS Max, COUNT(*) AS Count
INTO #TempTable
FROM recs
GROUP BY EmployeeID

SELECT * FROM #TempTable WHERE
Count > 5 AND
       Max - Min + 1 = Count

РЕДАКТИРОВАННЫЙ ОТВЕТ

пожалуйста, попробуйте это:

SELECT * FROM(    
SELECT EmployeeID, MIN(Id) AS min, MAX(Id) AS max, COUNT(*) AS count
    FROM recs
    GROUP BY EmployeeID) AS Table
    WHERE Table.count > 5 AND
           Table.max - Table.min + 1 = Table.count
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...