加入同一个表而不复制字段中的数据或使用 c#

Car*_*Dev 2 sql-server t-sql

我有下表(省略了几个字段):

CREATE TABLE [Device](
    [DeviceId] [int] IDENTITY(1,1) NOT NULL,
    [AuthorizedDate] [datetime2](0) NULL,
    [DeauthorizedDate] [datetime2](0) NULL,
    [MemberId] [int] NOT NULL,
    ...
 CONSTRAINT [PK_Device] PRIMARY KEY CLUSTERED 
(
    [DeviceId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)

该表用于跟踪每个成员的设备 - 他们现在只能同时激活 5 台设备,并且在 90 天内只能进行 3 次更改。此表不足以强制执行 3 更改规则(我不会详细说明)。为此,我决定创建一个日志表:

CREATE TABLE [SwapLog](
    [SwapLogId] [int] IDENTITY(1,1) NOT NULL,
    [MemberId] [int] NOT NULL,
    [DeauthorizationDate] [datetime] NOT NULL,
    [DeauthorizedDeviceId] [int] NOT NULL,
    [AuthorizationDate] [datetime] NULL,
    [AuthorizedDeviceId] [int] NULL,
 CONSTRAINT [PK_SwapLog] PRIMARY KEY CLUSTERED 
(
    [wapLogId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)

计划是在设备被取消授权时填充此表,然后在新设备随后被授权时填写 AuthorizationDate 和 Id

我需要填充 SwapLog 表,以便我可以正确执行规则。所以,如果Device表有以下记录:

DeviceId    AuthorizedDate          DeauthorizedDate        MemberId
60          2010-11-09 08:06:38     2010-11-10 00:00:00     2389
1795        2012-01-04 08:57:12     2013-09-02 15:32:59     2389
4467        2011-02-21 14:25:24     2013-09-02 15:33:52     2389
4733        2011-03-02 20:11:54     NULL                    2389
11835       2011-11-02 11:38:33     NULL                    2389
12707       2011-11-23 16:35:45     NULL                    2389
46663       2012-04-13 13:14:56     NULL                    2389
56651       2012-07-05 20:10:05     NULL                    2389
86247       2012-10-11 12:50:20     NULL                    2389
114493      2012-12-05 19:46:30     NULL                    2389
123867      2013-03-15 19:39:38     NULL                    2389
127595      2013-08-01 18:17:16     NULL                    2389
522826      2013-10-09 16:20:31     NULL                    2389
554246      2013-10-28 11:23:29     NULL                    2389
574637      2013-11-07 16:48:36     NULL                    2389
609731      2013-11-25 12:22:47     NULL                    2389
651808      2013-12-19 11:25:35     NULL                    2389
775957      2014-02-10 13:05:51     NULL                    2389
778288      2014-02-11 10:52:19     NULL                    2389
824090      2014-03-12 14:34:28     NULL                    2389
180842      2013-02-14 07:00:37     2013-08-23 09:49:58     225897
180857      2013-02-14 08:56:42     2013-08-23 09:50:01     225897
181974      2013-02-15 09:44:42     2013-08-23 09:50:04     225897
182026      2013-02-15 12:03:02     2013-08-23 09:50:07     225897
183139      2013-02-16 04:14:20     2014-04-11 09:44:47     225897
186993      2013-02-18 01:42:30     2014-04-11 09:44:55     225897
188424      2013-02-19 11:17:27     2014-04-11 09:45:02     225897
190263      2013-02-21 01:00:11     2014-04-11 09:45:10     225897
359562      2013-07-08 10:07:29     2014-04-11 09:45:17     225897
411095      2013-07-31 09:48:24     2013-08-16 15:08:39     225897
452576      2013-08-20 01:35:52     2013-08-23 09:50:09     225897
462050      2013-08-26 04:21:57     2013-09-06 16:16:14     225897
503700      2013-09-25 08:05:14     2013-10-15 15:02:43     225897
532174      2013-10-16 06:35:07     2013-10-16 12:33:32     225897
532177      2013-10-16 06:45:48     2013-10-16 12:33:35     225897
532301      2013-10-16 11:32:48     2013-10-16 12:33:38     225897
532340      2013-10-16 12:49:47     2013-10-23 13:13:29     225897
533323      2013-10-17 07:12:42     2013-10-23 13:13:37     225897
588192      2013-11-14 04:17:38     2013-12-17 09:16:34     225897
444955      2013-11-20 10:50:22     2013-12-11 09:09:05     225897
600932      2013-11-21 02:06:15     2013-12-17 12:58:32     225897
613745      2013-11-28 09:29:26     2014-02-13 17:21:25     225897
626805      2013-12-05 23:50:03     2014-02-13 17:21:35     225897
637629      2013-12-11 09:09:06     2014-02-13 17:21:42     225897
649121      2013-12-17 09:16:35     2014-02-13 17:21:38     225897
649238      2013-12-17 12:58:32     2014-02-13 17:21:31     225897
445006      2014-02-13 17:25:16     2014-02-14 13:41:05     225897
781025      2014-02-13 17:59:50     2014-02-14 13:41:11     225897
780992      2014-02-18 19:53:23     2014-03-20 08:30:12     225897
793156      2014-02-19 12:19:09     2014-03-20 09:12:42     225897
804491      2014-02-26 11:16:06     2014-03-20 10:40:43     225897
833582      2014-03-19 05:15:26     2014-04-11 09:45:57     225897
834404      2014-03-20 07:58:53     2014-04-11 09:45:52     225897
532426      2014-03-20 08:30:12     2014-04-11 09:45:30     225897
834414      2014-03-20 09:12:43     2014-04-11 09:45:46     225897
834446      2014-03-20 10:40:43     2014-04-11 09:45:36     225897
870418      2014-04-14 12:58:32 NULL    225897
871297      2014-04-15 07:16:26 NULL    225897
898213      2014-05-08 06:11:23 NULL    225897
Run Code Online (Sandbox Code Playgroud)

我希望 SwapLog 表具有以下内容:

SwapLogId       MemberId        DeauthorizationDate     DeauthorizedDeviceId    AuthorizationDate       AuthorizedDeviceId
1               2389            2010-11-10 00:00:00     60                      NULL                    NULL
2               2389            2013-09-02 15:32:59     1795                    2013-10-09 16:20:31     522826
3               2389            2013-09-02 15:33:52     4467                    2013-10-28 11:23:29     554246
Run Code Online (Sandbox Code Playgroud)

通过这种方式,我可以判断设备 1795 何时被取消授权,以及究竟是哪个设备在其位置获得了授权。

问题是我用来迁移数据的查询出错了。我在 authorizationDate 字段中收到重复的数据:

MemberId    DeauthorizedDate        DeauthorizedDeviceId    AuthorizedDate          AuthorizedDeviceId
2389        2013-09-02 15:32:59     1795                    2013-10-09 16:20:31     522826
2389        2013-09-02 15:32:59     1795                    2013-10-28 11:23:29     554246
2389        2013-09-02 15:32:59     1795                    2013-11-07 16:48:36     574637
2389        2013-09-02 15:32:59     1795                    2013-11-25 12:22:47     609731
2389        2013-09-02 15:33:52     4467                    2013-10-09 16:20:31     522826
2389        2013-09-02 15:33:52     4467                    2013-10-28 11:23:29     554246
2389        2013-09-02 15:33:52     4467                    2013-11-07 16:48:36     574637
2389        2013-09-02 15:33:52     4467                    2013-11-25 12:22:47     609731
Run Code Online (Sandbox Code Playgroud)

这是查询:

SELECT
    d.[MemberId]
    ,d.[DeauthorizedDate]
    ,d.[DeviceId] AS [DeauthorizedDeviceId]
    ,d2.[AuthorizedDate]
    ,d2.[DeviceId] AS [AuthorizedDeviceId]
FROM
    [Personalization].[Device] d
        LEFT JOIN [Personalization].[Device] d2 ON d.MemberId = d2.MemberId
WHERE
    d.DeauthorizedDate < d2.AuthorizedDate
    AND d2.AuthorizedDate <= DATEADD(day, 90, d.DeauthorizedDate)
    AND d.MemberId = 2389
ORDER BY MemberId, d.DeauthorizedDate
Run Code Online (Sandbox Code Playgroud)

我可以用 c# 写一些东西来做到这一点,但我怀疑这需要很长时间,因为我有大量的设备记录。任何帮助表示赞赏。

Pau*_*ite 5

有几种方法可以解决这个问题,以下是递归解决方案。

为了达到最佳性能,它需要 Devices 表上的几个索引,如果不是一般有用,可以在构建 SwapLog 行后删除:

CREATE UNIQUE NONCLUSTERED INDEX uq1
ON Personalization.Device 
    (MemberId, AuthorizedDate)
INCLUDE
    (DeviceId);

CREATE UNIQUE NONCLUSTERED INDEX uq2 
ON Personalization.Device 
    (MemberId, DeauthorizedDate) 
INCLUDE 
    (DeviceId) 
WHERE 
    DeauthorizedDate IS NOT NULL;
Run Code Online (Sandbox Code Playgroud)

递归查询本身是:

WITH R AS
(
    -- Anchor: Earliest deauthorization per member
    -- and its associated authorization, if any
    SELECT
        FirstDeauthPerMember.MemberId,
        FirstDeauthPerMember.DeauthorizedDate,
        DeauthorizedDeviceId = FirstDeauthPerMember.DeviceId,
        Auth.AuthorizedDate,
        AuthorizedDeviceId = Auth.DeviceId,
        RunningAuthDate = ISNULL(Auth.AuthorizedDate, FirstDeauthPerMember.DeauthorizedDate)
    FROM
    (
        SELECT
            DeauthPerMember.MemberId,
            DeauthPerMember.DeauthorizedDate,
            DeauthPerMember.DeviceId
        FROM
        (
            SELECT 
                D.MemberId,
                D.DeauthorizedDate,
                D.DeviceId,
                RN = ROW_NUMBER() OVER (
                    PARTITION BY D.MemberId 
                    ORDER BY D.DeauthorizedDate)
            FROM Personalization.Device AS D
            WHERE
                D.DeauthorizedDate IS NOT NULL
        ) AS DeauthPerMember
        WHERE
            DeauthPerMember.RN = 1
    ) AS FirstDeauthPerMember
    OUTER APPLY
    (
        SELECT TOP (1)
            D2.AuthorizedDate,
            D2.DeviceId
        FROM Personalization.Device AS D2
        WHERE
            D2.MemberId = FirstDeauthPerMember.MemberId
            AND D2.AuthorizedDate > FirstDeauthPerMember.DeauthorizedDate
            AND D2.AuthorizedDate <= DATEADD(DAY, 90, FirstDeauthPerMember.DeauthorizedDate)
        ORDER BY
            D2.AuthorizedDate ASC
    ) AS Auth

    UNION ALL

    -- Recursive part: next deauthorization in order
    -- and its associated authorization, if any
    SELECT
        D6.MemberId,
        D6.DeauthorizedDate,
        D6.DeauthorizedDeviceId,
        D6.AuthorizedDate,
        D6.AuthorizedDeviceId,
        D6.RunningAuthDate
    FROM 
    (
        SELECT
            D5.MemberId,
            D5.DeauthorizedDate,
            D5.DeauthorizedDeviceId,
            D5.AuthorizedDate,
            D5.AuthorizedDeviceId,
            D5.RunningAuthDate
        FROM 
        (
            SELECT
                D.MemberId,
                D.DeauthorizedDate,
                DeauthorizedDeviceId = D.DeviceId,
                D4.AuthorizedDate,
                AuthorizedDeviceId = D4.DeviceId,
                RN = ROW_NUMBER() OVER (
                    ORDER BY D.DeauthorizedDate),
                RunningAuthDate = ISNULL(D4.AuthorizedDate, R.RunningAuthDate)
            FROM Personalization.Device AS D
            JOIN R
                ON R.MemberId = D.MemberId
            OUTER APPLY
            (
                SELECT
                    D3.AuthorizedDate,
                    D3.DeviceId
                FROM 
                (
                    SELECT
                        D2.AuthorizedDate,
                        D2.DeviceId,
                        RN = ROW_NUMBER() OVER (
                            ORDER BY D2.AuthorizedDate)
                    FROM Personalization.Device AS D2
                    WHERE
                        D2.MemberId = D.MemberId
                        AND D2.AuthorizedDate > ISNULL(R.AuthorizedDate, R.RunningAuthDate)
                        AND D2.AuthorizedDate > D.DeauthorizedDate
                        AND D2.AuthorizedDate <= DATEADD(DAY, 90, D.DeauthorizedDate)
                ) AS D3
                WHERE
                    D3.RN = 1
            ) AS D4
            WHERE
                D.DeauthorizedDate IS NOT NULL
                AND D.DeauthorizedDate > R.DeauthorizedDate
        ) AS D5
        WHERE
            D5.RN = 1
    ) AS D6
)
SELECT
    R.MemberId,
    R.DeauthorizedDate,
    R.DeauthorizedDeviceId,
    R.AuthorizedDate,
    R.AuthorizedDeviceId
FROM R
ORDER BY
    R.MemberId,
    R.DeauthorizedDate
OPTION (MAXRECURSION 0);
Run Code Online (Sandbox Code Playgroud)

输出:

输出

SQLFiddle(由于 SQLFiddle 权限限制而使用 dbo 架构)

执行计划:

执行计划