【问题标题】:SQL Server 2019 : Subquery Join won't executeSQL Server 2019:子查询加入不会执行
【发布时间】:2022-01-24 14:59:46
【问题描述】:

我必须设计一个报告,该报告将是同一张表中一堆重叠数据的列表。

我必须从Project 表开始,因为对于报告,@ProjectId 是唯一作为锚点的东西。

RessourceBookings 表中是所有预订,有些是重叠的

  • 项目 A 图书资源 B 24.01.,上午 10 点至下午 6 点
  • 项目 C 图书资源 B 24.01.,上午 11 点到下午 1 点
  • 项目 D 书籍资源 B 24.01., 1 p.m.到下午 4 点

这些是我必须过滤并列出项目 A 的所有预订冲突。

这是我目前想出的代码。我没有缩短它,因为我想表明某些表存在一些依赖关系,这使它有点复杂(至少对我而言)。如果除了主要问题还有其他可以优化的部分,我很乐意学习。

DECLARE @ProjectId INT = 8501

SELECT 
    p.Id AS 'Project-ID'
    ,p.Name AS 'Project-Name'
    ,p.Info AS 'Project-Info'
    ,pc.Name AS 'Project-Color'
    ,upr.DocumentName AS 'Project-Responsible'
    ,r.ResourceName AS 'Ressource-Name'
    ,r.Info AS 'Ressource-Info'
    ,CASE WHEN ugrr.ResourceRoleId IN (4,7)
        THEN 0
        ELSE 1
        END AS 'Ressource-Availability'
    ,r.Quantity AS 'Booking-Quantity'
    ,rb.Start AS 'Booking-Start'
    ,rb.[End] AS 'Booking-End'
    ,pat.Name AS 'Booking-Action'
    ,rb.IsOption AS 'Booking-State'
    ,rb.OptionRequestAccepted AS 'Booking-Response'
    ,q2.ProjectId AS 'Conflict-Project-ID'
    ,q2.Name AS 'Conflict-Project-Name'
    ,q2.DocumentName AS 'Conflict-Project-Responsible'
    ,q2.Start AS 'Conflict-Start'
    ,q2.[End] AS 'Conflict-End'
    ,q2.IsOption AS 'Conflict-State'
FROM 
    Projects p
LEFT JOIN 
    ProjectColors pc ON pc.Id = p.ProjectColorId
LEFT JOIN 
    Users upr ON upr.Id = p.ResponsibleUserId
LEFT JOIN 
    ResourceBookings rb ON rb.ProjectId = p.Id
LEFT JOIN 
    Resources r ON r.Id = rb.ResourceId
LEFT JOIN 
    ProjectActions pa ON pa.Id = rb.ProjectActionId
LEFT JOIN 
    ProjectActionTypes pat ON pat.Id = pa.ProjectActionTypeId
LEFT JOIN 
    UserGroupResourceRoles ugrr ON ugrr.ResourceId = r.Id
LEFT JOIN 
    (SELECT 
         p2.ProjectId
         ,p2.Name
         ,upr2.DocumentName
         ,rb2.Start
         ,rb2.[End]
         ,rb2.IsOption
     FROM 
         ResourceBookings rb2
     LEFT JOIN 
         Projects p2 ON p2.Id = rb2.ProjectId
     LEFT JOIN 
         Users upr2 ON upr2.Id = p2.ResponsibleUserId
     WHERE 
         rb2.ResourceId = r.Id
         AND (rb2.Start BETWEEN DATEADD(s, +1, rb.Start) AND DATEADD(s, -1, rb.[End])
              OR rb2.[End] BETWEEN DATEADD(s, +1, rb.Start) AND DATEADD(s, -1, rb.[End])
              OR DATEADD(s,+1,rb.Start) BETWEEN rb2.Start AND rb2.[End])) q2
WHERE 
    p.Id = @ProjectId
    AND rb.Start >= SysDateTime()
    AND ugrr.UserGroupId = 11
    AND (SELECT Count(rb2.Id) - 1
         FROM ResourceBookings rb2
         WHERE rb2.ResourceId = r.Id
           AND (rb2.Start BETWEEN DATEADD(s, +1, rb.Start) AND DATEADD(s, -1, rb.[End])
                OR rb2.[End] BETWEEN DATEADD(s, +1, rb.Start) AND DATEADD(s, -1, rb.[End])
                OR DATEADD(s, +1, rb.Start) BETWEEN rb2.Start AND rb2.[End])) > 0

我的目标是这样的:

Project-ID Project-Name [...] Conflict-Project-ID Conflict-Project-Name [...]
1 Project A [...] 2 Project B [...]
1 Project A [...] 3 Project C [...]
1 Project A [...] 4 Project D [...]

没有整个 q2 部分,列出项目 A 的所有信息效果很好。

错误是:

