SQL Server组记录和计算具有相似名称的总计

时间:2017-03-22 03:27:31

标签: sql sql-server

我正在尝试计算每个用户的打开日记记录总数。 我的问题是用户名有三个不同的前缀。 CC YY或没有。 我可以替换使名称相同的前缀,但我无法通过此修改后的名称对它们进行分组。数据分布在三个表中,我需要排除关闭和非活动记录。

此简化查询返回我想要的未分组结果的示例。

select sname 
 ,( Select count(lDiaryHeaderID)
    FROM DiaryHeader
    inner join OwnersCorporation on lObjectID = lOwnersCorporationID
    where 
    sObjectType = 'B' 
    AND DiaryHeader.bRecordClosed = 'N'
    AND DiaryHeader.dActionDueDate < cast (GETDATE() as DATE)
    AND OwnersCorporation.bManaged  = 'Y'
    AND tbluser.lUserID = strata.dbo.OwnersCorporation.lUserID 
  ) as TotalOverDue

FROM tblUser 
WHERE bActive = 'Y'
AND bManager = 'Y'
ORDER by sName

结果

sname               TotalOverDue
Belinda Smith       525
CC Belinda Smith    18
CC Julie Brown      13
CC Kris White       0
CC Sharon Towell    38
Colleen Black       131
Jessica Jones       166
Joanne Beigh        284
Julie Brown         449
YS Belinda Smith    31
YS Colleen Black    0
YS Joanne Beigh     21

我想要做的是从名称中删除CC和YS前缀 并将它们分组为一个条目,并将总数加在一起。

期望的结果

sname               TotalOverDue
Belinda Smith       574
Julie Brown         462
Kris White          0
Sharon Towell       38
Colleen Black       131
Jessica Jones       166
Joanne Beigh        305

我想出了这个。

select Replace(Replace(sName, 'CC ',''), 'YS ','')    
 ,( Select count(lDiaryHeaderID)    
    FROM DiaryHeader    
    inner join OwnersCorporation on lObjectID = lOwnersCorporationID    
    where     
    sObjectType = 'B'    
    AND DiaryHeader.bRecordClosed = 'N'    
    AND DiaryHeader.dActionDueDate < cast (GETDATE() as DATE)    
    AND OwnersCorporation.bManaged  = 'Y'    
    AND tbluser.lUserID = strata.dbo.OwnersCorporation.lUserID     
  ) as TotalOverDue    

FROM tblUser     
WHERE bActive = 'Y'    
AND bManager = 'Y'    
group by sName

但是我收到此错误消息

列'tblUser.lUserID'在选择列表中无效,因为它不包含在聚合函数或GROUP BY子句中。

如果我将lUserID放在group by子句中,它只会按预期显示重复的用户名。

我出错的任何想法以及我应该如何做到这一点。 谢谢 大卫

更新

我的实际解决方案有多个列,我删除了其中两个以简化我的问题。现在我正在尝试再次扩展它,我遇到了更多问题。

这是我尝试将其他两列添加回来.DueToday和TotalDue然而我的总数正在滚雪球。

更新2 感谢Phylyp的鹰眼。他发现了一些剪切和粘贴错误,此代码现在返回所需的结果。 感谢所有回答了一些非常有趣和多样化的解决方案的人。我会坚持使用第一个有效的方法:)

    select Replace(Replace(sName, 'CC ', ''), 'YS ', ''),
    coalesce(sum(od.cnt), 0) as TotalOverDue,
    coalesce(sum(dt.cnt), 0) as DueToday,
    coalesce(sum(td.cnt), 0) as TotalDue

from tblUser u

left join (
    select oc.lUserID,
    count(lDiaryHeaderID) cnt
    from DiaryHeader dh
    inner join OwnersCorporation oc on lObjectID = lOwnersCorporationID
    where sObjectType = 'B'
        and dh.bRecordClosed = 'N'
        and dh.dActionDueDate < cast(GETDATE() as date)
        and oc.bManaged = 'Y'
    group by oc.lUserID
    ) od on u.lUserID = od.lUserID

left join (
    select oc.lUserID,
    count(lDiaryHeaderID) cnt
    from DiaryHeader dh
    inner join OwnersCorporation oc on lObjectID = lOwnersCorporationID
    where sObjectType = 'B'
        and dh.bRecordClosed = 'N'
        and dh.dActionDueDate = cast(GETDATE() as date)
        and oc.bManaged = 'Y'
    group by oc.lUserID
    ) dt on u.lUserID = dt.lUserID

left join (
    select oc.lUserID,
    count(lDiaryHeaderID) cnt
    from DiaryHeader dh
    inner join OwnersCorporation oc on lObjectID = lOwnersCorporationID
    where sObjectType = 'B'
        and dh.bRecordClosed = 'N'
        --and dh.dActionDueDate < cast(GETDATE() as date)
        and oc.bManaged = 'Y'
    group by oc.lUserID
    ) td on u.lUserID = td.lUserID

where bActive = 'Y'
and bManager = 'Y'
group by Replace(Replace(sName, 'CC ', ''), 'YS ', '')

