分层SQL查询

时间:2013-06-20 11:57:51

标签: mysql sql hierarchical-data hierarchical

我正在开发一个简单的CMS系统,我有一个包含下表的数据库:

Items
Contents
Langs

Items表具有以下结构:

itemId
name (for semantic reasons)
type
parent (foreign key to itemId)

项目可以是documentsection类型。节是文档上的一段内容,它通过父列链接到它。但是,文档也可以有一个父级,使其成为subpage

现在我不得不进行查询以分层次地从数据库中获取所有项目。所以像这样:

documentId => name
              metaDescription => language => meta
              sections => sectionId => language => title
                                                   content
                                                   uri
              subPages => documentId => name
                                        metaDescription
                                        sections => etc...

为了澄清一下,一个网站可以有多种语言在Langs表中,并且每种语言都链接到Contents表中的一段内容,该内容也链接到Items表。 metaDescription是与content类型的项目相关联的document列。

有一种方法可以通过一个查询执行此操作吗?这是我的第一次尝试,但它对subPages不起作用:

    SELECT
        documents.itemId        AS id,
        documents.name          AS documentName,
        documents.lastModified  AS lastModified,
        meta.content            AS metaDescription,
        meta.uri                AS documentUri,
        sections.itemId         AS sectionId,
        sections.name           AS sectionName,
        sections.lastModified   AS sectionLastModified,
        contents.name           AS sectionTitle,
        contents.content        AS sectionContent,
        contents.uri            AS contentUri,
        contents.lastModified   AS contentLastModified,
        langs.name              AS contentLang
    FROM 
        SITENAME_kw_items AS documents
            INNER JOIN
        SITENAME_kw_contents AS meta
        ON documents.itemId = meta.itemId
            INNER JOIN
        SITENAME_kw_items AS sections
        ON sections.parent = documents.itemId
            INNER JOIN
        SITENAME_kw_contents AS contents
        ON sections.itemId = contents.itemId
            INNER JOIN
        SITENAME_kw_langs AS langs
        ON langs.langId = contents.langId

很抱歉这个问题很长。希望你们能帮忙!

2 个答案:

答案 0 :(得分:0)

简短的回答是你不能用RDBMS真正做到这一点。答案很长,您可以通过编程方式(N + 1选择)进行排序,也可以使用公用表表达式(CTE)。

另一种选择是欺骗并使用深度列作为订单的提示。

答案 1 :(得分:0)

以下是我在“我们的”DMS(递归CTE)中的做法,这是Adam Gent的建议扩展。
请注意,我只是看到可以使用COALESCE而不是嵌套ISNULL。

您可以根据面包屑(此处为Bez_Path或UID_Path)执行此操作。

更好的方法是使用闭包表架构 见这里:
http://dirtsimple.org/2010/11/simplest-way-to-do-tree-based-queries.html
在这里:
http://www.mysqlperformanceblog.com/2011/02/14/moving-subtrees-in-closure-table/

闭包表还有一个优点,它适用于MySQL,CTE&不支持递归。

另请注意,闭包表比递归更好(并且查询更简单,更快) 还要考虑这种结构中的符号链接。
something_UID,something_parent_UID模式(如下所示)几乎总是反模式。

CREATE VIEW [dbo].[V_DMS_Navigation_Structure]
AS 
SELECT 
     NAV_UID 
    ,NAV_Typ 
    ,NAV_Parent_UID 
    ,NAV_Stufe 
    ,NAV_ApertureKey 
    ,NAV_Nr 
    --,NAV_Bemerkung 
    ,NAV_Status 
    ,NAV_Referenz 

    ,ISNULL(PJ_Bezeichnung, ISNULL(FO_Bezeichnung, DOC_Bezeichnung + '.' + DOC_Dateiendung)  ) AS NAV_Bezeichnung 
    ,NAV_PJ_UID 
    ,NAV_FO_UID 
    ,NAV_DOC_UID 
    ,ISNULL(NAV_PJ_UID, ISNULL(NAV_FO_UID,NAV_DOC_UID)) AS NAV_OBJ_UID 
FROM T_DMS_Navigation 

LEFT JOIN T_DMS_Projekt 
    ON T_DMS_Projekt.PJ_UID = T_DMS_Navigation.NAV_PJ_UID 

LEFT JOIN T_DMS_Folder 
    ON T_DMS_Folder.FO_UID = T_DMS_Navigation.NAV_FO_UID 

LEFT JOIN T_DMS_Dokument 
    ON T_DMS_Dokument.DOC_UID = T_DMS_Navigation.NAV_DOC_UID 








CREATE VIEW [dbo].[V_DMS_Navigation_Structure_Path]
AS 
WITH Tree 
(
     NAV_UID
    ,NAV_Bezeichnung
    ,NAV_Parent_UID
    ,Depth
    ,Sort
    ,Bez_Path
    ,UID_Path
    ,PJ_UID
    ,FO_UID
    ,DOC_UID
    ,OBJ_UID
) 
AS
(
    SELECT 
         NAV_UID 
        ,NAV_Bezeichnung 
        ,NAV_Parent_UID 
        ,0 AS Depth 
        ,CAST('0' AS varchar(10)) AS Sort 
        ,CAST(NAV_Bezeichnung AS varchar(4000)) AS Bez_Path 
        ,CAST(NAV_OBJ_UID AS varchar(4000)) AS UID_Path 
        ,NAV_PJ_UID AS PJ_UID 
        ,NAV_FO_UID AS FO_UID 
        ,NAV_DOC_UID AS DOC_UID 
        ,NAV_OBJ_UID AS OBJ_UID 
    FROM V_DMS_Navigation_Structure 

    WHERE NAV_Parent_UID IS NULL 

    UNION ALL 

    SELECT 
         CT.NAV_UID 
        ,CT.NAV_Bezeichnung 
        ,CT.NAV_Parent_UID 
        ,Parent.Depth + 1 AS Depth 
        ,CONVERT(varchar(10), Parent.Sort + '.' + CAST(Parent.Depth + 1 AS varchar(10))) AS Sort 
        ,CONVERT(varchar(4000), Parent.Bez_Path + '\' + CAST(CT.NAV_Bezeichnung AS varchar(1000))) AS Bez_Path 
        ,CONVERT(varchar(4000), Parent.UID_Path + '\' + CAST(CT.NAV_OBJ_UID AS varchar(1000))) AS UID_Path 
        ,NAV_PJ_UID AS PJ_UID 
        ,NAV_FO_UID AS FO_UID 
        ,NAV_DOC_UID AS DOC_UID 
        ,NAV_OBJ_UID AS OBJ_UID 
    FROM V_DMS_Navigation_Structure CT 

    INNER JOIN Tree AS Parent 
        ON Parent.NAV_UID = CT.NAV_Parent_UID
)

SELECT TOP 999999999999999 * FROM Tree
ORDER BY Depth