WHERE 关键字附近的语法错误。本机错误:156。SQLSTATE:42000。严重性 15。消息状态 1。第 76 行。

“>”附近的语法错误。本机错误:102。SQLSTATE:42000。严重性 15。MsgState 1。第 87 行。

没有子查询连接,它工作得很好,但我想我需要那个。我在过去两天搜索了谷歌,但找不到任何解决方案。在我看来,这似乎是正确的。

我无法更改表格或其内容。我必须处理设置。所以临时表不是一种选择(这可能会有所帮助)。

(但请不要使用一个单词的技术术语答案。我不是开发人员,我只是从事这类工作。我愿意学习,但如果没有专业背景)



编辑: 为澄清起见,这里是表格ResourceBookings(顶部)的图片,底部是查询的“目标”如果@ProjectId = 8525

【问题讨论】:

  • 错误来自最后一个子查询。您不能在 where 子句中使用聚合函数,而是使用 count(q2.id)>1。为此,您需要在左连接子查询中添加 rb2.id ------ 而且您在最后一个左连接中缺少连接条件,它只是以 q2 结束并开始 where 子句开始。即使你修复了 where 问题,join 也会抛出错误
  • 旁白:看来你的LEFT JOINs 应该是INNER。您可能会发现使用窗口函数比使用子查询更好。 BETWEEN is a bad idea to use on dates. 使用 [] 引用列名,而不是 ''
  • @Charlieface 感谢您的回复! INNER JOIN 似乎就在那里,我会记住这一点。我无法理解你的意思 You might find it better to use window functions rather than that subquery. 使用 [] 然后 '' 有什么区别?
  • 您可以使用窗口计数来实现与子查询相同的功能:COUNT(*) OVER (PARTITION BY rb.ResourceId) 或类似的东西。使用'' 引用列名是不好的,在某些情况下可能会导致意外结果,例如dba.stackexchange.com/questions/194224/…

标签: sql sql-server subquery left-join sql-server-2019


【解决方案1】:

试试这个

DECLARE @ProjectId INT = 8501

SELECT p.Id AS 'Project-ID'
    ,p.Name AS 'Project-Name'
    ,p.Info AS 'Project-Info'
    ,pc.Name AS 'Project-Color'
    ,upr.DocumentName AS 'Project-Responsible'
    ,r.ResourceName AS 'Ressource-Name'
    ,r.Info AS 'Ressource-Info'
    ,CASE WHEN ugrr.ResourceRoleId IN (4,7)
        THEN 0
        ELSE 1
        END AS 'Ressource-Availability'
    ,r.Quantity AS 'Booking-Quantity'
    ,rb.Start AS 'Booking-Start'
    ,rb.[End] AS 'Booking-End'
    ,pat.Name AS 'Booking-Action'
    ,rb.IsOption AS 'Booking-State'
    ,rb.OptionRequestAccepted AS 'Booking-Response'
    ,q2.ProjectId AS 'Conflict-Project-ID'
    ,q2.Name AS 'Conflict-Project-Name'
    ,q2.DocumentName AS 'Conflict-Project-Responsible'
    ,q2.Start AS 'Conflict-Start'
    ,q2.[End] AS 'Conflict-End'
    ,q2.IsOption AS 'Conflict-State'
    

FROM Projects p
LEFT JOIN ProjectColors pc ON pc.Id = p.ProjectColorId
LEFT JOIN Users upr ON upr.Id = p.ResponsibleUserId
LEFT JOIN ResourceBookings rb ON rb.ProjectId = p.Id
LEFT JOIN Resources r ON r.Id = rb.ResourceId
LEFT JOIN ProjectActions pa ON pa.Id = rb.ProjectActionId
LEFT JOIN ProjectActionTypes pat ON pat.Id = pa.ProjectActionTypeId
LEFT JOIN UserGroupResourceRoles ugrr ON ugrr.ResourceId = r.Id
LEFT JOIN (SELECT p2.Id as id2
                ,p2.Name
                ,upr2.DocumentName
                ,rb2.Start
                ,rb2.[End]
                ,rb2.IsOption
                ,rb2.id
                ,rb2.ProjectId as rb_pid
                ,count(rb2.id) as id_count
            FROM ResourceBookings rb2
            LEFT JOIN Projects p2 ON p2.Id = rb2.ProjectId
            LEFT JOIN Users upr2 ON upr2.Id = p2.ResponsibleUserId
            LEFT JOIN Resources r ON r.Id = rb2.ResourceId
            WHERE rb2.ResourceId = r.Id
                AND (
                    rb2.Start BETWEEN DateAdd(s,+1,rb2.Start) AND DateAdd(s,-1,rb2.[End])
                    OR rb2.[End] BETWEEN DateAdd(s,+1,rb2.Start) AND DateAdd(s,-1,rb2.[End])
                    OR DateAdd(s,+1,rb2.Start) BETWEEN rb2.Start AND rb2.[End]
                )
                group by p2.Id
                ,p2.Name
                ,upr2.DocumentName
                ,rb2.Start
                ,rb2.[End]
                ,rb2.IsOption
                ,rb2.id
                ,rb2.ProjectId 
            ) q2
