web-dev-qa-db-fra.com

Comment améliorer les performances pour le filtrage date/heure dans SQL Server?

J'ai un problème avec le filtrage par colonnes datetime

J'ai essayé ces deux méthodes:

datefield < '2013-03-15 17:17:55.179'
datefield < CAST('2013-03-15 17:17:55.179' AS datetime)

J'ai une grande base de données avec plus de 3.000.000 objets principaux.

J'ai donc besoin d'améliorer les performances pour mon filtrage datetime. Je lisais à propos de l'horodatage UNIX (convertissez toutes les datetime en horodatage UNIX, puis filtrez selon ce champ UNIX).

Je pense que c'est un meilleur moyen que de filtrer par datetime. Mais si quelqu'un connaissait un autre moyen, je l'apprécierais.

Ma requête est:

SELECT TOP (100)  ev.Title as Event_name, po.Name as POI_name, 
po.Address, po.City, po.Region, po.Country, po.Latitude, po.Longitude, ev.Start_time, 
(Select ID_Category FROM SubCategory s where ev.ID_SubCategory = s.ID_SubCategory) as ID_Category, 
ev.ID_SubCategory, ev.ID_Event, ev.ID_Channel, IDChanelEvent, 
ev.FavoriteCount, po.gmtOffset, v.IsFavorite, v1.IsFavorite  
FROM Events ev 
JOIN POI po ON ev.ID_POI = po.ID_POI 
JOIN (SELECT et.id_event as joinIdEv FROM EventTagLink et, tags t 
 WHERE t.id_tag = et.id_tag 
 AND ( t.Title = N'music' ) 
 ) as joinEvents 
 ON joinEvents.joinIdEv = ev.ID_Event 
LEFT JOIN Viewed v ON v.ID_Event = ev.ID_Event AND v.ID_User = 1 AND v.IsFavorite = 1 LEFT join Viewed v1 ON v1.ID_Event = ev.ID_Event AND v1.ID_User = 1 AND v1.IsFavorite = 0
WHERE 
--ev.GmtStop_time > '2013-03-15 14:17:55.188' AND 
po.Latitude > 41.31423 AND po.Latitude < 61.60511 
AND  po.Longitude > -6.676602 AND po.Longitude < 17.04498  
AND ev.ID_SubCategory in (3, 12, 21, 4, 30, 13, 22, 6, 14, 40, 23, 7, 32, 15, 41, 8, 50, 33, 16, 42, 25, 9, 34, 17, 35, 18, 44, 27, 36, 19, 45, 28, 37, 46, 29, 38, 47, 39, 48, 49, 10, 1, 11, 2, 20) 
--AND ev.GmtStart_time< '2013-03-15 17:17:55.179'
AND v1.IsFavorite is null

filtrer au moment où j'ai commenté.

Si je désactive ces filtres, la durée de la demande est de plusieurs secondes. Si je les allume, la durée de la demande est supérieure à 25 secondes.

Il y a donc beaucoup de discussions sur l'exécution des plans, des index, etc. Mais que dire de UNIX timestamp, qui est la raison principale pour laquelle j'ai posé la question ici. Cela améliorerait-il les performances du filtrage datetime? 

Merci d'avance.

28
Sasha Fentsyk

Juste une suggestion quand il s'agit d'index sur datetime dans msql, l'empreinte d'index a un impact sur les temps de recherche (Oui, cela semble évident ... mais lisez la suite). 

L'importance de ceci lors de l'indexation sur le datetime indique par exemple '2015-06-05 22: 47: 20.102', l'index doit prendre en compte chaque lieu dans le datetime. Cela devient très volumineux et encombrant. Une approche efficace que j'ai exploitée consiste à créer une nouvelle colonne datetime et à renseigner les données en arrondissant l'heure à l'heure, puis en générant l'index sur cette nouvelle colonne. Exemple '2015-06-05 22: 47: 20.102' se traduit par '2015-06-05 22: 00: 00.000'. En adoptant cette approche, nous laissons les données détaillées seules et pouvons les afficher ou les utiliser en effectuant une recherche dans cette nouvelle colonne, ce qui nous donne un retour d’environ 10 fois (au minimum) la rapidité avec laquelle les résultats sont renvoyés. Cela est dû au fait que l'index n'a pas à prendre en compte les champs minutes, secondes et millisecondes.

