具有数百万条记录的SQL查询优化

时间:2018-08-03 10:35:59

标签: sql sql-server

我目前正在使用以下查询来获取基于senderid的记录。我在messagein表中有200万条记录,并且该表中的条目也与此平行。 但是还需要5秒才能返回结果。该表仅在Providerid上创建了一个非聚集索引(包括列priorityid,senderid,maskid) 哪位SQL专家可以帮助我解决这个问题。

ALTER PROCEDURE [dbo].[GetNextSmsQueue] @NoOfRow int,
                                        @GatewayId int
AS
    BEGIN TRY
        BEGIN TRAN;
        CREATE TABLE #SmsIn ([Id] [bigint] NOT NULL,
                             [UserId] [bigint] NOT NULL,
                             [MaskId] [varchar](50) NOT NULL,
                             [Number] [varchar](20) NOT NULL,
                             [Message] [nvarchar](1300) NOT NULL,
                             [SenderId] [varchar](20) NOT NULL,
                             [UDH] [nvarchar](50) NULL,
                             [Credit] [int] NOT NULL,
                             [CurrentStatus] [int] NOT NULL,
                             [CheckDND] [bit] NULL,
                             [CheckFail] [bit] NULL,
                             [CheckBlackList] [bit] NULL,
                             [ProviderId] [int] NULL,
                             [PriorityId] [int] NULL,
                             [ScheduleDate] [datetime] NOT NULL,
                             [CreatedDate] [datetime] NOT NULL,
                             [EsmClass] [nvarchar](10) NOT NULL,
                             [DataCoding] [int] NOT NULL,
                             [Priority] [int] NOT NULL,
                             [Interface] [int] NOT NULL);
        DECLARE @PriorityIn table ([PriorityId] [int] NOT NULL);
        DECLARE @COUNT bigint;

        INSERT INTO @PriorityIn
        SELECT PriorityId
        FROM PriorityProviders
        WHERE ProviderId = @GatewayId
          AND Type = 0;

        SELECT @COUNT = COUNT(*)
        FROM MessageIn m
             LEFT JOIN @PriorityIn o ON m.PriorityId = o.PriorityId
        WHERE ((ProviderId IS NULL
            AND o.PriorityId IS NOT NULL)
            OR ProviderId = @GatewayId);

        IF @COUNT > 0
        BEGIN
            INSERT INTO #SmsIn ([Id],
                                [UserId],
                                [MaskId],
                                [Number],
                                [Message],
                                [SenderId],
                                [UDH],
                                [Credit],
                                [CurrentStatus],
                                [CheckDND],
                                [CheckFail],
                                [CheckBlackList],
                                [ProviderId],
                                [PriorityId],
                                [ScheduleDate],
                                [CreatedDate],
                                [EsmClass],
                                [DataCoding],
                                [Priority],
                                [Interface])
            (SELECT [Id],
                    [UserId],
                    [MaskId],
                    [Number],
                    [Message],
                    [SenderId],
                    [UDH],
                    [Credit],
                    [CurrentStatus],
                    [CheckDND],
                    [CheckFail],
                    [CheckBlackList],
                    [ProviderId],
                    [PriorityId],
                    [ScheduleDate],
                    [CreatedDate],
                    [EsmClass],
                    [DataCoding],
                    [Priority],
                    [Interface]
             FROM MessageIn
             WHERE MaskId IN (SELECT MaskId
                              FROM (SELECT ROW_NUMBER() OVER (PARTITION BY SenderId ORDER BY ScheduleDate) AS RowNo,
                                           MaskId
                                    FROM MessageIn msg
                                         LEFT JOIN @PriorityIn o ON msg.PriorityId = o.PriorityId
                                    WHERE ((msg.ProviderId IS NULL
                                        AND o.PriorityId IS NOT NULL)
                                        OR msg.ProviderId = @GatewayId)) res
                              WHERE res.RowNo <= @NoOfRow));

            DELETE msgin
            FROM MessageIn msgin
                 INNER JOIN #SmsIn temp ON msgin.MaskId = temp.MaskId;
        END;
        SELECT *
        FROM #SmsIn;
        DROP TABLE #SmsIn;
        COMMIT;
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0
        BEGIN
            ROLLBACK TRANSACTION;
        END;
    END CATCH;

执行计划可在此处获得: Execution Plan

更新的查询:

BEGIN TRY
begin tran;
CREATE TABLE #tmpMaskId (MaskId varchar(25) PRIMARY KEY)

INSERT INTO #tmpMaskId(MaskId)
SELECT DISTINCT MaskId From 
(SELECT  ROW_NUMBER() OVER ( PARTITION BY SenderId ORDER BY scheduledate ) AS RowNo, MaskId FROM MessageIn msg 
LEFT JOIN PriorityProviders o on o.ProviderId = @GatewayId AND o.Type = 0 and msg.PriorityId = o.PriorityId
WHERE  
((msg.ProviderId is null AND o.PriorityId is not null) OR msg.ProviderId = @GatewayId)
)as res WHERE res.RowNo <= @NoOfRow

Select [Id],[UserId],m.[MaskId],[Number],[Message],[SenderId],[UDH],[Credit],[CurrentStatus],[CheckDND],[CheckFail],[CheckBlackList],[ProviderId]
      ,[PriorityId],[ScheduleDate],[CreatedDate],[EsmClass],[DataCoding],[Priority],[Interface]
From MessageIn m inner join #tmpMaskId msk on m.MaskId = msk.MaskId

DELETE msgin
FROM MessageIn msgin
INNER JOIN #tmpMaskId temp ON msgin.MaskId=temp.MaskId

DROP TABLE #tmpMaskId
Commit;
END TRY
BEGIN CATCH
     IF @@TRANCOUNT > 0
     BEGIN
        ROLLBACK TRANSACTION;
     END