on q2.rb_pid = p.Id

WHERE p.Id = @ProjectId
    AND rb.Start >= SysDateTime()
    AND ugrr.UserGroupId = 11
    AND q2.id_count > 1 

【讨论】:

    【解决方案2】:

    很好地尝试了更长的时间,似乎我的子查询过于复杂了。

    没有它,再加上几个连接,它就像一个魅力。

    为了澄清为什么我“忽略”了我得到的一些提示:

    • INNER 不是正确的选择。它会严重影响结果(可能是因为表格设计得不是很好,并且有许多重叠的标题没有相关性......)
    • [] 而不是 '' 在阅读更多内容后可能会是更好的选择。将来会在我的查询中解决这个问题。
    • window functions 似乎功能强大且资源消耗较少,我会对其进行研究,但此时我还没有完全理解它们。

    我想我会在阅读广告了解更多有关命名提示后再次尝试优化它,但现在这对我有用:

    DECLARE @ProjectId INT = 6290
    
    SELECT
        p.Id AS 'Project-ID'
        ,p.Name AS 'Project-Name'
        ,p.Info AS 'Project-Info'
        ,pc.Name AS 'Project-Color'
        ,upr.DocumentName AS 'Project-Responsible'
        ,r.ResourceName AS 'Resource-Name'
        ,r.Info AS 'Resource-Info'
        ,CASE WHEN ugrr.ResourceRoleId IN (4,7)
            THEN 0
            ELSE 1
            END AS 'Resource-Availability'
        ,rb.Quantity AS 'Booking-Quantity'
        ,rb.Start AS 'Booking-Start'
        ,rb.[End] AS 'Booking-End'
        ,pat.Name AS 'Booking-Action'
        ,rb.IsOption AS 'Booking-State'
        ,rb.OptionRequestAccepted AS 'Booking-Response'
        ,rb2.ProjectId AS 'Conflict-Project-ID'
        ,p2.Name AS 'Conflict-Project-Name'
        ,pc2.Name AS 'Conflict-Project-Color'
        ,upr2.DocumentName AS 'Conflict-Project-Responsible'
        ,rb2.Start AS 'Conflict-Start'
        ,rb2.[End] AS 'Conflict-End'
        ,pat2.Name AS 'Conflict-Booking-Action'
        ,rb2.IsOption AS 'Conflict-Booking-State' 
        ,rb2.Quantity AS 'Conflict-Booking-Quantity'
    
    FROM Projects p
    LEFT JOIN ProjectColors pc ON pc.Id = p.ProjectColorId
    LEFT JOIN Users upr ON upr.Id = p.ResponsibleUserId
    LEFT JOIN ResourceBookings rb ON rb.ProjectId = p.Id
    LEFT JOIN Resources r ON r.Id = rb.ResourceId
    LEFT JOIN ProjectActions pa ON pa.Id = rb.ProjectActionId
    LEFT JOIN ProjectActionTypes pat ON pat.Id = pa.ProjectActionTypeId
    LEFT JOIN UserGroupResourceRoles ugrr ON ugrr.ResourceId = r.Id
    /* Doubles for Conflicts */
    LEFT JOIN ResourceBookings rb2 ON rb2.ResourceId = rb.ResourceId
    LEFT JOIN Projects p2 ON p2.Id = rb2.ProjectId
    LEFT JOIN ProjectColors pc2 ON pc2.Id = p2.ProjectColorId
    LEFT JOIN ProjectActions pa2 ON pa2.Id = rb2.ProjectActionId
    LEFT JOIN ProjectActionTypes pat2 ON pat2.Id = pa2.ProjectActionTypeId
    LEFT JOIN Users upr2 ON upr2.Id = p2.ResponsibleUserId
    
    WHERE
        p.Id = @ProjectId
        AND rb.Start >= SysDateTime()
        AND ugrr.UserGroupId = 11
        AND rb.Id <> rb2.Id
        AND (
                (
                    rb2.Start >= rb.Start
                    AND rb2.Start <= rb.[End]
                )
            OR
                (
                    rb2.[End] >= rb.Start
                    AND rb2.[End] <= rb.[End]
                )
            OR
                (
                    rb.Start >= rb2.Start
                    AND rb.Start <= rb2.[End]
                )
            )
    

    【讨论】:

      猜你喜欢
      • 2012-09-01
      • 1970-01-01
      • 2012-04-01
      • 2021-03-26
      • 2012-07-14
      • 2020-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多