Group By列抛出查询

时间:2013-02-07 15:14:11

标签: sql sql-server sql-server-2008 tsql

我有一个检查数据库的查询,以查看客户是否每天多次访问过。如果有,它会计算访问次数,然后告诉我他们访问的次数。问题是它将“Tickets.lcustomerid”抛入group by子句,导致我错过5条记录(没有条形码的客户)。如何更改以下查询以从group by子句中删除“tickets.lcustomerid”...如果我删除它我会收到一个错误告诉我“Tickets.lCustomerID”不是有效选择,因为它不是聚合或groupby子句。

有效的查询:

SELECT        Customers.sBarcode, CAST(FLOOR(CAST(Tickets.dtCreated AS FLOAT)) AS DATETIME) AS dtCreatedDate, COUNT(Customers.sBarcode) AS [Number of Scans], 
                         MAX(Customers.sLastName) AS LastName
FROM            Tickets INNER JOIN
                         Customers ON Tickets.lCustomerID = Customers.lCustomerID
WHERE        (Tickets.dtCreated BETWEEN @startdate AND @enddate) AND (Tickets.dblTotal <= 0)
GROUP BY Customers.sBarcode, CAST(FLOOR(CAST(Tickets.dtCreated AS FLOAT)) AS DATETIME)
HAVING        (COUNT(*) > 1)
ORDER BY dtCreatedDate

输出是:

sBarcode   dtcreated Date      Number of Scans  slastname    
1234     1/4/2013 12:00:00 AM         2          Jimbo         
         1/5/2013 12:00:00 AM         3          Jimbo2       
1578     1/6/2013 12:00:00 AM         3          Jimbo3        

我当前使用子查询查询

SELECT customers.sbarcode, 
       Max(customers.slastname)                                  AS LastName, 
       Cast(Floor(Cast(tickets.dtcreated AS FLOAT)) AS DATETIME) AS 
       dtCreatedDate, 
       Count(customers.sbarcode)                                 AS 
       [Number of Scans], 
       Stuff ((SELECT ', ' 
                      + RIGHT(CONVERT(VARCHAR, dtcreated, 100), 7) AS [text()] 
               FROM   tickets AS sub 
               WHERE  ( lcustomerid = tickets.lcustomerid ) 
                      AND ( dtcreated BETWEEN Cast(Floor(Cast(tickets.dtcreated 
                                                              AS 
                                                              FLOAT)) AS 
                                                   DATETIME 
                                              ) 
                                              AND 
Cast(Floor(Cast(tickets.dtcreated 
AS FLOAT 
)) AS 
DATETIME 
) 
+ '23:59:59' ) 
AND ( dbltotal <= '0' ) 
FOR xml path('')), 1, 1, '')                      AS [Times Scanned] 
FROM   tickets 
       INNER JOIN customers 
               ON tickets.lcustomerid = customers.lcustomerid 
WHERE  ( tickets.dtcreated BETWEEN @startdate AND @enddate ) 
       AND ( tickets.dbltotal <= 0 ) 
GROUP  BY customers.sbarcode, 
          Cast(Floor(Cast(tickets.dtcreated AS FLOAT)) AS DATETIME), 
          tickets.lcustomerid 
HAVING ( Count(*) > 1 ) 
ORDER  BY dtcreateddate 

当前输出(注意没有条形码的记录丢失)是:

sBarcode   dtcreated Date      Number of Scans  slastname    Times Scanned
1234     1/4/2013 12:00:00 AM         2          Jimbo         12:00PM, 1:00PM
1578     1/6/2013 12:00:00 AM         3          Jimbo3        03:05PM, 1:34PM

2 个答案:

答案 0 :(得分:1)

更新:基于我们的“聊天”,似乎customerid不是唯一的字段,但条形码是,即使客户ID是主键。

因此,为了不在子查询中使用GROUP BY客户ID,您需要加入第二个客户表,以便实际加入条形码。

试试这个:

