简体   繁体   English

内部查询的sql优化

[英]sql optimisation for inner query

I have a site with pages, each page may belong to several categories. 我有一个页面网站,每个页面可能属于几个类别。 Users are assigned to categories, and if a page has is assigned a category that a user belongs they can see the page. 用户被分配到类别,并且如果为页面分配了用户所属的类别,则他们可以看到该页面。

originally each page belonged to one category, I could join with this table and check the permission like so 最初每个页面属于一个类别,我可以加入此表并检查权限,如此

where Sec.[level] > 0

Now that pages can have multiple categories I've attempted to change the where statement of the SP that filters out results, and although it works it is greatly inefficient. 既然页面可以有多个类别,我试图改变过滤掉结果的SP的where语句,虽然它有效但效率很低。 I am getting the max permission of all the categories, and if at least one is greater than zero then access is granted 我获得了所有类别的最大权限,如果至少有一个大于零,则授予访问权限

WHERE 
        (

            (TLA.RecipientTypeID = 2 and (SELECT MAX(CAST(dbo.PersonHasPermission_forSection(CS.SectionID, @pViewersID, 1) AS tinyint))
                                          FROM CONTENT_SECTION CS
                                          WHERE CS.ContentID = tla.RecipientID) > 0
            )
            OR
            (TLA.RecipientTypeID <> 2 AND Sec.[level] > 0)
        )

the or is because only pages have multiple cats, other types of content still have only one. 或者因为只有页面有多个猫,其他类型的内容仍然只有一个。

I don't know if there is enough info here but any optimisation tips would be greatly appreciated. 我不知道这里是否有足够的信息,但任何优化提示将不胜感激。

According your WHERE statement the main reason of slow execution is calling a function for each row in inner subquery. 根据您的WHERE语句,执行缓慢的主要原因是为内部子查询中的每一行调用一个函数。 I think you shouldn't search MAX() and compare it with 0 in this case you scan all table. 我认为您不应该搜索MAX()并将其与0进行比较,在这种情况下您扫描所有表。 Instead of it try to use EXISTS like this to find just one record with permission>0 - it's enought: 而不是尝试使用这样的EXISTS来查找只有一个权限> 0的记录 - 它应该是:

and EXISTS(SELECT * FROM CONTENT_SECTION CS 
             WHERE CS.ContentID = tla.RecipientID
             AND 
            (CAST(dbo.PersonHasPermission_forSection(CS.SectionID, @pViewersID, 1) 
                       AS tinyint)>0)
           ) 

In cases when you have inefficiency, it is often true that repeated calls to a function are to blame. 在效率低下的情况下,通常需要重复调​​用函数。 Sometimes there is a big efficiency gain by eliminating multiple calls to the function. 有时通过消除对函数的多次调用可以获得很大的效率提升。 One way is to use a temp table. 一种方法是使用临时表。 So, that might be something to explore. 所以,这可能是值得探讨的事情。

However, I want to take a step back from your specific SQL, because I think there may be an underlying problem with the data modeling and approach. 但是,我想从您的特定SQL退一步,因为我认为数据建模和方法可能存在潜在问题。 Taking your situation as described: 按照你所描述的情况:

I have a site with pages, each page may belong to several categories. 我有一个页面网站,每个页面可能属于几个类别。 Users are assigned to categories, and if a page has is assigned a category that a user belongs they can see the page. 用户被分配到类别,并且如果为页面分配了用户所属的类别,则他们可以看到该页面。

The best data model for a situation like this is three "entity" tables (Category, User, and Page) with many-to-many relations among them; 对于这种情况的最佳数据模型是三个“实体”表(类别,用户和页面),它们之间具有多对多关系; the many-to-many relations imply two additional tables for joining (UserCategory and PageCategory): 多对多关系意味着要加入两个额外的表(UserCategory和PageCategory):

具有5个表的示例数据库结构

In this case, to get a listing of all pages a particular user can see, you can just use a query like this sample (which is quite efficient): 在这种情况下,要获取特定用户可以看到的所有页面的列表,您可以使用类似此示例的查询(这非常有效):

SELECT p.*
FROM UserCategory uc
  INNER JOIN PageCategory pc ON uc.CategoryID = pc.CategoryID
  INNER JOIN Page p ON pc.PageID = p.PageID
WHERE uc.UserID = @pViewersID

This is a semi-cartesian join, but that is what you want in this case. 这是一个半笛卡尔式连接,但在这种情况下,这就是你想要的。 Also note that, for optimum SELECT efficiency with the given sample query, you would also want an index on UserCategory.UserID. 另请注意,为了使给定的示例查询具有最佳的SELECT效率,您还需要UserCategory.UserID上的索引。

Finally, I eliminated the [Level] column since it seemed like you only use it for a true/false check ("greater than 0"), which is already handled by row-exists-or-not within UserCategory. 最后,我删除了[Level]列,因为它似乎只用于真/假检查(“大于0”),这已经由UserCategory中的row-exists-or-not处理。 But, if you did need multiple permission levels (more than just 0 or 1), note that you could also include a [Level] column in UserCategory and reference it in the sample query for minimal extra cost. 但是,如果您确实需要多个权限级别(超过0或1),请注意您还可以在UserCategory中包含[Level]列,并在示例查询中引用它以获得最低的额外成本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM