2017-06-20 7 views
0

J'ai le tableau suivant:Pourquoi ma requête SELECT dure-t-elle si longtemps?

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] 

un select * from Notifications simple, prend 30 secondes alors qu'il n'y a que 800 000 lignes dans ce tableau.

Cette situation réelle est chaque utilisateur exécute la requête select * from notifications where DisplayDate is null and Fk_userId = [the user id] et cela crée une erreur de serveur dans mon application web .NET en disant quelque chose sur un interblocage.

Le plan d'exécution pour cette requête est assez simple enter image description here

Qu'est-ce que je fais mal? Ou pourquoi crée-t-il une erreur de blocage?

+0

il semble qu'il y ait plus de colonnes que ci-dessus - que vous créez déclaration n'est pas complète –

+0

puis-je savoir, pourquoi vous voulez sélectionner toute la table ? autant que je sache SQL Server effectuera un balayage de table simple. implique l'analyse de chaque ligne de la table, l'allocation de mémoire, et le retour qui sera le moyen le plus rapide pour retourner le résultat –

+4

30 secondes pour aller chercher 800 000 lignes est 26,667 lignes par seconde. Un réseau lent est 100 Mbps ou environ 12,5 Mo par seconde. Donc, si vos enregistrements de base de données ont en moyenne environ 500 octets chacun (assez facile si vous avez de longues données texte), le réseau pourrait limiter votre vitesse de transfert. –

Répondre

0

Il pourrait être dû à plusieurs raisons:

Peut-être que quelqu'un d'autre accède à cette table trop et verrouilla. Dans ce cas, essayez ceci:

SELECT * FROM Notifications WITH(NOLOCK) 

Parfois, le problème est lié à des indices manquants. Je ne pense pas que cela a à voir avec les index puisque vous avez une clé primaire. Cependant, vous devez vérifier le degré de fragmentation de cet index et le reconstruire si nécessaire. Un autre point clé pourrait être le query optimization statistics. Vous pouvez les mettre à jour en faisant ceci:

UPDATE STATISTICS Notifications; 

Selon votre dernière mise à jour, vous pouvez utiliser cette requête pour traiter le blocage:

-- 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 
+0

Je suis d'accord! D'autres raisons pour que votre requête soit lente, vous incluez toutes les colonnes et il n'y a pas de clause WHERE dessus. Vous pouvez penser que vous obtiendrez des avantages de performance puisque vous avez un index clusterisé, mais ce n'est pas –

+0

@luis Teijon ajoutant 'WITH (NOLOCK)' n'a pas semblé faire de changement dans make case même si cela pourrait être un problème depuis la requête sur cette table retourner une erreur d'application de blocage parfois (voir mon edit) – Greg

0

Vous avez un champ de texte (NotificationText) dans votre table. "Texte" dans SQL Server est un type de BLOB (Binary Large OBject). Ces types de champs sont très difficiles à optimiser. Vous pouvez utiliser l'indexation en texte intégral, mais cela devient compliqué.

Je voudrais essayer un select excluant ce champ. Ou, demandez-vous si vous avez vraiment besoin d'utiliser le texte. Nvarchar (8000) suffirait-il? Je parie que si vous supprimez ce champ de texte, ou si vous le redéfinissez comme un nvarchar, votre requête commencera soudainement à voler.

+0

Ça pourrait être ça ... Il va falloir du travail avant de pouvoir réellement tester ça. Merci pour le commentaire – Greg

+0

Bonne chance! Et s'il vous plaît envisager un upvote ou accepter la réponse si cela aide. –

+0

J'ai finalement essayé votre solution et il semble que ça l'a fait pire ... Je ne pouvais rien obtenir d'autre que des blocages sur la table par rapport à avant où j'obtiendrais une impasse de temps en temps – Greg

1

Vous avez demandé à propos de deux requêtes différentes.

L'une plus importante semble être

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

Le plan d'exécution montre que vous manquez un indice utile sont donc obtenir une analyse parallèle de la table.

Si vous fournissez un index, il devrait fonctionner beaucoup mieux et être moins enclin à l'impasse simplement en raison de la nécessité de lire moins de données.

Un exemple index pour cette requête serait

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