TSQL:根据孩子的条件选择父母

Posted

技术标签:

【中文标题】TSQL:根据孩子的条件选择父母【英文标题】:TSQL: Selecting parent based on conditions on child 【发布时间】:2021-12-27 05:32:24 【问题描述】:

我有一个父表 Orders 和一个子表 Jobs,其中包含以下示例数据

我想根据以下要求选择订单

1>每个订单可能有 0 个或多个作业。如果订单没有任何工作,请不要选择它。 2>一个用户不能处理多个属于同一订单的工作。 例如,用户 1 无法处理属于订单 1 和 2 的作业,因为他已经处理过同一订单的作业 14。 3>只选择在Requested状态下工作的订单

我有以下查询,它给了我预期的结果

DECLARE @UserID INT = 2

SELECT O.OrderID
FROM Orders O
JOIN Jobs J ON J.OrderID = O.OrderID
WHERE 
J.JobStatus = 'Requested' AND
NOT EXISTS
(  
    --Must not have worked this Order
    SELECT 1 FROM Jobs J1
    WHERE J1.OrderID = O.OrderID AND J1.UserID = @UserID
)
Group By o.OrderID

SQL Fiddle Demo

查询连接Jobs 表两次。我正在尝试优化查询并寻找一种方法来通过使用Jobs 表在可能的情况下仅使用一次来实现预期结果。任何其他解决方案也值得赞赏。如果需要,我可以更改表架构。

jobs 表有将近 20M 行,并且有时查询显示性能不佳。 (是的,我们查看了索引)。我认为它的扫描作业表两次导致性能问题。

【问题讨论】:

根据问题指南,请不要发布代码、数据、错误消息等的图像 - 将文本复制或输入到问题中。请保留将图像用于图表或演示渲染错误,无法通过文本准确描述的事情。 在询问性能问题时,通常需要执行计划(使用粘贴计划)。 【参考方案1】:

如果目标只是“仅使用 Jobs 表一次”,我会尝试类似:

DECLARE @UserID INT = 2
    
SELECT 
    O.OrderID
FROM 
    Orders O
    INNER JOIN Jobs J ON J.OrderID = O.OrderID  
GROUP BY
    O.OrderID
HAVING
    SUM(CASE WHEN J.JobStatus = 'Requested' THEN 1 ELSE 0 END) > 0
    AND SUM(CASE WHEN J.UserID = @UserId THEN 1 ELSE 0 END) = 0

SQL Fiddle

为了进一步优化,我建议将JobStatus 列的varchar 数据类型替换为tinyint 之一(并创建一个JobStatuses 表)。一旦您的 Job 表有 20M 条记录,varchar(10) 就会为您提供 190 Mb,但是使用 tinyint 会将列大小减少到 19 Mb — 这可以减少 IO-Read 操作。

我会尝试将子过滤与与父母一起加入过滤分开。这种方法可能会为单个操作使用更少的内存并因此而提高性能:

DECLARE @UserID INT = 2
DECLARE @OrderIDs TABLE (OrderID INT NOT NULL PRIMARY KEY)

INSERT INTO @OrderIDs
SELECT
    OrderID
FROM
    Jobs
GROUP BY
    OrderID
HAVING
    SUM(CASE WHEN JobStatus = 'Requested' THEN 1 ELSE 0 END) > 0
    AND SUM(CASE WHEN UserID = @UserId THEN 1 ELSE 0 END) = 0

SELECT
    O.*
FROM
    Orders O
    INNER JOIN @OrderIDs ids on ids.OrderID = O.OrderID

【讨论】:

作业状态实际上是int类型的ID。只是为了理解目的,我将其保留为 nvarchar 使用这种方法看起来我什至不必加入 Orders 表。我可以直接使用 Jobs 表来获取 OrderID【参考方案2】:

您可以考虑将以下索引添加到Jobs 表中:

CREATE INDEX idx_jobs ON Jobs (OrderID, UserID, JobStatus);

如果使用此索引,则应加快上述查询中不存在的子查询。此外,它还可用于外部***查询中从 OrdersJobs 的连接(尽管 SQL Server 可能必须进行索引扫描)。

【讨论】:

以上是关于TSQL:根据孩子的条件选择父母的主要内容,如果未能解决你的问题,请参考以下文章

根据条件分组/不同列[关闭]

如何根据lxml中的孩子选择父母?

如父如子

Laravel Eloquent 只有在最近的孩子 created_at < 5 年后才获得父母

从父母向孩子发送信号,反之亦然

休眠 HQL 到条件