Thiết lập
Tôi có một bảng khổng lồ ~ 115.382.254 hàng. Bảng tương đối đơn giản và ghi lại các hoạt động quy trình ứng dụng.
CREATE TABLE [data].[OperationData](
[SourceDeciveID] [bigint] NOT NULL,
[FileSource] [nvarchar](256) NOT NULL,
[Size] [bigint] NULL,
[Begin] [datetime2](7) NULL,
[End] [datetime2](7) NOT NULL,
[Date] AS (isnull(CONVERT([date],[End]),CONVERT([date],'19000101',(112)))) PERSISTED NOT NULL,
[DataSetCount] [bigint] NULL,
[Result] [int] NULL,
[Error] [nvarchar](max) NULL,
[Status] [int] NULL,
CONSTRAINT [PK_OperationData] PRIMARY KEY CLUSTERED
(
[SourceDeviceID] ASC,
[FileSource] ASC,
[End] ASC
))
CREATE TABLE [model].[SourceDevice](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
CONSTRAINT [PK_DataLogger] PRIMARY KEY CLUSTERED
(
[ID] ASC
))
ALTER TABLE [data].[OperationData] WITH CHECK ADD CONSTRAINT [FK_OperationData_SourceDevice] FOREIGN KEY([SourceDeviceID])
REFERENCES [model].[SourceDevice] ([ID])
Bảng được nhóm ở khoảng 500 cụm và trên cơ sở hàng ngày.
Ngoài ra, bảng được lập chỉ mục tốt bởi PK, số liệu thống kê được cập nhật và INDEXer bị phân mảnh mỗi đêm.
CHỌN dựa trên chỉ mục rất nhanh và chúng tôi không có vấn đề gì với nó.
Vấn đề
Tôi cần biết hàng cuối cùng (TOP) [End]
và được phân vùng bởi [SourceDeciveID]
. Để có được cuối cùng [OperationData]
của mọi thiết bị nguồn.
Câu hỏi
Tôi cần tìm cách giải quyết vấn đề này một cách tốt và không đưa DB đến giới hạn.
Nỗ lực 1
Lần thử đầu tiên là hiển nhiên GROUP BY
hoặc SELECT OVER PARTITION BY
truy vấn. Vấn đề ở đây cũng rất rõ ràng, mọi truy vấn đều phải quét theo thứ tự phân vùng / tìm hàng trên cùng. Vì vậy, truy vấn rất chậm và có tác động IO rất cao.
Ví dụ truy vấn 1
;WITH cte AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [SourceDeciveID] ORDER BY [End] DESC) AS rn
FROM [data].[OperationData]
)
SELECT *
FROM cte
WHERE rn = 1
Ví dụ truy vấn 2
SELECT *
FROM [data].[OperationData] AS d
CROSS APPLY
(
SELECT TOP 1 *
FROM [data].[OperationData]
WHERE [SourceDeciveID] = d.[SourceDeciveID]
ORDER BY [End] DESC
) AS ds
THẤT BẠI!
Nỗ lực 2
Tôi đã tạo một bảng trợ giúp để luôn giữ một tham chiếu đến hàng TOP.
CREATE TABLE [data].[LastOperationData](
[SourceDeciveID] [bigint] NOT NULL,
[FileSource] [nvarchar](256) NOT NULL,
[End] [datetime2](7) NOT NULL,
CONSTRAINT [PK_LastOperationData] PRIMARY KEY CLUSTERED
(
[SourceDeciveID] ASC
)
ALTER TABLE [data].[LastOperationData] WITH CHECK ADD CONSTRAINT [FK_LastOperationData_OperationData] FOREIGN KEY([SourceDeciveID], [FileSource], [End])
REFERENCES [data].[OperationData] ([SourceDeciveID], [FileSource], [End])
Để lấp đầy bảng, một trình kích hoạt đã tạo để luôn thêm / cập nhật hàng nguồn nếu [End]
cột cao hơn được chèn.
CREATE TRIGGER [data].[OperationData_Last]
ON [data].[OperationData]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
MERGE [data].[LastOperationData] AS [target]
USING (SELECT [SourceDeciveID], [FileSource], [End] FROM inserted) AS [source] ([SourceDeciveID], [FileSource], [End])
ON ([target].[SourceDeciveID] = [FileSource].[SourceDeciveID])
WHEN MATCHED AND [target].[End] < [source].[End] THEN
UPDATE SET [target].[FileSource] = source.[FileSource], [target].[End] = source.[End]
WHEN NOT MATCHED THEN
INSERT ([SourceDeciveID], [FileSource], [End])
VALUES (source.[SourceDeciveID], source.[FileSource], source.[End]);
END
Vấn đề ở đây là, nó cũng có tác động IO rất lớn và tôi không biết tại sao.
Như bạn có thể thấy ở đây trong kế hoạch truy vấn, nó cũng thực hiện quét toàn bộ [OperationData]
bảng.
Nó có tác động tổng thể rất lớn đến DB của tôi.
THẤT BẠI!
CREATE TABLE
tập lệnh nhưng bên trong kế hoạch truy vấn bạn sẽ thấy các phân vùng. Tôi sẽ chỉnh sửa câu hỏi.
PRIMARY KEY CLUSTERED
bạn nghĩ rằng nó có thể giúp đỡ?
SELECT [SourceID], [Source], [End] FROM inserted
một số cách quét bảng trên [OperationData]
.