SELECT customers.sbarcode, 
       Max(customers.slastname)                                  AS LastName, 
       Cast(Floor(Cast(tickets.dtcreated AS FLOAT)) AS DATETIME) AS 
       dtCreatedDate, 
       Count(customers.sbarcode)                                 AS 
       [Number of Scans], 
       Stuff ((SELECT ', ' 
                      + RIGHT(CONVERT(VARCHAR, dtcreated, 100), 7) AS [text()] 
               FROM   tickets AS subticket
               inner join
               customers as subcustomers
               on
               subcustomers.lcustomerid = subticket.lcustomerid
               WHERE  ( subcustomers.sbarcode = customers.sbarcode ) 
                      AND ( subticket.dtcreated BETWEEN Cast(Floor(Cast(tickets.dtcreated 
                                                              AS 
                                                              FLOAT)) AS 
                                                   DATETIME 
                                              ) 
                                              AND 
Cast(Floor(Cast(tickets.dtcreated 
AS FLOAT 
)) AS 
DATETIME 
) 
+ '23:59:59' ) 
AND ( dbltotal <= '0' ) 
FOR xml path('')), 1, 1, '')                      AS [Times Scanned] 
FROM   tickets 
       INNER JOIN customers 
               ON tickets.lcustomerid = customers.lcustomerid 
WHERE  ( tickets.dtcreated BETWEEN @startdate AND @enddate ) 
       AND ( tickets.dbltotal <= 0 ) 
GROUP  BY customers.sbarcode, 
          Cast(Floor(Cast(tickets.dtcreated AS FLOAT)) AS DATETIME)
HAVING ( Count(*) > 1 ) 
ORDER  BY dtcreateddate 

答案 1 :(得分:1)

我无法直接解决您的问题,因为我不了解您的数据模型或您尝试使用此查询完成的任务。但是,我可以就如何自己解决问题给你一些建议。

首先,您是否完全了解您要完成的工作以及表格如何组合在一起?如果是这样的话,请继续进行下一步,如果没有,请先了解这些知识,如果没有这种理解,就无法进行复杂的查询。

接下来分解您要完成的步骤,并确保在完成剩下的工作之前先让每个人完成任务。所以在你的情况下,你似乎错过了一些客户。从一个新的查询开始(我很确定这个问题有多个问题)。所以从join和where子句开始。

我怀疑你可能需要从客户开始并离开加入门票(这会将条件移动到左边的连接处,因为它们在门票上)。无论是否有票,这都将为您提供所有客户。如果那不是您想要的,那么使用jon和where clasues(并在尝试解决问题时使用select *),直到您返回所需的确切客户记录集。在此阶段使用select *的原因是为了查看数据中可能导致您遇到问题的原因。这可能会告诉你如何修复。

通常我从一个连接开始,然后一次一个地添加where clasues,直到我知道我正在获得正确的初始记录集。如果您有多个联接,请及时执行一次联接,以了解突然开始的记录何时记录的记录多于或少于预期。

然后进入更复杂的部分。一次添加一个并检查结果。如果你突然从10条记录变为5条或15条,那么你可能遇到了问题。当您一次只执行一个步骤并遇到问题时,您确切知道导致问题的原因使得查找和修复更加容易。

BY BY对于彻底理解很重要。您必须拥有组中的每个非聚合字段,否则它将无法工作。将此视为法律,如万有引力定律。这不是你可以改变的。但是,它可以通过使用派生表或CTE来解决。如果你不知道它们是什么,请仔细阅读它们,当你遇到复杂的东西并且你应该彻底理解它们时,它们是非常有用的技术。我怀疑你需要在这里使用派生表方法只对你需要的东西进行分组,然后将该派生表连接到其余的查询以获取ontehr字段。我将举一个简单的例子:

select 
      t1.table1id
    , t1.field1
    , t1.field2
    , a.field3
    , a.MostRecentDate
From table1 t1
JOIN
    (select t1.table1id, t2.field3, max (datefield) as MostRecentDate 
    from table1 t1
    JOin Table2 t2 on t1.table1id = t2.table1id
    Where t2.field4 = 'test'
    group by t1.table1id,t2.field3) a
    ON a.table1id = t1.table1id

希望这种方法可以帮助您解决这个问题。