2010-04-26 18 views
8

Estoy tratando de implementar hierarchyID en una tabla (dbo. [Message]) que contiene aproximadamente 50,000 filas (crecerá sustancialmente en el futuro). Sin embargo, se necesitan entre 30 y 40 segundos para recuperar unos 25 resultados.Pregunta sobre el rendimiento en profundidad de HierarchyID de SQL Server

El nodo raíz es un relleno para proporcionar unicidad, por lo tanto, cada fila posterior es un elemento secundario de esa fila ficticia.

Necesito poder recorrer la profundidad de la tabla primero y he hecho que la columna hierarchyID (dbo. [Message] .MessageID) la clave principal del clúster, también haya agregado una smallint calculada (dbo. [Message] .Hierarchy) que almacena el nivel del nodo.

Uso: Una aplicación .Net pasa a través de un valor de hierarchyID en la base de datos y quiero poder recuperar todos (si los hay) hijos Y padres de ese nodo (además de la raíz, ya que es rellenador).

Una versión simplificada de la consulta que estoy utilizando:

@MessageID hierarchyID /* passed in from application */ 

SELECT 
m.MessageID, m.MessageComment 

FROM 
dbo.[Message] as m 

WHERE 
m.Messageid.IsDescendantOf(@MessageID.GetAncestor((@MessageID.GetLevel()-1))) = 1 

ORDER BY 
m.MessageID 

Por lo que entiendo, el índice debe ser detectada automáticamente sin una pizca.

Desde la búsqueda de foros, he visto personas que utilizan consejos de índice cuando se trata de índices amplios, pero no han observado esta aplicación en situaciones de profundidad. ¿Sería ese un enfoque relevante para mi escenario?

He pasado los últimos días tratando de encontrar una solución para este problema, pero fue en vano. Agradecería cualquier ayuda, y como esta es mi primera publicación, me disculpo por adelantado si esto se considera una pregunta 'noobish', he leído la documentación de MS y buscado en innumerables foros, pero no he encontrado una descripción sucinta del problema específico.

+0

Por cierto, la consulta que tiene? Tal como está escrito, siempre va a seleccionar TODOS los nodos en toda la tabla. El '@ MessageID.GetAncestor (@ MessageID.GetLevel() - 1)' lleva todo el camino hasta la raíz, y luego selecciona todo lo que es un descendiente, que es ... todo. Por eso es tan lento. – Aaronaught

+0

Solo para aclarar: mi situación requiere el uso de la indexación de profundidad en primer lugar, perdón por la confusión (me refiero a la amplitud primero al final simplemente para proporcionar un ejemplo de donde las personas han sugerido usar consejos de índice) – ObjectiveCat

Respuesta

2

solución encontrado aquí: http://connect.microsoft.com/SQLServer/feedback/details/532406/performance-issue-with-hierarchyid-fun-isdescendantof-in-where-clause#

Sólo recuerda que empecé con un heirarchyID transcurrido desde la aplicación y mi objetivo es recuperar cualquiera y todos los familiares de ese valor (ambos ascendientes y descendientes).

En mi ejemplo específico, he tenido que añadir las siguientes declaraciones antes de la SELECT declaración:

declare @topNode hierarchyid = (select @messageID.GetAncestor((@messageID.GetLevel()-1))) 
declare @topNodeParent hierarchyid = (select @topNode.GetAncestor(1)) 
declare @leftNode hierarchyid= (select @topNodeParent.GetDescendant (null, @topNode)) 
declare @rightNode hierarchyid= (select @topNodeParent.GetDescendant (@topNode, null)) 

La cláusula WHERE se ha cambiado a:

aumento de rendimiento
messageid.IsDescendantOf(@topNode)=1 AND (messageid > @leftNode) AND (messageid < @rightNode) 

La consulta es muy significativa :

Para cada resultado pasado, el tiempo de búsqueda es ahora de 20 ms en promedio (de 120 a 420).

Al consultar 25 valores, anteriormente tardaba de 25 a 35 segundos en devolver todos los nodos relacionados (en algunos casos, cada valor tenía muchos parientes, en algunos no existían). Ahora solo lleva 2 segundos.

Muchas gracias a todos los que han contribuido a este problema en este sitio y en otros.

8

No está del todo claro si está tratando de optimizar la búsqueda en profundidad o amplia; la pregunta sugiere profundidad primero, pero los comentarios al final son acerca de la amplitud primero.

Tiene todos los índices que necesita para la profundidad primero (simplemente indexe la columna hierarchyid). Para primero en amplitud, que no es suficiente sólo para crear columna calculada level, hay que también indexarla:

ALTER TABLE Message 
ADD [Level] AS MessageID.GetLevel() 

CREATE INDEX IX_Message_BreadthFirst 
ON Message (Level, MessageID) 
INCLUDE (...) 

(Tenga en cuenta que para los índices no agrupados lo más probable es necesario el INCLUDE - de lo contrario , SQL Server puede recurrir a hacer un análisis de índice agrupado en su lugar.)

Ahora, si está tratando de encontrar todos los antecesores de un nodo, desea tomar un rumbo ligeramente diferente. Puede hacer estas búsquedas a la velocidad del rayo, porque - y esto es lo bueno de hierarchyid - cada nodo ya "contiene" todos sus antepasados.

que utiliza una función CLR para hacer esto lo más rápido posible, pero puede hacerlo con un CTE recursiva:

CREATE FUNCTION dbo.GetAncestors 
(
    @h hierarchyid 
) 
RETURNS TABLE 
AS RETURN 
WITH Hierarchy_CTE AS 
(
    SELECT @h AS id 

    UNION ALL 

    SELECT h.id.GetAncestor(1) 
    FROM Hierarchy_CTE h 
    WHERE h.id <> hierarchyid::GetRoot() 
) 
SELECT id FROM Hierarchy_CTE 

Ahora, para obtener todos los ascendientes y descendientes, utilizar de esta manera:

DECLARE @MessageID hierarchyID /* passed in from application */ 

SELECT m.MessageID, m.MessageComment 
FROM Message as m 
WHERE m.MessageId.IsDescendantOf(@MessageID) = 1 
OR m.MessageId IN (SELECT id FROM dbo.GetAncestors(@MessageID.GetAncestor(1))) 
ORDER BY m.MessageID 

Pruébelo - esto debería resolver sus problemas de rendimiento.

+0

Disculpe por la ¡confusión, profundidad primero es de hecho lo que busco! Muchas gracias por la sugerencia, lo intentaré de inmediato. – ObjectiveCat

+0

Sólo para propósitos de prueba, que han eliminado por completo @ MessageID.GetAncestor dejando sólo: m.MessageId.IsDescendantOf (@MessageID) = 1 en la cláusula WHERE y cuando me encontré que proc, el tiempo de búsqueda es todavía entre 150 a 420ms por resultado, que es muy lento para mi aplicación. El rendimiento es una prioridad y no estoy completamente familiarizado con CLR, sin embargo, me gustaría aprender cómo implementarlo, si eso proporciona el mejor rendimiento. ¿Alguna sugerencia según dónde comenzar? – ObjectiveCat

+0

@AndalusianCat: la versión de CLR es para la consulta de ancestros. Si le resulta lento utilizar 'IsDescendantOf', publique una consulta real, un esquema de tabla (incluidos los índices) y el plan de ejecución. 'hierarchyid' consultas suelen ser mucho más rápido que eso. – Aaronaught

Cuestiones relacionadas