Sql 将if语句中的这两个计数比较合并到一个查询中以消除游标

Sql 将if语句中的这两个计数比较合并到一个查询中以消除游标,sql,sql-server,performance,Sql,Sql Server,Performance,我知道我应该避免使用游标,但我正在努力处理这个查询。所以我想,在优化之前,我最好先做些事情。我的目标是更新所有孩子,如果没有孩子有电子邮件。这意味着,如果任何孩子有电子邮件,他们都不会被更新 DECLARE @familyId INT DECLARE @childId INT DECLARE @famEmail NVARCHAR DECLARE famEmailCursor CURSOR FAST_FORWARD READ_ONLY FOR SELECT ID, Email, Chi

我知道我应该避免使用游标,但我正在努力处理这个查询。所以我想,在优化之前,我最好先做些事情。我的目标是更新所有孩子,如果没有孩子有电子邮件。这意味着,如果任何孩子有电子邮件,他们都不会被更新

DECLARE @familyId INT
DECLARE @childId INT
DECLARE @famEmail NVARCHAR

DECLARE famEmailCursor CURSOR FAST_FORWARD READ_ONLY FOR 
    SELECT ID, Email, ChildId 
    FROM #family

OPEN famEmailCursor

FETCH NEXT FROM famEmailCursor INTO @familyId, @famEmail, @childId

WHILE @@FETCH_STATUS = 0
BEGIN
    -- Check if none of the children have an email address
    IF((SELECT COUNT(*) FROM #family f
        INNER JOIN Children a ON a.ID = f.ChildId
        WHERE f.ID = @familyId
        AND (RTRIM(LTRIM(a.Email)) = '' OR a.Email IS NULL))
    = (SELECT COUNT(*) FROM #family fa WHERE fa.ID = @familyId))
    BEGIN 
        -- Update email
        UPDATE Children
        SET Email = @famEmail
        WHERE ID = @childId
    END

    FETCH NEXT FROM famEmailCursor INTO @familyId, @famEmail, @childId
END
这当然非常慢,所以我尝试使用GROUPBY和组合对其进行优化,但每次在得到实际执行的查询之前,我都会陷入困境

我如何摆脱光标,并可能在一个查询中完成这项工作

每个请求提供一些示例数据

-- #family example data
ID, Email, ChildId
133587,example1@example.com,133588
133587,example1@example.com,133589
133598,example2@example.com,133599
133598,example2@example.com,133600
133604,example3@example.com,133605
133604,example3@example.com,133606
133608,example4@example.com,133609
133608,example4@example.com,133610
133623,example5@example.com,133624
133623,example5@example.com,133625
133623,example5@example.com,134811


-- Children example data
ID, Email
133588,example1@example.com
133589,
133599,
133600,example2@example.com
133605,
133606,
133609,example4@example.com
133610,example4@example.com
133624,
133625,
134811,

听起来你在找这样的东西:

UPDATE a
set a.email=b.email 
from Children a
join #family b on a.id=b.id
where b.email is null

根据需要调整逻辑如果这不是您想要的

好的,处理复杂查询时最有价值的事情之一是将任务分解为子查询

因此,首先,让我们得到一个简单的查询,简单地列出每个孩子以及他们是否有电子邮件地址:

select id as ChildID,
    case when (RTRIM(LTRIM(a.Email)) = '' OR a.Email IS NULL)
        then 1 else 0 end as HasEmail
。。。接下来,让我们将其与family表连接起来,并使用前面的查询作为子查询,获得子查询数和电子邮件地址数:

select f.ID as familyID, sum(HasEmail) as emails, count(hasEmail) as total
from
#family f
JOIN
(
    select id as ChildId,
        case when (RTRIM(LTRIM(a.Email)) = '' OR a.Email IS NULL)
            then 1 else 0 end as HasEmail
) as childEmailSubQ
ON f.ChildId = childEmailSubQ.ChildID
group by f.ID
到目前为止有意义吗?现在我们有一个查询,它有三个字段:

家庭ID 具有电子邮件地址的孩子数 家庭中的子女人数 现在我们也可以将其用作子查询:

select familyID
from
(
    -- that text from the previous query)
) childrenEmailCountsSubquery
where emails = 0
该子查询为我们提供了一个列表,其中列出了所有在其子女中没有单一电子邮件地址的家庭。就个人而言,这已经变得非常复杂,我只需要创建一个临时表来存储数据:

declare @familiesWithNoChildrenHavingEmail table (FamilyID int)
insert into @familiesWithNoChildrenHavingEmail
-- the previous SQL command
。。。然后,让update语句使用该表


有道理吗?您不再试图在一个大规模查询中解决它,而是将事情分解为具体步骤,甚至可以通过描述性子查询/表命名来记录您的过程

请与我们分享一些测试数据,以便我们进行测试。我不知道这是如何做到的。如果孩子们有电子邮件,我怎么能确保他们不会得到更新?我不完全理解你的逻辑-这就是为什么我说“根据你认为合适的方式进行调整”-我的观点是你可以更新连接,你不需要在此处设置光标,你可以做一些事情,比如在加入时更新一个set a.email=case,然后把你所有的逻辑放在case语句中。我需要一些时间来理解这一点。我马上就要睡觉了,我应该早点停止工作。。。这似乎解决了我的问题!我明天早上会回来找你。顺便说一句,祝贺1k代表;谢谢是的,子查询是SQL中最有用的东西之一。它可以在心里消化更复杂的查询,同时让您记录您正在做的事情:-中途我得到错误列“family.ID”在选择列表中无效,因为它不包含在聚合函数或GROUP BY子句中。但我可以解决这个问题。谢谢你的帮助。聚合子查询成功了。