4 个答案:

答案 0 :(得分:2)

首先,您需要按替换表达式而不是列sName进行分组。另外,请小心替换,因为它替换了字符串中任何位置的匹配。

其次,用左连接替换相关子查询。

select Replace(Replace(sName, 'CC ', ''), 'YS ', ''),
    coalesce(sum(od.cnt), 0) as TotalOverDue
from tblUser u
left join (
    select oc.lUserID,
        count(ldhID) cnt
    from DiaryHeader dh
    inner join OwnersCorporation oc on lObjectID = locID
    where sObjectType = 'B'
        and dh.bRecordClosed = 'N'
        and dh.dActionDueDate < cast(GETDATE() as date)
        and oc.bManaged = 'Y'
    group by oc.lUserID
    ) od on u.lUserID = od.lUserID
where bActive = 'Y'
    and bManager = 'Y'
group by Replace(Replace(sName, 'CC ', ''), 'YS ', '')

答案 1 :(得分:1)

以下是我的建议。

我正在使用CASE表达式来检查名称是否以 CC或YS开头,如果是,请删除前3个字母。这确保了名称本身中存在的任何CC / YS(例如作为首字母)不会被无意中移除,如REPLACE()函数的情况。

SELECT 
    tblUserNameNormalized.sNameNormalized 
  , COALESCE(SUM(od.cnt), 0) AS TotalOverDue 
  , COALESCE(SUM(dt.cnt), 0) AS DueToday 
  , COALESCE(SUM(td.cnt), 0) AS TotalDue 
FROM 
  ( 
      SELECT 
          lUserID
        , CASE 
          -- Check if the name starts with CC or YS, and skip the first 3 letters in that case 
          WHEN sName LIKE ('CC%') THEN SUBSTRING(sName, 4, LEN(sName) - 3) 
          WHEN sName LIKE ('YS%') THEN SUBSTRING(sName, 4, LEN(sName) - 3) 
          -- Else use the name as-is 
          ELSE sName 
          END AS sNameNormalized 
      FROM 
        tblUser 
      WHERE bActive = 'Y'
        AND bManager = 'Y'
  ) as tblUserNameNormalized 
  LEFT JOIN ( 
    select oc.lUserID,
    count(lDiaryHeaderID) cnt
    from DiaryHeader dh
    inner join OwnersCorporation oc on lObjectID = lOwnersCorporationID
    where sObjectType = 'B'
        and dh.bRecordClosed = 'N'
        and dh.dActionDueDate < cast(GETDATE() as date)
        and oc.bManaged = 'Y'
    group by oc.lUserID
  ) od on tblUserNameNormalized.lUserID = od.lUserID
  LEFT JOIN (
    select oc.lUserID,
    count(lDiaryHeaderID) cnt
    from DiaryHeader dh
    inner join OwnersCorporation oc on lObjectID = lOwnersCorporationID
    where sObjectType = 'B'
        and dh.bRecordClosed = 'N'
        and dh.dActionDueDate = cast(GETDATE() as date)
        and oc.bManaged = 'Y'
    group by oc.lUserID
    ) dt on tblUserNameNormalized.lUserID = dt.lUserID
  LEFT JOIN (
    select oc.lUserID,
    count(lDiaryHeaderID) cnt
    from DiaryHeader dh
    inner join OwnersCorporation oc on lObjectID = lOwnersCorporationID
    where sObjectType = 'B'
        and dh.bRecordClosed = 'N'
        --and dh.dActionDueDate < cast(GETDATE() as date)
        and oc.bManaged = 'Y'
    group by oc.lUserID
    ) td on tblUserNameNormalized.lUserID = td.lUserID 
GROUP BY tblUserNameNormalized.sNameNormalized 

答案 2 :(得分:1)

是的,它会给出错误,因为在您的代码中,您正在使用带有聚合函数的GROUP BY子句。

请尝试下面的脚本。

选择a.sName,Sum(TotalOverDue)作为TotalOverDue FROM(         选择替换(替换(sName,&#39; CC&#39;,&#39;&#39;),&#39; YS&#39;,&#39;&#39;)作为sName          ,(选择计数(lDiaryHeaderID)
            来自DiaryHeader
            内部联接OwnersCorporation on lObjectID = lOwnersCorporationID
            其中
            sObjectType =&#39; B&#39;
            和DiaryHeader.bRecordClosed =&#39; N&#39;
            和DiaryHeader.dActionDueDate&lt;施放(GETDATE()作为DATE)
            AND OwnersCorporation.bManaged =&#39; Y&#39;
            AND tbluser.lUserID = strata.dbo.OwnersCorporation.lUserID
          )作为TotalOverDue

    FROM tblUser     
    WHERE bActive = 'Y'    
    AND bManager = 'Y'  ) a

按名称分组

答案 3 :(得分:0)

您可以像这样使用替换,然后将您想要的内容分组:

declare @CC varchar(20)
declare @YS varchar(20)
Set @CC = 'CC sample name'
Set @YS = 'CC sample name'
select replace(@CC,'CC ',''),replace(@YS,'YS ','');