END CATCH;

2 个答案:

答案 0 :(得分:1)

ALTER PROCEDURE [dbo].[GetNextSmsQueue]
@NoOfRow    INT
,@GatewayId INT
AS
BEGIN TRY
BEGIN TRAN;

CREATE TABLE #tmpMaskId (MaskId INT PRIMARY KEY)

DECLARE @PriorityIn TABLE ([PriorityId] [INT] NOT NULL)

INSERT INTO @PriorityIn
SELECT PriorityId
FROM PriorityProviders
WHERE ProviderId=@GatewayId AND Type=0

INSERT INTO #tmpMaskId (MaskId)
SELECT DISTINCT MaskId
FROM (
     SELECT ROW_NUMBER() OVER (PARTITION BY SenderId ORDER BY ScheduleDate) AS RowNo
         ,MaskId
     FROM MessageIn msg
     WHERE ((msg.ProviderId IS NULL AND o.PriorityId IS NOT NULL) OR msg.ProviderId=@GatewayId)
     ) res
WHERE res.RowNo<=@NoOfRow

SELECT [Id]
    ,[UserId]
    ,[MaskId]
    ,[Number]
    ,[Message]
    ,[SenderId]
    ,[UDH]
    ,[Credit]
    ,[CurrentStatus]
    ,[CheckDND]
    ,[CheckFail]
    ,[CheckBlackList]
    ,[ProviderId]
    ,[PriorityId]
    ,[ScheduleDate]
    ,[CreatedDate]
    ,[EsmClass]
    ,[DataCoding]
    ,[Priority]
    ,[Interface]
FROM MessageIn mi
WHERE EXISTS (SELECT 1 FROM #tmpMaskId AS tmi WHERE tmi.MaskId=mi.MaskId)

DELETE msgin
FROM MessageIn msgin
INNER JOIN #tmpMaskId temp ON msgin.MaskId=temp.MaskId

COMMIT;
END TRY
BEGIN CATCH
IF @@TRANCOUNT>0 
BEGIN
         ROLLBACK TRANSACTION;
END;
END CATCH;

DROP TABLE #tmpMaskId

答案 1 :(得分:1)

IMO,根据您的要求,我将仅从此proc返回记录以发送短信。成功发送短信后,我仅将从Message表中的要求id发送到另一个proc来删除这些记录。

从技术上来说,这听起来不错。您现有的proc不会因为删除而变慢。但是在发送短信并再次尝试插入之前,无法删除它。

在我之前的文章中,我指出您不需要在PriorityProviders上加入。

我已经修改了脚本(如果可能的话,请改成内部)

    SET NOCOUNT ON
    BEGIN TRY
    begin tran;

    CREATE TABLE #tmpMaskId (MaskId varchar(25) not null)

    INSERT INTO #tmpMaskId(MaskId)
    SELECT MaskId From 
    (SELECT  ROW_NUMBER() OVER ( PARTITION BY SenderId ORDER BY scheduledate ) AS RowNo, MaskId FROM MessageIn msg with(nolock)
    LEFT JOIN PriorityProviders with(nolock)
  o on o.ProviderId = msg.ProviderId and o.ProviderId= @GatewayId AND o.Type = 0 and msg.PriorityId = o.PriorityId

    WHERE  
    ((msg.ProviderId is null AND o.PriorityId is not null) OR msg.ProviderId = @GatewayId)
    )as res WHERE res.RowNo <= @NoOfRow

    CREATE TABLE #tmpMaskId (MaskId INT not null)
    create clusetered index ix_mask on #tmpMaskId


    Select [Id],[UserId],m.[MaskId],[Number],[Message],[SenderId],[UDH],[Credit],[CurrentStatus]
    ,[CheckDND],[CheckFail],[CheckBlackList],[ProviderId]
          ,[PriorityId],[ScheduleDate],[CreatedDate],[EsmClass],[DataCoding],[Priority],[Interface]
    From MessageIn m 
    inner join 
    #tmpMaskId msk 
    on m.MaskId = msk.MaskId

    DELETE msgin
    FROM MessageIn msgin
    where exists(select 1 from #tmpMaskId temp where msgin.MaskId=temp.MaskId)

    DROP TABLE #tmpMaskId

    Commit;
    END TRY
    BEGIN CATCH
         IF @@TRANCOUNT > 0
         BEGIN
            ROLLBACK TRANSACTION;
         END
    END CATCH;

请注意我如何从临时表中删除PK并使它成为聚集索引。 我如何删除distinct吗?

现在的罪魁祸首是这句话,

ROW_NUMBER() OVER ( PARTITION BY SenderId ORDER BY scheduledate ) AS RowNo

我想一旦您发表评论,proc就会表现更好。

现在您只需要索引。

哪个列最有选择性,将该列设置为聚集索引。

由于我不知道每一列的选择性,所以我无法说出应该选择composite clustered还是composite Non clustered index

如果您使用Composite Non Clustered index,则将ID设为聚集索引(PK),并保留最多selective column on left side and so on

Composite Non Clustered index可以是(maskid,ProviderId,SenderId,PriorityId)Include(结果集中需要的消息表的其他列)

我不是在告诉您删除Row_number()。创建composite non clustered index并按照上面的描述重建索引。

With (nolock):与数据重复性无关。                      如果没有机会获得未提交的数据。                          如果消息表中没有太多并发问题,并且插入/更新不是很频繁。 然后,您就可以安全使用它了。您可以在Google中搜索“ Advantage and disadvantage of with (Nolock)”。 如果它可以改善重要查询,则可以在一两个地方使用它。

就像您说的那样,如果在maskid上创建索引,则会创建死锁。这是由于Insert中脚本错误所致。