27
Sean Ford

Vous devez d'abord examiner votre plan d'exécution pour voir ce que fait SQL Server. Plus que probablement, vous avez juste besoin d'ajouter un index. Les petites conversions de ce genre ne sont presque jamais la raison pour laquelle votre requête est lente. Les index sont un bon point de départ pour la résolution des requêtes.

Vous n'avez pas besoin d'en faire un index clusterisé. Le fait de l'indexer en cluster signifie que vous n'avez pas besoin de faire une recherche, mais pour seulement 100 lignes, la recherche est très rapide. Je mettrais date-heure et sous-catégorie dans un index non clusterisé, dans cet ordre.

Si vous commandez, vous devez également vous assurer qu'il est dans un index. Comme il est logique d'utiliser un seul index par table, vous devez vous assurer que toutes les colonnes pertinentes sont dans le même index, dans le bon ordre.

Mais d’abord, obtenez votre plan d’exécution!

3
John Tseng

Pour de meilleures performances, je vous suggère de créer de nouveaux index:

CREATE INDEX x1 ON LiveCity.dbo.Tags(Title) INCLUDE(ID_Tag)
CREATE INDEX x2 ON LiveCity.dbo.Tags(ID_Event, GmtStart_time, GmtStop_time) 
  INCLUDE(
          FavoriteCount, 
          ID_Channel, 
          ID_POI, 
          ID_SubCategory, 
          IDChanelEvent, 
          Start_time, 
          Title
          )
CREATE INDEX x ON LiveCity.dbo.POI(ID_POI, Latitude, Longitude) 
  INCLUDE(
          Address, 
          City, 
          Country, 
          gmtOffset, 
          Name, 
          Region
          )

Cela vous aidera à éviter l'opération de recherche RID et à améliorer les performances globales de la requête.

2

Essaye celui-là -

;WITH cte AS (
     SELECT IsFavorite, ID_Event  
     FROM Viewed
     WHERE ID_User = 1 
)
SELECT TOP (100)
      Event_name = ev.Title 
    , POI_name = po.Name 
    , po.[address]
    , po.City
    , po.Region
    , po.Country
    , po.Latitude
    , po.Longitude
    , ev.start_time
    , s.ID_Category
    , ev.ID_SubCategory
    , ev.ID_Event
    , ev.ID_Channel
    , IDChanelEvent
    , ev.FavoriteCount
    , po.gmtOffset
    , v.IsFavorite
    , IsFavorite = NULL
FROM [events] ev
JOIN POI po ON ev.ID_POI = po.ID_POI
LEFT JOIN SubCategory s ON ev.ID_SubCategory = s.ID_SubCategory
LEFT JOIN cte v ON v.ID_Event = ev.ID_Event AND v.IsFavorite = 1
WHERE po.Latitude BETWEEN 41.31423 AND 61.60511
     AND po.Longitude BETWEEN -6.676602 AND 17.04498
     AND ev.ID_SubCategory IN (3, 12, 21, 4, 30, 13, 22, 6, 14, 40, 23, 7, 32, 15, 41, 8, 50, 33, 16, 42, 25, 9, 34, 17, 35, 18, 44, 27, 36, 19, 45, 28, 37, 46, 29, 38, 47, 39, 48, 49, 10, 1, 11, 2, 20)
     AND v1.IsFavorite IS NULL
     AND EXISTS(
          SELECT 1 
          FROM EventTagLink et
          WHERE t.Title = 'music'
               AND et.joinIdEv = ev.ID_Event
     )
     AND NOT EXISTS (
          SELECT * 
          FROM cte v1 
          WHERE v1.ID_Event = ev.ID_Event AND v1.IsFavorite = 0
     )
0
Devart