使用SQL游标。担心表现。有什么办法用set操作吗?

时间:2014-11-26 18:10:35

标签: sql tsql cursor

我正在开发一个不断更新的生产表,它还处于起步阶段,但最终每分钟可能有多次插入(并且可能会达到数百万条目)。我对表结构无控制或输入数据的方式。

我需要计算_COFF在机器具有_SETUP状态后的数量。此计数必须每隔15分钟至少进行。该ID用作另一个表中的键,并且必须与输出中的MachineNumber和PartNumber相关联。 (下面的样本输出)

以下是我的工作内容:

'生产'表:

+---------------+---------------------+---------+------------+--------+
| MachineNumber |      DateTime       | Comment | PartNumber | Status |
+---------------+---------------------+---------+------------+--------+
|             1 | 11/11/2014 12:12:32 |         |        104 | _SETUP |
|             1 | 11/11/2014 12:12:40 |     155 |        104 | _ID    |
|             1 | 11/11/2014 12:12:45 |         |        104 | _CON   |
|             1 | 11/11/2014 12:16:45 |         |        104 | _COFF  |
|             1 | 11/11/2014 12:16:46 |         |        104 | _CON   |
|             1 | 11/11/2014 12:20:46 |         |        104 | _COFF  |
|             2 | 11/11/2014 12:20:50 |         |        223 | _SETUP |
|             1 | 11/11/2014 12:21:00 |         |        104 | _CON   |
|             1 | 11/11/2014 12:23:00 |         |        104 | _COFF  |
|             2 | 11/11/2014 12:25:00 |     543 |        223 | _ID    |
|             2 | 11/11/2014 12:25:20 |         |        223 | _CON   |
|             2 | 11/11/2014 12:26:20 |         |        223 | _COFF  |
            ...             ...             ...         ...      ...                       
+---------------+---------------------+---------+------------+--------+

目前我使用游标获取以下输出:

+---------------+------------+-----+-------------+
| MachineNumber |  DateTime  | ID  | _COFF Count |
+---------------+------------+-----+-------------+
|             1 | 11/11/2014 | 155 |           3 |
|             2 | 11/11/2014 | 543 |           1 |
+---------------+------------+-----+-------------+

无论如何要做到这一点比循环(可能)一百万个条目更好?如何删除已经循环的表中的记录,并将输出存储在另一个表中?

编辑:每台机器每个部件只有一个_SETUP和一个_ID,但每台机器会有多个部件(因此表中的每台机器都有多个_SETUP和_ID)。

2 个答案:

答案 0 :(得分:0)

select table.machinenumber, timestamp.starttime, id.comment, count(*) 
  from table 
  join ( select MachineNumber, min(comment) as commment
           from table
          where Status = '_ID' 
            and comment is not null 
          group by MachineNumber ) as id 
    on id.MachineNumber = table.machinenumber 
  join ( select MachineNumber, min(datetime) as starttime
           from table 
          where Status = '_SETUP'
          group by MachineNumber ) as timestamp 
     on timestamp.ID = table.ID 
    and timestamp.starttime < table.datetime 
    and table.Status = '_CON'
  group by table.machinenumber, timestamp.starttime, id.comment

答案 1 :(得分:0)

最好的办法是创建一个触发器,重置机器的上次设置日期,然后在索引视图中使用它。

假设您的表名为Status

CREATE INDEX
        IX_Status_Machine_Status_DateTime
ON      Status (machineNumber, status, dateTime)
GO

CREATE UNIQUE INDEX
        UX_Status_Machine_Part__Id
ON      Status (machineNumber, partNumber)
WHERE   status = '_ID'

CREATE TABLE
        Status_LastSetup
        (
        machineNumber INT NOT NULL PRIMARY KEY,
        partNumber INT NOT NULL,
        lastSetup DATETIME NOT NULL
        )
GO

CREATE TRIGGER
        TR_Status_All
ON      Status
AFTER   INSERT, UPDATE, DELETE
AS
WITH    c AS
        (
        SELECT  machineNumber
        FROM    INSERTED
        UNION ALL
        SELECT  machineNumber
        FROM    DELETED
        ),
        t AS
        (
        SELECT  sls.*
        FROM    c
        JOIN    Status_LastSetup sls
        ON      sls.machineNumber = c.machineNumber
        ),
        s AS
        (
        SELECT  *
        FROM    c
        CROSS APPLY
                (
                SELECT  TOP 1
                        partNumber, dateTime
                FROM    Status st
                WHERE   st.machineNumber = c.machineNumber
                        AND st.status = '_STATE'
                ORDER BY
                        dateTime DESC
                )
        )
ON      t.machineNumber = s.machineNumber
WHEN NOT MATCHED BY TARGET THEN
INSERT  (machineNumber, partNumber, lastSetup)
VALUES  (s.machineNumber, s.partNumber, s.dateTime)
WHEN MATCHED THEN
UPDATE
SET     t.dateTime = s.dateTime,
        t.partNumber = s.partNumber
WHERE   EXISTS
        (
        SELECT  t.dateTime, t.partNumber
        EXCEPT
        SELECT  s.dateTime, s.partNumber
        )
WHEN NOT MATCHED BY SOURCE THEN
DELETE
GO

CREATE VIEW
        V_Status_CoffCount
WITH SCHEMABINDING
AS
SELECT  s.machineNumber,
        COUNT_BIG(*) cnt
FROM    dbo.Status_LastSetup sls
JOIN    dbo.Status s
ON      s.machineNumber = sls.machineNumber
        AND s.status = '_COFF'
        AND s.dateTime >= sls.lastSetup
GROUP BY
        s.machineNumber
GO

CREATE UNIQUE CLUSTERED INDEX
        UX_V_Status_CoffCount_Machine
ON      V_Status_CoffCount (machineNumber)
GO

一旦启动并运行,只需运行此查询:

SELECT  s.machineNumber, sls.lastSetup, comment id, cnt
FROM    V_Status_CoffCount scc WITH (NOEXPAND)
JOIN    Status_LastSetup sls
ON      sls.machineNumber = scc.machineNumber
LEFT JOIN
        Status s
ON      s.machineNumber = sls.machineNumber
        AND s.partNumber = sls.partNumber
        AND s.status = '_ID'

它的执行时间与您拥有的机器数量呈线性关系,并且(几乎)不依赖于Status中的条目数。

在SQLFiddle上看到这一切:http://sqlfiddle.com/#!6/9671c/2