为什么我的SELECT查询需要这么长时间?

时间:2017-06-20 19:31:46

标签: sql-server select sqlperformance

我有下表:

CREATE TABLE [dbo].[Notifications](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Fk_institutionId] [int] NOT NULL,
[Fk_userId] [int] NOT NULL,
[Read] [bit] NOT NULL,
[CategoryId] [int] NOT NULL,
[Title] [nvarchar](150) NULL,
[NotificationText] [text] NULL,
[CreateDate] [datetime] NOT NULL,
[ReadDate] [datetime] NULL,
[DisplayDate] [datetime] NULL,
[ReadBy] [nvarchar](100) NULL,
CONSTRAINT [PK_Notifications] PRIMARY KEY CLUSTERED 
(
   [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

一个简单的select * from Notifications需要30秒,而此表中只有800,000行。

这种实际情况是每个用户执行查询select * from notifications where DisplayDate is null and Fk_userId = [the user id],这会在我的.NET Web应用程序中创建一个服务器错误,说明死锁。

该查询的执行计划非常简单enter image description here

我做错了什么?或者为什么会造成死锁错误?

3 个答案:

答案 0 :(得分:2)

您已经询问了两个不同的问题。

更重要的似乎是

SELECT *
FROM   notifications
WHERE  DisplayDate IS NULL
       AND Fk_userId = [the user id] 

执行计划显示您缺少任何有用的索引,因此正在对整个表进行并行扫描。

如果你提供一个索引,那么它应该表现得更好,并且只需要阅读更少的数据就可以减少死锁。

该查询的示例索引是

CREATE INDEX IX_Fk_userId_Where_DisplayDate_Is_Null
  ON notifications(Fk_userId)
  INCLUDE (DisplayDate) 
  WHERE DisplayDate IS NULL;

答案 1 :(得分:0)

可能是由于很多原因:

也许有人正在访问此表并锁定它。在这种情况下,试试这个:

SELECT * FROM Notifications WITH(NOLOCK)

有时问题与缺少索引有关。因为你有一个主键,我不认为它与索引有关。但是,您需要检查该索引的碎片程度,并在必要时重建它。

另一个关键点可能是query optimization statistics。您可以通过执行以下操作来更新它们:

UPDATE STATISTICS Notifications;

根据您上次更新,您可以使用此查询来处理阻止:

-- Detect blocking (run multiple times)  (Query 62) (Detect Blocking)
SELECT t1.resource_type AS [lock type], DB_NAME(resource_database_id) AS [database],
t1.resource_associated_entity_id AS [blk object],t1.request_mode AS [lock req],  --- lock requested
t1.request_session_id AS [waiter sid], t2.wait_duration_ms AS [wait time],       -- spid of waiter  
(SELECT [text] FROM sys.dm_exec_requests AS r WITH (NOLOCK)                      -- get sql for waiter
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) 
WHERE r.session_id = t1.request_session_id) AS [waiter_batch],
(SELECT SUBSTRING(qt.[text],r.statement_start_offset/2, 
    (CASE WHEN r.statement_end_offset = -1 
    THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2 
    ELSE r.statement_end_offset END - r.statement_start_offset)/2) 
FROM sys.dm_exec_requests AS r WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
WHERE r.session_id = t1.request_session_id) AS [waiter_stmt],                   -- statement blocked
t2.blocking_session_id AS [blocker sid],                                        -- spid of blocker
(SELECT [text] FROM sys.sysprocesses AS p                                       -- get sql for blocker
CROSS APPLY sys.dm_exec_sql_text(p.[sql_handle]) 
WHERE p.spid = t2.blocking_session_id) AS [blocker_stmt]
FROM sys.dm_tran_locks AS t1 WITH (NOLOCK)
INNER JOIN sys.dm_os_waiting_tasks AS t2 WITH (NOLOCK)
ON t1.lock_owner_address = t2.resource_address OPTION (RECOMPILE);

-- Helps troubleshoot blocking and deadlocking issues
-- The results will change from second to second on a busy system
-- You should run this query multiple times when you see signs of blocking

答案 2 :(得分:0)

您的表格中有一个文本字段(NotificationText)。 "文本"在SQL Server中是一种BLOB(二进制大对象)。这些Field类型很难优化。您可以使用全文索引,但它会变得复杂。

我会尝试排除该字段的选择。或者,问问自己是否真的需要使用Text。 Nvarchar(8000)会满足吗?

我敢打赌,如果删除该文本字段,或将其重新定义为nvarchar,您的查询将突然开始飞行。